Skip to content

Posts tagged ‘os x’

21
Oct

How to Launch a Privileged Process on OS X

For security reasons, Apple recommends that GUI applications should never run with the privileges of the root user. GUI applications normally load several types of plugins and input managers automatically. If a malicious plugin was installed then it could cause security problems when the privileged application was launched.

In addition, system services that run with the privileges of the root user (such as launch daemons) need to avoid using certain technology frameworks provided by Apple. These frameworks are not safe to use in a service that runs with the privileges of the root user.

If you need your GUI application to do something that requires root privileges, Apple recommends you split your application into two parts. First, create a GUI that runs as a normal user. Then when you need to do something with root privileges, you launch a separate helper process or tool. Splitting your application avoids security holes while keeping things very easy for your users.

Two Steps

Launching a privileged process is done in two steps:

1) Request authorization. The operating system will ask the user for permission to run a privileged process. The user will need to enter an administrator’s username and password.

#import <Security/Security.h>

OSStatus PreauthorizePrivilegedProcess(AuthorizationRef *authRef) {
    AuthorizationItem item = { kAuthorizationRightExecute, 0, NULL, 0 };
    AuthorizationRights rights = { 1, &item };
    AuthorizationFlags flags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize;
    return AuthorizationCreate(&rights, kAuthorizationEmptyEnvironment, flags, authRef);
}

2) Launch the process. If the user authenticates correctly, you can use the authorization reference created above to launch the helper process.

OSStatus LaunchPreauthorizedProcess(AuthorizationRef *authRef, NSString *path) {
    OSStatus status = AuthorizationExecuteWithPrivileges(*authRef, [path UTF8String], kAuthorizationFlagDefaults, NULL, NULL);
    AuthorizationFree(*authRef, kAuthorizationFlagDestroyRights);
    return status;
}

Please note that the authorization reference created in the first function is released in the second function. If you want to launch several privileged processes in a short amount of time, you can comment out this line and and release the reference on your own afterwards.

Some of Apple’s sample code (and other examples) has an extra step where they copy the authorization reference. This is only necessary if you have a previously created authorization reference that you want to add elevated privileges to.

Just One Step

If you don’t need to update your GUI between these two steps, then both actions can be combined into one step.

#import <Security/Security.h>

OSStatus LaunchPrivilegedProcess(NSString *path) {
    AuthorizationRef authRef;
    OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authRef);
    if (status == errAuthorizationSuccess) {
        status = AuthorizationExecuteWithPrivileges(authRef, [path UTF8String], kAuthorizationFlagDefaults, NULL, NULL);
        AuthorizationFree(authRef, kAuthorizationFlagDestroyRights);
    }
    return status;
}

I consider this code to be in the public domain. Please feel free to copy and paste. And let me know if you find any problems or have suggestions.

4
Aug

How To Type Curly Quotes In Mac OS X

Mac OS X has an easy way to type “curly” quotes and apostrophes instead of "straight" versions. I used both versions in that sentence to show the difference. Here is a bigger version to make the distinction more visible:

Many people think “curly” quotes look better than "straight" ones.

You can use the following keyboard shortcuts to type a single or double curly quote:

  1. Single quote open (‘) — option ]
  2. Single quote close (’) — shift option ]
  3. Double quote open (“) — option [
  4. Double quote close (”) — shift option [

However, I think it makes more sense to use [ and ] for open and close versions instead of the shift key. I found myself constant typing “mismatched‘ quotes. I also wanted to use the shift key for double quotes since that’s how the normal keyboard button works.

  1. Single quote open (‘) — option [
  2. Single quote close (’) — option ]
  3. Double quote open (“) — option shift [
  4. Double quote close (”) — option shift ]

Since OS X supports custom key bindings, I looked for a way to fix this. The trick is to create a file called DefaultKeyBinding.dict in the KeyBindings folder inside your Library folder. You can use this file to override the default key bindings for most applications.

Here are my changes. Please feel free to copy the settings below and save them to your own computer. You may need to create the KeyBindings folder if it isn’t already there.

/*
 Updates Apple's default keybindings for curly quotes.
 See http://www.danandcheryl.com/1072

 Save this file here:
 /Users/<name>/Library/KeyBindings/DefaultKeyBinding.dict
*/
{
    "~[" = ("insertText:", "‘");
    "~]" = ("insertText:", "’");
    "~{" = ("insertText:", "“");
    "~}" = ("insertText:", "”");
}
28
Jun

How to Check the System Idle Time Using Cocoa

There is sample code on the Internet for programmatically checking the system idle time using IOKit and Cocoa (see here, for example). However, most of the examples seem overly long (see Paul Graham’s Succinctness is Power). The code below works in Tiger/10.4 and later and is about as concise as I can make it while still handling errors properly.

#include <IOKit/IOKitLib.h>

/**
 Returns the number of seconds the machine has been idle or -1 if an error occurs.
 The code is compatible with Tiger/10.4 and later (but not iOS).
 */
int64_t SystemIdleTime(void) {
    int64_t idlesecs = -1;
    io_iterator_t iter = 0;
    if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOHIDSystem"), &iter) == KERN_SUCCESS) {
        io_registry_entry_t entry = IOIteratorNext(iter);
        if (entry) {
            CFMutableDictionaryRef dict = NULL;
            if (IORegistryEntryCreateCFProperties(entry, &dict, kCFAllocatorDefault, 0) == KERN_SUCCESS) {
                CFNumberRef obj = CFDictionaryGetValue(dict, CFSTR("HIDIdleTime"));
                if (obj) {
                    int64_t nanoseconds = 0;
                    if (CFNumberGetValue(obj, kCFNumberSInt64Type, &nanoseconds)) {
                        idlesecs = (nanoseconds >> 30); // Divide by 10^9 to convert from nanoseconds to seconds.
                    }
                }
                CFRelease(dict);
            }
            IOObjectRelease(entry);
        }
        IOObjectRelease(iter);
    }
    return idlesecs;
}    

I consider this code to be in the public domain. Please feel free to copy and paste. And let me know if you find any problems or have suggestions.

18
May

How to Print a PDF File Using Cocoa

Mac OS X is well known for its great support for PDF files. You can create a PDF file from anything you can print. I thought that using Apple’s PDFKit framework would make it easy to program a way to print an existing PDF file. That turned out not to be the case.

Sending a file to a printer using the lp command is easy. However, this approach does not work for PDF files formatted for landscape printing. You can specify landscape orientation, but I wanted a way to detect the orientation automatically.

PDFKit has a PDFView object that has a printWithInfo:autoRotate: method. However, adding a PDFDocument to a PDFView and telling it to print doesn’t work. I eventually stumbled onto the fact that PDFDocumenthas a secret method that makes printing easy. So here is the code:

#import <Quartz/Quartz.h>

- (void)printPDF:(NSURL *)fileURL {

    // Create the print settings.
    NSPrintInfo *printInfo = [NSPrintInfo sharedPrintInfo];
    [printInfo setTopMargin:0.0];
    [printInfo setBottomMargin:0.0];
    [printInfo setLeftMargin:0.0];
    [printInfo setRightMargin:0.0];
    [printInfo setHorizontalPagination:NSFitPagination];
    [printInfo setVerticalPagination:NSFitPagination];

    // Create the document reference.
    PDFDocument *pdfDocument = [[[PDFDocument alloc] initWithURL:fileURL] autorelease];

    // Invoke private method.
    // NOTE: Use NSInvocation because one argument is a BOOL type. Alternately, you could declare the method in a category and just call it.
    BOOL autoRotate = YES;
    NSMethodSignature *signature = [PDFDocument instanceMethodSignatureForSelector:@selector(getPrintOperationForPrintInfo:autoRotate:)];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    [invocation setSelector:@selector(getPrintOperationForPrintInfo:autoRotate:)];
    [invocation setArgument:&printInfo atIndex:2];
    [invocation setArgument:&autoRotate atIndex:3];
    [invocation invokeWithTarget:pdfDocument];

    // Grab the returned print operation.
    NSPrintOperation *op = nil;
    [invocation getReturnValue:&op];

    // Run the print operation without showing any dialogs.
    [op setShowsPrintPanel:NO];
    [op setShowsProgressPanel:NO];
    [op runOperation];
}

I consider this code to be in the public domain. Please feel free to copy and paste. And let me know if you find any problems or have suggestions.

27
Aug

How to Create an Alias Programmatically

First, a disclaimer. Apple will warn you not to do this. The only supported way of creating an alias is to use the Finder. If you must do it programmatically, you will be told to use AppleScript. But if AppleScript won’t work for you, and a simple Cocoa method is what you want, read on.

Mozy’s Mac client doesn’t create aliases, but our customers do. We want to make sure our software backs them up correctly. So we added some unit tests to our build process that create aliases and check to see that Mozy handles them correctly.

We first used AppleScript, but ran quickly into two issues:

  1. Our build server runs as the root user, which doesn’t have a UI context. AppleScript doesn’t work without a UI context.

  2. Even running as a normal user, AppleScript cannot access the system temporary files location (/tmp) which is where we wanted to create our aliases.

That’s when the fun began.

I spent quite a bit of time failing to find the right bit of magic to create an alias that functioned properly in Finder. It turns out that an alias is a data structure inside another data structure stored in the resource fork of an empty file. Those structures need to have the correct record types for everything to work.

Having gone to the trouble of figuring this out, I thought I’d share. This code creates an alias for a folder, but it should serve as a good template if you need to create another type.

- (void)makeAliasToFolder:(NSString *)destFolder inFolder:(NSString *)parentFolder withName:(NSString *)name
{
    // Create a resource file for the alias.
    FSRef parentRef;
    CFURLGetFSRef((CFURLRef)[NSURL fileURLWithPath:parentFolder], &parentRef);
    HFSUniStr255 aliasName;
    FSGetHFSUniStrFromString((CFStringRef)name, &aliasName);
    FSRef aliasRef;
    FSCreateResFile(&parentRef, aliasName.length, aliasName.unicode, 0, NULL, &aliasRef, NULL);

    // Construct alias data to write to resource fork.
    FSRef targetRef;
    CFURLGetFSRef((CFURLRef)[NSURL fileURLWithPath:destFolder], &targetRef);
    AliasHandle aliasHandle = NULL;
    FSNewAlias(NULL, &targetRef, &aliasHandle);

    // Add the alias data to the resource fork and close it.
    ResFileRefNum fileReference = FSOpenResFile(&aliasRef, fsRdWrPerm);
    UseResFile(fileReference);
    AddResource((Handle)aliasHandle, 'alis', 0, NULL);
    CloseResFile(fileReference);

    // Update finder info.
    FSCatalogInfo catalogInfo;
    FSGetCatalogInfo(&aliasRef, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL);
    FileInfo *theFileInfo = (FileInfo*)(&catalogInfo.finderInfo);
    theFileInfo->finderFlags |= kIsAlias; // Set the alias bit.
    theFileInfo->finderFlags &= ~kHasBeenInited; // Clear the inited bit to tell Finder to recheck the file.
    theFileInfo->fileType = kContainerFolderAliasType;
    FSSetCatalogInfo(&aliasRef, kFSCatInfoFinderInfo, &catalogInfo);
}

I consider this code to be in the public domain. Please feel free to copy and paste. And let me know if you find any problems or have suggestions.

If you need a complete solution, Nathan Day wrote a nice set of classes called NDAlias. We didn’t want to import 9 classes for just a handful of unit tests.

I later found some of Apple’s sample code from 1999 demonstrating a similar approach. I think our Objective-C example is much easier to use.

25
May

Mac vs PC

My aunt is in the market for a new computer and asked me for a recommendation. She said she was really tempted by the new Macs and wanted to know why I liked them. Since I wrote out my reasons for her, I thought I’d share them here too.

Stability

The thing I like most is that I no longer think about the operating system. Crashes, viruses, security software, instability caused by service packs — all have pretty much gone away. I just don’t worry about that stuff anymore.

I used to look forward to reinstalling Windows on my computer about every six months. I loved the feeling of having a fast computer again. Now I can’t imagine what I was thinking. With Mac OS X, I hardly even think about the OS anymore.

Included Software

The second thing is that Macs come with lots of included software. There is software for managing pictures, editing home movies, making DVDs, recording and mixing songs, publishing web pages, chatting, email, playing music, calendaring, local backup and more.

Some of that comes with Windows, though I don’t find it as easy to use. All of it is available with Linux, but getting it all set up is not for the faint of heart. With a Mac, it’s all there ready for immediate use.

Plug and Play

Another thing I like is that, if you limit yourself to their supported hardware, everything works without any setup or drivers required. When I plug in my camera, iPhoto launches and takes me to the import screen. When I plug in a new printer, it just shows up and can be used immediately. That sort of thing.

The list of supported hardware is pretty long. I just check the list of cameras, video cameras and printers before buying stuff. Given how nice things are when using the supported hardware, that’s one concession I’m okay with.

Windows

One of the best thing for Mac newcomers is that if you decide you don’t like OS X, you can just install Windows, which runs perfectly. I reboot into Windows to play computer games a few times a month but am happy to go back to OS X when I’m done.

Momentum

Over the last two years my company has become predominantly Mac. Most people still use Windows too, either virtually or by rebooting when necessary. Even a couple of our Windows programmers use Macs.

Of course, Apple has their own reasons you should buy a Mac. And I highly recommend watching Apple’s ads, which are hilarious. My favorites are Elimination, Bean Counter and Out of the Box.

I’ve been using Macs for five and a half years now and have never found myself looking back.