Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mac: After MacOS update, repair primary groups for users boinc_master, boinc_project #6088

Merged
merged 14 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 104 additions & 56 deletions client/check_security.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -36,8 +36,12 @@

#ifdef __APPLE__ // If Mac BOINC Manager
#include "mac_branding.h"
#include "mac_spawn.h"

bool IsUserInGroup(char* groupName);
static int check_boinc_users_primarygroupIds(
int useFakeProjectUserAndGroup, int isMacInstaller
);
#endif

static int CheckNestedDirectories(
Expand Down Expand Up @@ -75,8 +79,6 @@ char *bundlePath, char *dataPath, // These arguments are used only when called
#endif
int use_sandbox, int isManager, char* path_to_error, int len
) {
passwd *pw;
group *grp;
char dir_path[MAXPATHLEN], full_path[MAXPATHLEN];
struct stat sbuf;
int retval;
Expand Down Expand Up @@ -159,62 +161,42 @@ int use_sandbox, int isManager, char* path_to_error, int len
boinc_master_gid = getegid();
}

if ((!useFakeProjectUserAndGroup) || isMacInstaller) {
// Require absolute owner and group boinc_master:boinc_master
strlcpy(boinc_master_user_name, REAL_BOINC_MASTER_NAME, sizeof(boinc_master_user_name));
pw = getpwnam(boinc_master_user_name);
if (pw == NULL)
return -1006; // User boinc_master does not exist
boinc_master_uid = pw->pw_uid;

strlcpy(boinc_master_group_name, REAL_BOINC_MASTER_NAME, sizeof(boinc_master_group_name));
grp = getgrnam(boinc_master_group_name);
if (grp == NULL)
return -1007; // Group boinc_master does not exist
boinc_master_gid = grp->gr_gid;

// A MacOS update sometimes changes the PrimaryGroupID of user boinc_master to staff (20).
if (pw->pw_gid != grp->gr_gid) {
return -1301;
}
} else { // Use current user and group (see comment above)

pw = getpwuid(boinc_master_uid);
if (pw == NULL)
return -1008; // Should never happen
strlcpy(boinc_master_user_name, pw->pw_name, sizeof(boinc_master_user_name));

grp = getgrgid(boinc_master_gid);
if (grp == NULL)
return -1009;
strlcpy(boinc_master_group_name, grp->gr_name, sizeof(boinc_master_group_name));

}

if (useFakeProjectUserAndGroup) {
// For easier debugging of project applications
strlcpy(boinc_project_user_name, boinc_master_user_name, sizeof(boinc_project_user_name));
strlcpy(boinc_project_group_name, boinc_master_group_name, sizeof(boinc_project_group_name));
boinc_project_uid = boinc_master_uid;
boinc_project_gid = boinc_master_gid;
} else {
strlcpy(boinc_project_user_name, REAL_BOINC_PROJECT_NAME, sizeof(boinc_project_user_name));
pw = getpwnam(boinc_project_user_name);
if (pw == NULL)
return -1010; // User boinc_project does not exist
boinc_project_uid = pw->pw_uid;

strlcpy(boinc_project_group_name, REAL_BOINC_PROJECT_NAME, sizeof(boinc_project_group_name));
grp = getgrnam(boinc_project_group_name);
if (grp == NULL)
return -1011; // Group boinc_project does not exist
boinc_project_gid = grp->gr_gid;
#ifdef __APPLE__
char DataDirPath[MAXPATHLEN];
getcwd(DataDirPath, sizeof(DataDirPath));

// A MacOS update sometimes changes the PrimaryGroupID of user boinc_project to staff (20).
if (pw->pw_gid != grp->gr_gid) {
return -1302;
snprintf(full_path, sizeof(full_path),
"%s/%s", DataDirPath, FIX_BOINC_USERS_FILENAME
);
retval = stat(full_path, &sbuf);
if (retval)
return -1061;

if (sbuf.st_uid != 0) // root
return -1062;

if (sbuf.st_gid != boinc_master_gid)
return -1063;

if ((sbuf.st_mode & 07777) != 04555)
return -1064;

if (! isMacInstaller) {
// MacOS updates often change the PrimaryGroupID of boinc_master and
// boinc_project to 20 (staff.)
retval = check_boinc_users_primarygroupIds(useFakeProjectUserAndGroup, isMacInstaller);
if ((retval == -1301) || (retval == -1302)) {
snprintf(full_path, sizeof(full_path),
"\"%s/%s\"", DataDirPath, FIX_BOINC_USERS_FILENAME
);
printf("Permissions error %d, calling %s\n", retval, full_path);
callPosixSpawn(full_path); // Try to fix it
retval = check_boinc_users_primarygroupIds(useFakeProjectUserAndGroup, isMacInstaller);
}
}
if (retval)
return retval;
#endif

#if 0 // Manager is no longer setgid
#if (defined(__WXMAC__) || defined(_MAC_INSTALLER)) // If Mac BOINC Manager or installer
Expand Down Expand Up @@ -497,6 +479,72 @@ int use_sandbox, int isManager, char* path_to_error, int len
}


#ifdef __APPLE__
static int check_boinc_users_primarygroupIds(int useFakeProjectUserAndGroup, int isMacInstaller) {
passwd *pw;
group *grp;

if ((!useFakeProjectUserAndGroup) || isMacInstaller) {
// Require absolute owner and group boinc_master:boinc_master
strlcpy(boinc_master_user_name, REAL_BOINC_MASTER_NAME, sizeof(boinc_master_user_name));
pw = getpwnam(boinc_master_user_name);
if (pw == NULL)
return -1006; // User boinc_master does not exist
boinc_master_uid = pw->pw_uid;

strlcpy(boinc_master_group_name, REAL_BOINC_MASTER_NAME, sizeof(boinc_master_group_name));
grp = getgrnam(boinc_master_group_name);
if (grp == NULL)
return -1007; // Group boinc_master does not exist
boinc_master_gid = grp->gr_gid;

// A MacOS update sometimes changes the PrimaryGroupID of user boinc_master to staff (20).
if (pw->pw_gid != grp->gr_gid) {
return -1301;
}
} else { // Use current user and group (see comment above)

pw = getpwuid(boinc_master_uid);
if (pw == NULL)
return -1008; // Should never happen
strlcpy(boinc_master_user_name, pw->pw_name, sizeof(boinc_master_user_name));

grp = getgrgid(boinc_master_gid);
if (grp == NULL)
return -1009;
strlcpy(boinc_master_group_name, grp->gr_name, sizeof(boinc_master_group_name));

}

if (useFakeProjectUserAndGroup) {
// For easier debugging of project applications
strlcpy(boinc_project_user_name, boinc_master_user_name, sizeof(boinc_project_user_name));
strlcpy(boinc_project_group_name, boinc_master_group_name, sizeof(boinc_project_group_name));
boinc_project_uid = boinc_master_uid;
boinc_project_gid = boinc_master_gid;
} else {
strlcpy(boinc_project_user_name, REAL_BOINC_PROJECT_NAME, sizeof(boinc_project_user_name));
pw = getpwnam(boinc_project_user_name);
if (pw == NULL)
return -1010; // User boinc_project does not exist
boinc_project_uid = pw->pw_uid;

strlcpy(boinc_project_group_name, REAL_BOINC_PROJECT_NAME, sizeof(boinc_project_group_name));
grp = getgrnam(boinc_project_group_name);
if (grp == NULL)
return -1011; // Group boinc_project does not exist
boinc_project_gid = grp->gr_gid;

// A MacOS update sometimes changes the PrimaryGroupID of user boinc_project to staff (20).
if (pw->pw_gid != grp->gr_gid) {
return -1302;
}
}
return 0;
}
#endif


static int CheckNestedDirectories(
char * basepath, int depth,
int use_sandbox, int isManager,
Expand Down
3 changes: 2 additions & 1 deletion client/file_names.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2021 University of California
// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -77,6 +77,7 @@ extern void send_log_after(const char* filename, double t, MIOFILE& mf);
#define CPU_BENCHMARKS_FILE_NAME "cpu_benchmarks"
#define CREATE_ACCOUNT_FILENAME "create_account.xml"
#define DAILY_XFER_HISTORY_FILENAME "daily_xfer_history.xml"
#define FIX_BOINC_USERS_FILENAME "Fix_BOINC_Users"
#define GET_CURRENT_VERSION_FILENAME "get_current_version.xml"
#define GET_PROJECT_CONFIG_FILENAME "get_project_config.xml"
#define GLOBAL_PREFS_FILE_NAME "global_prefs.xml"
Expand Down
6 changes: 1 addition & 5 deletions clientgui/DlgEventLog.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -157,10 +157,6 @@ class CDlgEventLog : public DlgEventLogBase
private:
////@begin CDlgEventLog member variables
////@end CDlgEventLog member variables
wxTimer* m_pRefreshTimer;

wxInt32 m_iPreviousDocCount;

CDlgEventLogListCtrl* m_pList;
wxArrayInt m_iFilteredIndexes;
wxInt32 m_iTotalDocCount;
Expand Down
32 changes: 32 additions & 0 deletions clientgui/mac/MacFixUserGroups.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.

// MacFixUserGroups.cpp
//
// MacOS updates often change the PrimaryGroupID of boinc_master and
// boinc_project to 20 (staff.) This tiny executable fixes that.
//
// This must be called setuid root.

#include "mac_spawn.h"

int main(int argc, char *argv[])
{
callPosixSpawn ("dscl . -create /users/boinc_master PrimaryGroupID boinc_master");
callPosixSpawn ("dscl . -create /users/boinc_project PrimaryGroupID boinc_project");
return 0;
}
2 changes: 1 addition & 1 deletion clientgui/mac/SetupSecurity.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2023 University of California
// Copyright (C) 2025 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down
7 changes: 6 additions & 1 deletion mac_build/Mac_SA_Secure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# This file is part of BOINC.
# http://boinc.berkeley.edu
# Copyright (C) 2022 University of California
# Copyright (C) 2025 University of California
#
# BOINC is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -68,6 +68,7 @@
# and to create RealName key with empty string as value (for users)
# Updated 11/8/22 revised setprojectgrp ownership & permissions for MacOS 13
# Updated 4/6/23 revised setprojectgrp ownership to match PR #5061
#Updated 2/11/25 to add Fix_BOINC_Users
#
# WARNING: do not use this script with versions of BOINC older
# than 7.20.4
Expand Down Expand Up @@ -267,6 +268,10 @@ if [ -d locale ] ; then
set_perm_recursive locale boinc_master boinc_master u+r-w,g+r-w,o+r-w
fi

if [ -f Fix_BOINC_Users ] ; then
set_perm Fix_BOINC_Users root boinc_master 04555 # Fix_BOINC_Users
fi

if [ -f boinc ] ; then
set_perm boinc boinc_master boinc_master 6555 # boinc client
fi
Expand Down
Loading
Loading