📜 ⬆️ ⬇️

Memory management in Objective C, working with KeyChain, a GUI utility for mounting SSHFS

The purpose of my article is to give the reader an initial idea of ​​how to work with memory in Objective-C, to talk about working with KeyChain, and to show a new version of your SSHFS mount application, which was written in just a few days (in total), but can already compete with cumbersome Macfusion.app, and which works without a file and does not write your passwords in clear text to the system log .

Memory management


In general, of course, starting with Mac OS X 10.5, memory management has become much easier as support for garbage collection (GC) has been added. Accordingly, you can simply turn it on by default in your project and forget about it, like a bad dream.

image

However, if you carefully read the description, the following will be written there: "All Objective-C code connected or loaded by this application must also be GC capable". That is, if you use third-party closed-source frameworks that were compiled without GC support, you will have to either give up on them or use older memory management mechanisms, in the form of retain / release. Also, GC support is not available in the iPhone , so if you decide to write your application under the iPhone OS, then in any case you will have to deal with it.
')
I will assume that you still have some basic knowledge about Objective C, for example, you have already read this topic .

In Objective C, the standard reference counting mechanism is used: every object created that is the heir of NSObject / NSProxy has an internal reference counter, which after creation is set equal to one. To increase the object reference count, you must call retain , to decrease the reference count, release . After the link counter reaches 0, the object is released.

NSString *someString = [[NSString alloc ] initWithFormat: @"usage: %s first-arg second-arg\n" , argv[0]]; // 1

// ... -

[someString release]; //

There is a very convenient mechanism, which for the most part eliminates the need for manual reference counting and is called NSAutoreleasePool - according to the agreement, if the method returns a link to an object and does not contain the keywords alloc , copy , then the reference count is still set to 1 , but after processing the event, or after manually emptying the pool, its reference count automatically decreases by one (you can also send a manually selected object to the autorelease pool by sending it an autorelease message instead of release).

NSString *someString = [NSString stringWithFormat: @"usage: %s first-arg second-arg\n" , argv[0]]; // 1, , retain ( release retain)

// ... -

// !


If your code is executed in a separate thread, then you need to create an NSAutoreleasePool in this thread manually, otherwise all memory from autorelease objects will leak (which you can easily guess because of the huge number of entries in the console).

XCode has excellent utilities for debugging and analyzing memory, for example, you can select Build → Build and Analyze , and then your code will be analyzed with a static Clang analyzer, which can point you to typical errors in memory management (and not only). Also, it is possible to launch the application to detect leaks. To do this, run the Run → Run with Performance Tool → Leaks in your project. It tracks memory leaks right in the process of the application , and it can find leaks not only in Objective C code, but also in ordinary C (with malloc / free), and even inside closed frameworks (including from Apple)!

Work with Keychain


The functions for working with Keychain are quite low-level (unlike most frameworks that work with the user interface) and use the C-language API. Apple’s documentation has a very comprehensive guide to all the challenges supported by the Keychain Services subsystem , but I would like show how easy it is to do basic things.

When dealing with C calls, Apple mainly uses CoreFoundation. CoreFoundation uses and supports almost the same data types used in Objective-C with the Cocoa framework, and even supports the transparent type casting of CoreFoundation <-> Cocoa. All CoreFoundation calls have a CF prefix (cf. NS), and type names are obtained by replacing NS with CF and asterisks [*] with Ref (reference, reference) at the end (for example, NSString * <-> CFStringRef, NSArray * <-> CFArrayRef). CFRelease ( CFTypeRef ) / CFRetain ( CFTypeRef ) are used for working with memory, the purpose and use of which you can guess yourself.

So, to get started with Keychain, you need to add a couple of headers and connect the Security.framework framework:

#import <CoreFoundation/CoreFoundation.h>
#import <Security/Security.h>

To add the Internet password for your application to the default keychain, use the SecKeychainAddInternetPassword call. He has a lot of arguments, most of which are optional. An example of usage is below:

const char *serverName = "habrahabr.ru" ;
int serverNameLength = strlen(serverName);

const char *accountName = "youROCK" ;
int accountNameLength = strlen(accountName);

char *path = "/" ;
int pathLength = strlen(path);

int port = 22;

const char *passwordData = "myExtremelySecretPassword" ;
int passwordLength = strlen(passwordData);

/* , ( SSH :)), . , */

SecKeychainAddInternetPassword(NULL, serverNameLength, serverName, 0, NULL, accountNameLength, accountName, pathLength, path, port, kSecProtocolTypeSSH, kSecAuthenticationTypeDefault, passwordLength, passwordData, NULL);


* This source code was highlighted with Source Code Highlighter .


To obtain the Internet password from Keychain, we use the SecKeychainFindInternetPassword call, an example of which can be seen below:

const char *serverName = "habrahabr.ru" ;
int serverNameLength = strlen(serverName);

const char *accountName = "youROCK" ;
int accountNameLength = strlen(accountName);

char *path = "/" ;
int pathLength = strlen(path);

int port = 22;

UInt32 passwordLength;
void *passwordData;

OSStatus retVal;

if ( (retVal = SecKeychainFindInternetPassword(NULL, serverNameLength, serverName, 0, NULL, accountNameLength, accountName, pathLength, path, port, kSecProtocolTypeSSH, kSecAuthenticationTypeDefault, &passwordLength, &passwordData, NULL)) == 0)
{
// , passwordData (void *) ,
// ,
NSString *passValue = [[NSString alloc] initWithBytes:passwordData length:passwordLength encoding:NSUTF8StringEncoding];

// ,

SecKeychainItemFreeContent(NULL, passwordData);
[passValue release];
} else
{
// 2 — -, copy,
// — (NSString*), NSString CFStringRef

CFStringRef reason = SecCopyErrorMessageString(retVal, NULL);
NSLog( @"Could not fetch info from KeyChain, recieved code %d with following explanation: %@" , retVal, (NSString*) reason);
CFRelease(reason);
}


* This source code was highlighted with Source Code Highlighter .


Notice that we never imagined anywhere, on which OS this code will be run, so it will work on Mac OS X and iPhoneOS (and on iPhoneOS, user access is not required to access Keychain from the application).

GUI Utility for mounting volumes over SSH using SSHFS


I have repeatedly written on Habré about my utility that allows you to connect remote filesystems using SSHFS and GUI, but I want to inform you that it has finally reached the state where it can be used:


It allows you to mount a remote file system using MacFUSE and / or utilities from pqrs.org . There is a list of recent servers, secure password transfer to the ssh utility (a matter of my special pride :)) , optional storage of passwords in Keychain, support for compression. Compared to MacFusion.app, it certainly has fewer features, but it is easier to use, the codebase is 11 (!) Times smaller (28Kb vs. 318Kb), does not throw a bunch of requests from Keychain at the start, does not require launching a separate demon, etc. And compared with ExpanDrive free and opensource.
PS This is my eighth habratopik, please do not kick too much.

Source: https://habr.com/ru/post/83688/


All Articles