OSStatus DoPrivilegedExec(const char *pathToTool, char *arg1, char *arg2, char *arg3, char *arg4, char *arg5, char *arg6) { short i; char *args[8]; OSStatus err; FILE *ioPipe = NULL; char *p, junk[256]; err = GetAuthorization(); if (err != noErr) { if (err == errAuthorizationCanceled) return err; ShowSecurityError("GetAuthorization returned error %d", err); } else { for (i=0; i<5; i++) { // Retry 5 times if error args[0] = arg1; args[1] = arg2; args[2] = arg3; args[3] = arg4; args[4] = arg5; args[5] = arg6; args[6] = NULL; err = AuthorizationExecuteWithPrivileges (gOurAuthRef, pathToTool, 0, args, &ioPipe); if (ioPipe) { // We use the pipe to signal us when the command has completed do { p = fgets(junk, sizeof(junk), ioPipe); } while (p); fclose (ioPipe); } // AuthorizationExecuteWithPrivileges() does a fork() and so // leaves a zombie process. Clear these so we don't exceed // the system-imposed limit of processes per user (MAXUPRC). while (waitpid(-1, 0, WNOHANG) > 0); #if 0 if (strcmp(arg2, "-R") == 0) SleepTicks(DELAY_TICKS_R); else SleepTicks(DELAY_TICKS); #endif if (err == noErr) break; } } if (err != noErr) ShowSecurityError("\"%s %s %s %s %s %s\" returned error %d", pathToTool, arg1 ? arg1 : "", arg2 ? arg2 : "", arg3 ? arg3 : "", arg4 ? arg4 : "", arg5 ? arg5 : "", err); return err; }
static OSStatus GetAuthorization (void) { static Boolean sIsAuthorized = false; AuthorizationRights ourAuthRights; AuthorizationFlags ourAuthFlags; AuthorizationItem ourAuthRightsItem[RIGHTS_COUNT]; AuthorizationEnvironment ourAuthEnvironment; AuthorizationItem ourAuthEnvItem[1]; char prompt[] = "BOINC needs to have certain permissions set up.\n\n"; OSStatus err = noErr; if (sIsAuthorized) return noErr; ourAuthRights.count = 0; ourAuthRights.items = NULL; err = AuthorizationCreate (&ourAuthRights, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &gOurAuthRef); if (err != noErr) { ShowSecurityError("AuthorizationCreate returned error %d", err); return err; } ourAuthRightsItem[0].name = kAuthorizationRightExecute; ourAuthRightsItem[0].value = dsclPath; ourAuthRightsItem[0].valueLength = strlen (dsclPath); ourAuthRightsItem[0].flags = 0; ourAuthRightsItem[1].name = kAuthorizationRightExecute; ourAuthRightsItem[1].value = chmodPath; ourAuthRightsItem[1].valueLength = strlen (chmodPath); ourAuthRightsItem[1].flags = 0; ourAuthRightsItem[2].name = kAuthorizationRightExecute; ourAuthRightsItem[2].value = chownPath; ourAuthRightsItem[2].valueLength = strlen (chownPath); ourAuthRightsItem[2].flags = 0; ourAuthRights.count = RIGHTS_COUNT; ourAuthRights.items = ourAuthRightsItem; ourAuthEnvItem[0].name = kAuthorizationEnvironmentPrompt; ourAuthEnvItem[0].value = prompt; ourAuthEnvItem[0].valueLength = strlen (prompt); ourAuthEnvItem[0].flags = 0; ourAuthEnvironment.count = 1; ourAuthEnvironment.items = ourAuthEnvItem; ourAuthFlags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights; // When this is called from the installer, the installer has already authenticated. // In that case we are already running with full root privileges so AuthorizationCopyRights() // does not request a password from the user again. err = AuthorizationCopyRights (gOurAuthRef, &ourAuthRights, &ourAuthEnvironment, ourAuthFlags, NULL); if (err == noErr) sIsAuthorized = true; return err; }
// Pass NULL for path when calling this routine from within BOINC Manager int SetBOINCAppOwnersGroupsAndPermissions(char *path) { char fullpath[MAXPATHLEN]; char dir_path[MAXPATHLEN]; char buf1[80]; ProcessSerialNumber ourPSN; FSRef ourFSRef, ref; char *p; Boolean isDirectory; OSStatus err = noErr; #define NUMBRANDS 3 char *saverName[NUMBRANDS]; saverName[0] = "BOINCSaver"; saverName[1] = "GridRepublic"; saverName[2] = "Progress Thru Processors"; #ifdef _DEBUG err = SetFakeMasterNames(); if (err) return err; #endif if (path == NULL) { // NULL means we were called from within BOINC Manager // Get the full path to this application's bundle (BOINC Manager's bundle) err = GetCurrentProcess (&ourPSN); if (err) return err; // Should never happen err = GetProcessBundleLocation(&ourPSN, &ourFSRef); if (err) return err; // Should never happen err = FSRefMakePath (&ourFSRef, (UInt8*)dir_path, sizeof(dir_path)); if (err) return err; // Should never happen } else strlcpy(dir_path, path, MAXPATHLEN); // Path to BOINC Manager's bundle was passed as argument if (strlen(fullpath) >= (MAXPATHLEN-1)) { ShowSecurityError("SetBOINCAppOwnersGroupsAndPermissions: path to Manager is too long"); return -1; } strlcpy(fullpath, dir_path, sizeof(fullpath)); #ifdef _DEBUG // chmod -R u=rwx,g=rwx,o=rx path/BOINCManager.app // 0775 = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH // Set read, write permission for user; read and execute permission for group and others err = DoPrivilegedExec(chmodPath, "-R", "u=rwx,g=rwx,o=rx", fullpath, NULL, NULL, NULL); #else // chmod -R u=rx,g=rx,o=rx path/BOINCManager.app // 0555 = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH // Set read, write permission for user; read and execute permission for group and others err = DoPrivilegedExec(chmodPath, "-R", "u=rx,g=rx,o=rx", fullpath, NULL, NULL, NULL); #endif if (err) return err; // Get the full path to BOINC Manager executable inside this application's bundle strlcat(fullpath, "/Contents/MacOS/", sizeof(fullpath)); // To allow for branding, assume name of executable inside bundle is same as name of bundle p = strrchr(dir_path, '/'); // Assume name of executable inside bundle is same as name of bundle if (p == NULL) p = dir_path - 1; strlcat(fullpath, p+1, sizeof(fullpath)); p = strrchr(fullpath, '.'); // Strip off bundle extension (".app") if (p) *p = '\0'; sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name); // chown boinc_master:boinc_master path/BOINCManager.app/Contents/MacOS/BOINCManager err = DoPrivilegedExec(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL); if (err) return err; #ifdef _DEBUG // chmod u=rwx,g=rwx,o=rx path/BOINCManager.app/Contents/MacOS/BOINCManager // 0775 = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH // Set read, write and execute permission for user & group, read & execute for others err = DoPrivilegedExec(chmodPath, "u=rwx,g=rwx,o=rx", fullpath, NULL, NULL, NULL, NULL); #else // chmod u=rx,g=rx,o=rx path/BOINCManager.app/Contents/MacOS/BOINCManager // 0555 = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH // Set read and execute permission for user, group & others err = DoPrivilegedExec(chmodPath, "u=rx,g=rx,o=rx", fullpath, NULL, NULL, NULL, NULL); #endif if (err) return err; // Get the full path to BOINC Clients inside this application's bundle strlcpy(fullpath, dir_path, sizeof(fullpath)); strlcat(fullpath, "/Contents/Resources/boinc", sizeof(fullpath)); if (strlen(fullpath) >= (MAXPATHLEN-1)) { ShowSecurityError("SetBOINCAppOwnersGroupsAndPermissions: path to client is too long"); return -1; } sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name); // chown boinc_master:boinc_master path/BOINCManager.app/Contents/Resources/boinc err = DoPrivilegedExec(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL); if (err) return err; #ifdef _DEBUG // chmod u=rwsx,g=rwsx,o=rx path/BOINCManager.app/Contents/Resources/boinc // 06775 = S_ISUID | S_ISGID | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH // Set setuid-on-execution, setgid-on-execution plus read, write and execute permission for user & group, read & execute for others err = DoPrivilegedExec(chmodPath, "u=rwsx,g=rwsx,o=rx", fullpath, NULL, NULL, NULL, NULL); #else // chmod u=rsx,g=rsx,o=rx path/BOINCManager.app/Contents/Resources/boinc // 06555 = S_ISUID | S_ISGID | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH // Set setuid-on-execution, setgid-on-execution plus read and execute permission for user, group & others err = DoPrivilegedExec(chmodPath, "u=rsx,g=rsx,o=rx", fullpath, NULL, NULL, NULL, NULL); #endif if (err) return err; for (int i=0; i<NUMBRANDS; i++) { // Version 6 screensaver has its own embedded switcher application, but older versions don't. // We don't allow unauthorized users to run the switcher application in the BOINC Data directory // because they could use it to run as user & group boinc_project and damage project files. // The screensaver's switcher application runs as user and group "nobody" to avoid this risk. // Does switcher exist in screensaver bundle? sprintf(fullpath, "/Library/Screen Savers/%s.saver/Contents/Resources/gfx_switcher", saverName[i]); err = FSPathMakeRef((StringPtr)fullpath, &ref, &isDirectory); // Does it exist? if ((err == noErr) && (! isDirectory)) { #ifdef _DEBUG sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name); // chown boinc_master:boinc_master "/Library/Screen Savers/BOINCSaver.saver/Contents/Resources/gfx_switcher" err = DoPrivilegedExec(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL); if (err) return err; // chmod u=rwx,g=rwx,o=rx "/Library/Screen Savers/BOINCSaver.saver/Contents/Resources/gfx_switcher" // 0775 = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH // Set read, write and execute permission for user & group; read and execute permission for others err = DoPrivilegedExec(chmodPath, "u=rwx,g=rwx,o=rx", fullpath, NULL, NULL, NULL, NULL); if (err) return err; #else sprintf(buf1, "root:%s", boinc_master_group_name); // chown root:boinc_master "/Library/Screen Savers/BOINCSaver.saver/Contents/Resources/gfx_switcher" err = DoPrivilegedExec(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL); if (err) return err; // chmod u=rsx,g=rx,o=rx "/Library/Screen Savers/BOINCSaver.saver/Contents/Resources/gfx_switcher" // 04555 = S_ISUID | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH // Set setuid-on-execution plus read and execute permission for user, group & others err = DoPrivilegedExec(chmodPath, "u=rsx,g=rx,o=rx", fullpath, NULL, NULL, NULL, NULL); if (err) return err; #endif } } return noErr; }