int AddAdminUserToGroups(char *user_name, bool add_to_boinc_project) { #ifndef _DEBUG char buf1[80]; OSStatus err = noErr; sprintf(buf1, "/groups/%s", boinc_master_group_name); // "dscl . -merge /groups/boinc_master users user_name" err = DoPrivilegedExec(dsclPath, ".", "-merge", buf1, "users", user_name, NULL); if (err) return err; if (add_to_boinc_project) { sprintf(buf1, "/groups/%s", boinc_project_group_name); // "dscl . -merge /groups/boinc_project users user_name" err = DoPrivilegedExec(dsclPath, ".", "-merge", buf1, "users", user_name, NULL); if (err) return err; } err = ResynchSystem(); if (err != noErr) return err; #endif // ! _DEBUG return noErr; }
int CreateBOINCUsersAndGroups() { OSStatus err = noErr; err = CreateUserAndGroup(REAL_BOINC_MASTER_NAME, REAL_BOINC_MASTER_NAME); if (err != noErr) return err; err = CreateUserAndGroup(REAL_BOINC_PROJECT_NAME, REAL_BOINC_PROJECT_NAME); if (err != noErr) return err; err = ResynchSystem(); if (err != noErr) return err; return noErr; }
// Find all visible users. // If user is a member of group admin, add user to groups boinc_master and boinc_project. // Optionally add non-admin users to group boinc_master but not to group boinc_project. // Set login item for all members of group boinc_master to launch BOINC Manager. // If our install package included a skin, set those user's preferences to use that skin. // Optionally set BOINC as screensaver for all users running BOINC. OSErr UpdateAllVisibleUsers(long brandID) { DIR *dirp; dirent *dp; passwd *pw; uid_t saved_uid; Boolean deleteLoginItem; char skinName[256]; char s[256]; group grpAdmin, *grpAdminPtr; char adminBuf[32768]; group grpBOINC_master, *grpBOINC_masterPtr; char bmBuf[32768]; Boolean saverAlreadySetForAll = true; Boolean setSaverForAllUsers = false; Boolean allNonAdminUsersAreSet = true; Boolean allowNonAdminUsersToRunBOINC = false; Boolean found = false; FILE *f; OSStatus err; Boolean isGroupMember; #ifdef SANDBOX char *p; short i; err = getgrnam_r("admin", &grpAdmin, adminBuf, sizeof(adminBuf), &grpAdminPtr); if (err) { // Should never happen unless buffer too small puts("getgrnam(\"admin\") failed\n"); return -1; } err = getgrnam_r("boinc_master", &grpBOINC_master, bmBuf, sizeof(bmBuf), &grpBOINC_masterPtr); if (err) { // Should never happen unless buffer too small puts("getgrnam(\"boinc_master\") failed\n"); return -1; } #endif // SANDBOX FindSkinName(skinName, sizeof(skinName)); // Step through all users puts("Beginning first pass through all users\n"); dirp = opendir("/Users"); if (dirp == NULL) { // Should never happen puts("[1] opendir(\"/Users\") failed\n"); return -1; } while (true) { dp = readdir(dirp); if (dp == NULL) break; // End of list printf("[1] Checking user %s\n", dp->d_name); if (dp->d_name[0] == '.') continue; // Ignore names beginning with '.' // getpwnam works with either the full / login name (pw->pw_gecos) // or the short / Posix name (pw->pw_name) pw = getpwnam(dp->d_name); if (pw == NULL) { // "Deleted Users", "Shared", etc. printf("[1] %s not in getpwnam data base\n", dp->d_name); continue; } printf("[1] User %s: Posix name=%s, Full name=%s\n", dp->d_name, pw->pw_name, pw->pw_gecos); #ifdef SANDBOX isGroupMember = false; i = 0; while ((p = grpAdmin.gr_mem[i]) != NULL) { // Step through all users in group admin if (strcmp(p, pw->pw_name) == 0) { // User is a member of group admin, so add user to groups boinc_master and boinc_project printf("[1] User %s is a member of group admin\n", pw->pw_name); err = AddAdminUserToGroups(p); if (err != noErr) return err; isGroupMember = true; break; } ++i; } if (!isGroupMember) { i = 0; while ((p = grpBOINC_master.gr_mem[i]) != NULL) { // Step through all users in group boinc_master if (strcmp(p, pw->pw_name) == 0) { // User is a member of group boinc_master printf("[1] User %s is a member of group boinc_master\n", pw->pw_name); isGroupMember = true; break; } ++i; } } if (!isGroupMember) { allNonAdminUsersAreSet = false; } #else // SANDBOX isGroupMember = true; #endif // SANDBOX if (isGroupMember) { if ((strcmp(loginName, dp->d_name) == 0) || (strcmp(loginName, pw->pw_name) == 0)) { currentUserCanRunBOINC = true; } saved_uid = geteuid(); seteuid(pw->pw_uid); // Temporarily set effective uid to this user if (OSVersion < 0x1060) { f = popen("defaults -currentHost read com.apple.screensaver moduleName", "r"); } else { sprintf(s, "sudo -u %s defaults -currentHost read com.apple.screensaver moduleDict -dict", pw->pw_name); f = popen(s, "r"); } if (f) { found = false; while (PersistentFGets(s, sizeof(s), f)) { if (strstr(s, saverName[brandID])) { found = true; break; } } pclose(f); if (!found) { saverAlreadySetForAll = false; } } seteuid(saved_uid); // Set effective uid back to privileged user } // End if (isGroupMember) } // End while (true) closedir(dirp); ResynchSystem(); if (! allNonAdminUsersAreSet) { if (ShowMessage(true, "Users who are permitted to administer this computer will automatically be allowed to " "run and control %s.\n\n" "Do you also want non-administrative users to be able to run and control %s on this Mac?", brandName[brandID], brandName[brandID]) ) { allowNonAdminUsersToRunBOINC = true; currentUserCanRunBOINC = true; saverAlreadySetForAll = false; printf("[2] User answered Yes to allowing non-admin users to run %s\n", brandName[brandID]); } else { printf("[2] User answered No to allowing non-admin users to run %s\n", brandName[brandID]); } } else { puts("[2] All non-admin users are already members of group boinc_master\n"); } if (! saverAlreadySetForAll) { setSaverForAllUsers = ShowMessage(true, "Do you want to set %s as the screensaver for all %s users on this Mac?", brandName[brandID], brandName[brandID]); } // Step through all users a second time, setting non-admin users and / or our screensaver puts("Beginning second pass through all users\n"); dirp = opendir("/Users"); if (dirp == NULL) { // Should never happen puts("[2] opendir(\"/Users\") failed\n"); return -1; } while (true) { dp = readdir(dirp); if (dp == NULL) break; // End of list printf("[2] Checking user %s\n", dp->d_name); if (dp->d_name[0] == '.') continue; // Ignore names beginning with '.' pw = getpwnam(dp->d_name); if (pw == NULL) { // "Deleted Users", "Shared", etc. printf("[2] %s not in getpwnam data base\n", dp->d_name); continue; } printf("[2] User %s: Posix name=%s, Full name=%s\n", dp->d_name, pw->pw_name, pw->pw_gecos); #ifdef SANDBOX isGroupMember = false; i = 0; while ((p = grpAdmin.gr_mem[i]) != NULL) { // Step through all users in group admin if (strcmp(p, pw->pw_name) == 0) { // User is a member of group admin printf("[2] User %s is a member of group admin\n", pw->pw_name); isGroupMember = true; break; } ++i; } // If allNonAdminUsersAreSet, some older versions added non-admin users only to group // boinc_master; ensure all permitted BOINC users are also members of group boinc_project if (isGroupMember || allowNonAdminUsersToRunBOINC || allNonAdminUsersAreSet) { // Add to group boinc_master but not group boinc_project err = AddAdminUserToGroups(pw->pw_name); printf("[2] Calling AddAdminUserToGroups(%s)\n", pw->pw_name); isGroupMember = true; } #else // SANDBOX isGroupMember = true; #endif // SANDBOX saved_uid = geteuid(); seteuid(pw->pw_uid); // Temporarily set effective uid to this user deleteLoginItem = CheckDeleteFile(dp->d_name); if (CheckDeleteFile(pw->pw_name)) { deleteLoginItem = true; } if (!isGroupMember) { deleteLoginItem = true; } SetLoginItem(brandID, deleteLoginItem); // Set login item for this user if (isGroupMember) { SetSkinInUserPrefs(dp->d_name, skinName); if (setSaverForAllUsers) { if (OSVersion < 0x1060) { sprintf(s, "defaults -currentHost write com.apple.screensaver moduleName %s", saverNameEscaped[brandID]); system (s); sprintf(s, "defaults -currentHost write com.apple.screensaver modulePath /Library/Screen\\ Savers/%s.saver", saverNameEscaped[brandID]); } else { sprintf(s, "sudo -u %s defaults -currentHost write com.apple.screensaver moduleDict -dict moduleName %s path /Library/Screen\\ Savers/%s.saver", pw->pw_name, saverNameEscaped[brandID], saverNameEscaped[brandID]); } system (s); } } seteuid(saved_uid); // Set effective uid back to privileged user } closedir(dirp); ResynchSystem(); return noErr; }
static OSStatus CreateUserAndGroup(char * user_name, char * group_name) { OSStatus err = noErr; passwd *pw = NULL; group *grp = NULL; uid_t userid = 0; gid_t groupid = 0; gid_t usergid = 0; Boolean userExists = false; Boolean groupExists = false; short i; static short start_id = MIN_ID; char buf1[80]; char buf2[80]; char buf3[80]; char buf4[80]; // OS 10.4 has problems with Accounts pane if we create uid or gid > 501 pw = getpwnam(user_name); if (pw) { userid = pw->pw_uid; userExists = true; } grp = getgrnam(group_name); if (grp) { groupid = grp->gr_gid; groupExists = true; } sprintf(buf1, "/groups/%s", group_name); sprintf(buf2, "/users/%s", user_name); if ( userExists && groupExists ) goto setRealName; // User and group already exist // If only user or only group exists, try to use the same ID for the one we create if (userExists) { // User exists but group does not usergid = pw->pw_gid; if (usergid) { grp = getgrgid(usergid); if (grp == NULL) // Set the group ID = users existing group if this group ID is available groupid = usergid; } if (groupid == 0) { grp = getgrgid(userid); if (grp == NULL) // Set the group ID = user ID if this group ID is available groupid = userid; } } else { if (groupExists) { // Group exists but user does not pw = getpwuid(groupid); if (pw == NULL) // Set the user ID = group ID if this user ID is available userid = groupid; } } // We need to find an available user ID, group ID, or both. Find a value that is currently // neither a user ID or a group ID. // If we need both a new user ID and a new group ID, finds a value that can be used for both. if ( (userid == 0) || (groupid == 0) ) { for(i=start_id; ; i++) { if ((uid_t)i != userid) { pw = getpwuid((uid_t)i); if (pw) continue; // Already exists as a user ID of a different user } if ((gid_t)i != groupid) { grp = getgrgid((gid_t)i); if (grp) continue; // Already exists as a group ID of a different group } if (! userExists) userid = (uid_t)i; if (! groupExists) groupid = (gid_t)i; start_id = i + 1; // Start with next higher value next time break; // Success! } } sprintf(buf3, "%d", groupid); sprintf(buf4, "%d", userid); if (! groupExists) { // If we need to create group // Something like "dscl . -create /groups/boinc_master" err = DoPrivilegedExec(dsclPath, ".", "-create", buf1, NULL, NULL, NULL); if (err) return err; // Something like "dscl . -create /groups/boinc_master gid 33" err = DoPrivilegedExec(dsclPath, ".", "-create", buf1, "gid", buf3, NULL); if (err) return err; } // if (! groupExists) if (! userExists) { // If we need to create user // Something like "dscl . -create /users/boinc_master" err = DoPrivilegedExec(dsclPath, ".", "-create", buf2, NULL, NULL, NULL); if (err) return err; // Something like "dscl . -create /users/boinc_master uid 33" err = DoPrivilegedExec(dsclPath, ".", "-create", buf2, "uid", buf4, NULL); if (err) return err; // Prevent a security hole by not allowing a login from this user // Something like "dscl . -create /users/boinc_master shell /usr/bin/false" err = DoPrivilegedExec(dsclPath, ".", "-create", buf2, "shell", "/usr/bin/false", NULL); if (err) return err; // Something like "dscl . -create /users/boinc_master home /var/empty" err = DoPrivilegedExec(dsclPath, ".", "-create", buf2, "home", "/var/empty", NULL); if (err) return err; } // if (! userExists) // Always set the user gid if we created either the user or the group or both // Something like "dscl . -create /users/boinc_master gid 33" err = DoPrivilegedExec(dsclPath, ".", "-create", buf2, "gid", buf3, NULL); if (err) return err; setRealName: // Always set the RealName field to an empty string // Note: create RealName with empty string fails under OS 10.7, but // creating it with non-empty string and changing to empty string does work. // // Something like "dscl . -create /users/boinc_master RealName tempName" err = DoPrivilegedExec(dsclPath, ".", "-create", buf2, "RealName", user_name, NULL); if (err) return err; // Something like 'dscl . -change /users/boinc_master RealName ""' err = DoPrivilegedExec(dsclPath, ".", "-change", buf2, "RealName", user_name, ""); if (err) return err; err = ResynchSystem(); if (err != noErr) return err; SleepTicks(120); return noErr; }
// NOTE: getgrnam and getgrgid use one static memory area to return their results, // so each call to getgrnam or getgrgid overwrites the data from any previous calls. void CheckUserAndGroupConflicts() { #ifdef SANDBOX passwd *pw = NULL; group *grp = NULL; gid_t boinc_master_gid = 0, boinc_project_gid = 0; uid_t boinc_master_uid = 0, boinc_project_uid = 0; FILE *f; char cmd[256], buf[256]; int entryCount; entryCount = 0; grp = getgrnam(boinc_master_group_name); if (grp) { boinc_master_gid = grp->gr_gid; sprintf(cmd, "dscl . -search /Groups PrimaryGroupID %d", boinc_master_gid); f = popen(cmd, "r"); if (f) { while (PersistentFGets(buf, sizeof(buf), f)) { if (strstr(buf, "PrimaryGroupID")) { ++entryCount; } } pclose(f); } } if (entryCount > 1) { system ("dscl . -delete /groups/boinc_master"); // User boinc_master must have group boinc_master as its primary group. // Since this group no longer exists, delete the user as well. system ("dscl . -delete /users/boinc_master"); ResynchSystem(); } entryCount = 0; grp = getgrnam(boinc_project_group_name); if (grp) { boinc_project_gid = grp->gr_gid; sprintf(cmd, "dscl . -search /Groups PrimaryGroupID %d", boinc_project_gid); f = popen(cmd, "r"); if (f) { while (PersistentFGets(buf, sizeof(buf), f)) { if (strstr(buf, "PrimaryGroupID")) { ++entryCount; } } pclose(f); } } if (entryCount > 1) { system ("dscl . -delete /groups/boinc_project"); // User boinc_project must have group boinc_project as its primary group. // Since this group no longer exists, delete the user as well. system ("dscl . -delete /users/boinc_project"); ResynchSystem(); } entryCount = 0; pw = getpwnam(boinc_master_user_name); if (pw) { boinc_master_uid = pw->pw_uid; sprintf(cmd, "dscl . -search /Users UniqueID %d", boinc_master_uid); f = popen(cmd, "r"); if (f) { while (PersistentFGets(buf, sizeof(buf), f)) { if (strstr(buf, "UniqueID")) { ++entryCount; } } pclose(f); } } if (entryCount > 1) { system ("dscl . -delete /users/boinc_master"); ResynchSystem(); } entryCount = 0; pw = getpwnam(boinc_project_user_name); if (pw) { boinc_project_uid = pw->pw_uid; sprintf(cmd, "dscl . -search /Users UniqueID %d", boinc_project_uid); f = popen(cmd, "r"); if (f) { while (PersistentFGets(buf, sizeof(buf), f)) { if (strstr(buf, "UniqueID")) { ++entryCount; } } pclose(f); } } if (entryCount > 1) { system ("dscl . -delete /users/boinc_project"); ResynchSystem(); } #endif // SANDBOX }