1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2017 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17
18 // SetupSecurity.cpp
19
20
21 #include <Carbon/Carbon.h>
22
23 #include <grp.h> // getgrname, getgrgid
24 #include <pwd.h> // getpwnam, getpwuid, getuid
25 #include <unistd.h> // usleep
26 #include <sys/param.h> // for MAXPATHLEN
27 #include <sys/stat.h>
28 #include <dirent.h>
29 #include <spawn.h>
30
31 #include "file_names.h"
32 #include "mac_util.h"
33 #include "SetupSecurity.h"
34
35 // Set VERBOSE_TEST to 1 for debugging DoSudoPosixSpawn()
36 #define VERBOSE_TEST 0
37
38 static OSStatus UpdateNestedDirectories(char * basepath);
39 static OSStatus MakeXMLFilesPrivate(char * basepath);
40 static OSStatus DoSudoPosixSpawn(const char *pathToTool, char *arg1, char *arg2, char *arg3, char *arg4, char *arg5, char *arg6);
41 #ifndef __x86_64__
42 static pascal Boolean ErrorDlgFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *theItemHit);
43 #endif
44 #ifdef _DEBUG
45 static OSStatus SetFakeMasterNames(void);
46 #endif
47 static OSStatus CreateUserAndGroup(char * user_name, char * group_name);
48 static double dtime(void);
49 static void SleepSeconds(double seconds);
50
51 #if VERBOSE_TEST
52 extern void print_to_log_file(const char *format, ...);
53 #endif
54
55 #define DELAY_SECONDS 0.05
56 #define DELAY_SECONDS_R 0.167
57
58 #define REAL_BOINC_MASTER_NAME "boinc_master"
59 #define REAL_BOINC_PROJECT_NAME "boinc_project"
60
61 #ifdef _DEBUG
62 // GDB can't attach to applications which are running as a diferent user or group so
63 // it ignores the S_ISUID and S_ISGID permission bits when launching an application.
64 // To work around this, the _DEBUG version uses the current user and group.
65 //
66 // NOTE: The Manager and Client call these routines only "#ifdef _DEBUG" (i.e.,
67 // only from the DEVELOPMENT BUILD), never from the Deployment build.
68 //
69 // As of January, 2017: In the past, the client and BOINC Manager used to call
70 // routines in this source file when debugging with SANDBOX defined, but they
71 // can no longer do so because various operations here must be performed as root
72 // and the AuthorizationExecuteWithPrivileges() API was deprecated as of OS 10.7.
73 // Please see the comments in check_security.cpp for more details describing the
74 // new method replacing that approach. Because the new approach has not been
75 // thoroughly tested, we have not yet removed the client-specific code or the
76 // manager-specific code from this file.
77 //
78 static char boinc_master_user_name[64];
79 static char boinc_master_group_name[64];
80 static char boinc_project_user_name[64];
81 static char boinc_project_group_name[64];
82 #else
83 #define boinc_master_user_name REAL_BOINC_MASTER_NAME
84 #define boinc_master_group_name REAL_BOINC_MASTER_NAME
85 #define boinc_project_user_name REAL_BOINC_PROJECT_NAME
86 #define boinc_project_group_name REAL_BOINC_PROJECT_NAME
87 #endif
88
89 #define MIN_ID 501 /* Minimum user ID / Group ID to create */
90
91 static char dsclPath[] = "/usr/bin/dscl";
92 static char chmodPath[] = "/bin/chmod";
93 static char chownPath[] = "/usr/sbin/chown";
94 #define RIGHTS_COUNT 3 /* Count of the 3 above items */
95
CreateBOINCUsersAndGroups()96 int CreateBOINCUsersAndGroups() {
97 OSStatus err = noErr;
98
99 if (geteuid() != 0) {
100 ShowSecurityError("CreateBOINCUsersAndGroups must be called as root");
101 }
102
103 err = CreateUserAndGroup(REAL_BOINC_MASTER_NAME, REAL_BOINC_MASTER_NAME);
104 if (err != noErr)
105 return err;
106
107 err = CreateUserAndGroup(REAL_BOINC_PROJECT_NAME, REAL_BOINC_PROJECT_NAME);
108 if (err != noErr)
109 return err;
110
111 err = ResynchDSSystem();
112 if (err != noErr)
113 return err;
114
115 return noErr;
116 }
117
118
119 // Pass NULL for path when calling this routine from within BOINC Manager
SetBOINCAppOwnersGroupsAndPermissions(char * path)120 int SetBOINCAppOwnersGroupsAndPermissions(char *path) {
121 char fullpath[MAXPATHLEN];
122 char dir_path[MAXPATHLEN];
123 char buf1[80];
124 char *p;
125 struct stat sbuf;
126 Boolean isDirectory;
127 OSStatus err = noErr;
128
129 #define NUMBRANDS 3
130
131 char *saverName[NUMBRANDS];
132
133 saverName[0] = "BOINCSaver";
134 saverName[1] = "GridRepublic";
135 saverName[2] = "Progress Thru Processors";
136
137 if (geteuid() != 0) {
138 ShowSecurityError("SetBOINCAppOwnersGroupsAndPermissions must be called as root");
139 }
140
141
142 #ifdef _DEBUG
143 err = SetFakeMasterNames();
144 if (err)
145 return err;
146 #endif
147
148 if (path == NULL) { // NULL means we were called from within BOINC Manager
149 // Get the full path to this application's bundle (BOINC Manager's bundle)
150 dir_path[0] = '\0';
151 // Get the full path to our executable inside this application's bundle
152 getPathToThisApp(dir_path, sizeof(dir_path));
153 if (!dir_path[0]) {
154 ShowSecurityError(false, false, false, "Couldn't get path to self.");
155 return -1;
156 }
157
158 // To allow for branding, assume name of executable inside bundle is same as name of bundle
159 p = strrchr(dir_path, '/'); // Assume name of executable inside bundle is same as name of bundle
160 if (p == NULL)
161 p = dir_path - 1;
162 strlcpy(fullpath, p+1, sizeof(fullpath));
163 p = strrchr(fullpath, '.'); // Strip off bundle extension (".app")
164 if (p)
165 *p = '\0';
166
167 strlcat(dir_path, "/Contents/MacOS/", sizeof(dir_path));
168 strlcat(dir_path, fullpath, sizeof(dir_path));
169 } else {
170 if (strlen(path) >= (MAXPATHLEN-1)) {
171 ShowSecurityError("SetBOINCAppOwnersGroupsAndPermissions: path to Manager is too long");
172 return -1;
173 }
174
175 strlcpy(dir_path, path, MAXPATHLEN); // Path to BOINC Manager's bundle was passed as argument
176 }
177
178 strlcpy(fullpath, dir_path, sizeof(fullpath));
179
180 #ifdef _DEBUG
181 // chmod -R u=rwx,g=rwx,o=rx path/BOINCManager.app
182 // 0775 = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
183 // Set read, write permission for user; read and execute permission for group and others
184 err = DoSudoPosixSpawn(chmodPath, "-R", "u=rwx,g=rwx,o=rx", fullpath, NULL, NULL, NULL);
185 #else
186 // chmod -R u=rx,g=rx,o=rx path/BOINCManager.app
187 // 0555 = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
188 // Set read, write permission for user; read and execute permission for group and others
189 err = DoSudoPosixSpawn(chmodPath, "-R", "u=rx,g=rx,o=rx", fullpath, NULL, NULL, NULL);
190 #endif
191 if (err)
192 return err;
193
194 // Get the full path to BOINC Manager executable inside this application's bundle
195 strlcat(fullpath, "/Contents/MacOS/", sizeof(fullpath));
196 // To allow for branding, assume name of executable inside bundle is same as name of bundle
197 p = strrchr(dir_path, '/'); // Assume name of executable inside bundle is same as name of bundle
198 if (p == NULL)
199 p = dir_path - 1;
200 strlcat(fullpath, p+1, sizeof(fullpath));
201 p = strrchr(fullpath, '.'); // Strip off bundle extension (".app")
202 if (p)
203 *p = '\0';
204
205 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name);
206 // chown boinc_master:boinc_master path/BOINCManager.app/Contents/MacOS/BOINCManager
207 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
208 if (err)
209 return err;
210
211 #ifdef _DEBUG
212 // chmod u=rwx,g=rwx,o=rx path/BOINCManager.app/Contents/MacOS/BOINCManager
213 // 0775 = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH
214 // Set read, write and execute permission for user & group, read & execute for others
215 err = DoSudoPosixSpawn(chmodPath, "u=rwx,g=rwx,o=rx", fullpath, NULL, NULL, NULL, NULL);
216 #else
217 // chmod u=rx,g=rx,o=rx path/BOINCManager.app/Contents/MacOS/BOINCManager
218 // 0555 = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
219 // Set read and execute permission for user, group & others
220 err = DoSudoPosixSpawn(chmodPath, "u=rx,g=rx,o=rx", fullpath, NULL, NULL, NULL, NULL);
221 #endif
222 if (err)
223 return err;
224
225 // Get the full path to BOINC Clients inside this application's bundle
226 strlcpy(fullpath, dir_path, sizeof(fullpath));
227 strlcat(fullpath, "/Contents/Resources/boinc", sizeof(fullpath));
228 if (strlen(fullpath) >= (MAXPATHLEN-1)) {
229 ShowSecurityError("SetBOINCAppOwnersGroupsAndPermissions: path to client is too long");
230 return -1;
231 }
232
233 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name);
234 // chown boinc_master:boinc_master path/BOINCManager.app/Contents/Resources/boinc
235 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
236 if (err)
237 return err;
238
239 #ifdef _DEBUG
240 // chmod u=rwsx,g=rwsx,o=rx path/BOINCManager.app/Contents/Resources/boinc
241 // 06775 = S_ISUID | S_ISGID | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH
242 // Set setuid-on-execution, setgid-on-execution plus read, write and execute permission for user & group, read & execute for others
243 err = DoSudoPosixSpawn(chmodPath, "u=rwsx,g=rwsx,o=rx", fullpath, NULL, NULL, NULL, NULL);
244 #else
245 // chmod u=rsx,g=rsx,o=rx path/BOINCManager.app/Contents/Resources/boinc
246 // 06555 = S_ISUID | S_ISGID | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
247 // Set setuid-on-execution, setgid-on-execution plus read and execute permission for user, group & others
248 err = DoSudoPosixSpawn(chmodPath, "u=rsx,g=rsx,o=rx", fullpath, NULL, NULL, NULL, NULL);
249 #endif
250 if (err)
251 return err;
252
253 for (int i=0; i<NUMBRANDS; i++) {
254 // Version 6 screensaver has its own embedded switcher application, but older versions don't.
255 // We don't allow unauthorized users to run the switcher application in the BOINC Data directory
256 // because they could use it to run as user & group boinc_project and damage project files.
257 // The screensaver's switcher application runs as user and group "nobody" to avoid this risk.
258
259 // Does switcher exist in screensaver bundle?
260 sprintf(fullpath, "/Library/Screen Savers/%s.saver/Contents/Resources/gfx_switcher", saverName[i]);
261 err = stat(fullpath, &sbuf);
262 isDirectory = S_ISDIR(sbuf.st_mode);
263 if ((err == noErr) && (! isDirectory)) {
264 #ifdef _DEBUG
265 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name);
266 // chown boinc_master:boinc_master "/Library/Screen Savers/BOINCSaver.saver/Contents/Resources/gfx_switcher"
267 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
268 if (err)
269 return err;
270
271 // chmod u=rwx,g=rwx,o=rx "/Library/Screen Savers/BOINCSaver.saver/Contents/Resources/gfx_switcher"
272 // 0775 = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH
273 // Set read, write and execute permission for user & group; read and execute permission for others
274 err = DoSudoPosixSpawn(chmodPath, "u=rwx,g=rwx,o=rx", fullpath, NULL, NULL, NULL, NULL);
275 if (err)
276 return err;
277 #else
278 sprintf(buf1, "root:%s", boinc_master_group_name);
279 // chown root:boinc_master "/Library/Screen Savers/BOINCSaver.saver/Contents/Resources/gfx_switcher"
280 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
281 if (err)
282 return err;
283
284 // chmod u=rsx,g=rx,o=rx "/Library/Screen Savers/BOINCSaver.saver/Contents/Resources/gfx_switcher"
285 // 04555 = S_ISUID | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
286 // Set setuid-on-execution plus read and execute permission for user, group & others
287 err = DoSudoPosixSpawn(chmodPath, "u=rsx,g=rx,o=rx", fullpath, NULL, NULL, NULL, NULL);
288 if (err)
289 return err;
290 #endif
291 }
292 }
293
294 return noErr;
295 }
296
297
SetBOINCDataOwnersGroupsAndPermissions()298 int SetBOINCDataOwnersGroupsAndPermissions() {
299 Boolean isDirectory;
300 char fullpath[MAXPATHLEN];
301 char buf1[80];
302 struct stat sbuf;
303 OSStatus err = noErr;
304 OSStatus result;
305 char *BOINCDataDirPath = "/Library/Application Support/BOINC Data";
306
307 if (geteuid() != 0) {
308 ShowSecurityError("SetBOINCDataOwnersGroupsAndPermissions must be called as root");
309 }
310
311 #ifdef _DEBUG
312 err = SetFakeMasterNames();
313 if (err)
314 return err;
315 #endif
316
317 strlcpy(fullpath, BOINCDataDirPath, MAXPATHLEN);
318
319 // Does BOINC Data directory exist?
320 result = stat(fullpath, &sbuf);
321 isDirectory = S_ISDIR(sbuf.st_mode);
322 if ((result != noErr) || (! isDirectory))
323 return dirNFErr; // BOINC Data Directory does not exist
324
325 // Set owner and group of BOINC Data directory's contents
326 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name);
327 // chown -R boinc_master:boinc_master "/Library/Application Support/BOINC Data"
328 err = DoSudoPosixSpawn(chownPath, "-R", buf1, BOINCDataDirPath, NULL, NULL, NULL);
329 if (err)
330 return err;
331
332 #if 0 // Redundant if we already set BOINC Data directory to boinc_master:boinc_master
333 // Set owner and group of BOINC Data directory itself
334 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name);
335 // chown boinc_master:boinc_master "/Library/Application Support/BOINC Data"
336 err = DoSudoPosixSpawn(chownPath, buf1, BOINCDataDirPath, NULL, NULL, NULL, NULL);
337 if (err)
338 return err;
339 #endif
340
341 // Set permissions of BOINC Data directory's contents:
342 // ss_config.xml is world-readable so screensaver coordinator can read it
343 // all other *.xml are not world-readable to keep authenticators private
344 // gui_rpc_auth.cfg is not world-readable to keep RPC password private
345 // all other files are world-readable so default screensaver can read them
346
347 // First make all files world-readable (temporarily)
348 // chmod -R u+rw,g+rw,o+r-w "/Library/Application Support/BOINC Data"
349 // 0661 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
350 // Set read and write permission for user and group, read-only for others (leaves execute bits unchanged)
351 err = DoSudoPosixSpawn(chmodPath, "-R", "u+rw,g+rw,o+r-w", BOINCDataDirPath, NULL, NULL, NULL);
352 if (err)
353 return err;
354
355 // Next make gui_rpc_auth.cfg not world-readable to keep RPC password private
356 // Does gui_rpc_auth.cfg file exist?
357 strlcpy(fullpath, BOINCDataDirPath, MAXPATHLEN);
358 strlcat(fullpath, "/", MAXPATHLEN);
359 strlcat(fullpath, GUI_RPC_PASSWD_FILE, MAXPATHLEN);
360
361 result = stat(fullpath, &sbuf);
362 isDirectory = S_ISDIR(sbuf.st_mode);
363 if ((result == noErr) && (! isDirectory)) {
364 // Make gui_rpc_auth.cfg file readable and writable only by user boinc_master and group boinc_master
365
366 // Set owner and group of gui_rpc_auth.cfg file
367 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name);
368 // chown boinc_master:boinc_master "/Library/Application Support/BOINC Data/gui_rpc_auth.cfg"
369 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
370 if (err)
371 return err;
372
373 // chmod u=rw,g=rw,o= "/Library/Application Support/BOINC Data/gui_rpc_auth.cfg"
374 // 0660 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
375 // Set read and write permission for user and group, no access for others
376 err = DoSudoPosixSpawn(chmodPath, "u=rw,g=rw,o=", fullpath, NULL, NULL, NULL, NULL);
377 if (err)
378 return err;
379 } // gui_rpc_auth.cfg
380
381 // Next make all *.xml files not world-readable to keep authenticators private
382 err = MakeXMLFilesPrivate(BOINCDataDirPath);
383 if (err)
384 return err;
385
386 // Next make ss_config.xml world-readable so screensaver coordinator can read it
387 // Does screensaver config file ss_config.xml exist?
388 strlcpy(fullpath, BOINCDataDirPath, MAXPATHLEN);
389 strlcat(fullpath, "/", MAXPATHLEN);
390 strlcat(fullpath, SS_CONFIG_FILE, MAXPATHLEN);
391
392 result = stat(fullpath, &sbuf);
393 isDirectory = S_ISDIR(sbuf.st_mode);
394 if ((result == noErr) && (! isDirectory)) {
395 // Make ss_config.xml file world readable but writable only by user boinc_master and group boinc_master
396
397 // Set owner and group of ss_config.xml file
398 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name);
399 // chown boinc_master:boinc_master "/Library/Application Support/BOINC Data/ss_config.xml"
400 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
401 if (err)
402 return err;
403
404 // chmod u=rw,g=rw,o=r "/Library/Application Support/BOINC Data/ss_config.xml"
405 // 0664 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
406 // Set read and write permission for user and group, read-only for others
407 err = DoSudoPosixSpawn(chmodPath, "u=rw,g=rw,o=r", fullpath, NULL, NULL, NULL, NULL);
408 if (err)
409 return err;
410 } // ss_config.xml
411
412
413 // Set permissions of BOINC Data directory itself
414 // chmod u=rwx,g=rwx,o=x "/Library/Application Support/BOINC Data"
415 // 0771 = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IXOTH
416 // Set read, write and execute permission for user & group; execute-only permission for others
417 err = DoSudoPosixSpawn(chmodPath, "u=rwx,g=rwx,o=x", BOINCDataDirPath, NULL, NULL, NULL, NULL);
418 if (err)
419 return err;
420
421
422 // Does projects directory exist?
423 strlcpy(fullpath, BOINCDataDirPath, MAXPATHLEN);
424 strlcat(fullpath, "/", MAXPATHLEN);
425 strlcat(fullpath, PROJECTS_DIR, MAXPATHLEN);
426
427 result = stat(fullpath, &sbuf);
428 isDirectory = S_ISDIR(sbuf.st_mode);
429 if ((result == noErr) && (isDirectory)) {
430 // Set owner and group of projects directory and it's contents
431 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_project_group_name);
432 // chown -R boinc_master:boinc_project "/Library/Application Support/BOINC Data/projects"
433 err = DoSudoPosixSpawn(chownPath, "-Rh", buf1, fullpath, NULL, NULL, NULL);
434 if (err)
435 return err;
436
437 #if 0 // Redundant if the same as projects directory's contents
438 // Set owner and group of projects directory itself
439 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_project_group_name);
440 // chown -R boinc_master:boinc_project "/Library/Application Support/BOINC Data/projects"
441 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
442 if (err)
443 return err;
444 #endif
445
446 // Set permissions of project directories' contents
447 // Contents of project directories must be world-readable so BOINC Client can read
448 // files written by projects which have user boinc_project and group boinc_project
449 // chmod -R u+rw,g+rw,o+r-w "/Library/Application Support/BOINC Data/projects"
450 // 0664 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
451 // set read and write permission for user and group, no access for others (leaves execute bits unchanged)
452 err = DoSudoPosixSpawn(chmodPath, "-R", "u+rw,g+rw,o+r-w", fullpath, NULL, NULL, NULL);
453 if (err)
454 return err;
455
456 // Set permissions for projects directory itself (not its contents)
457 // chmod u=rwx,g=rwx,o= "/Library/Application Support/BOINC Data/projects"
458 // 0770 = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP
459 // Set read, write and execute permission for user & group, no access for others
460 err = DoSudoPosixSpawn(chmodPath, "u=rwx,g=rwx,o=", fullpath, NULL, NULL, NULL, NULL);
461 if (err)
462 return err;
463
464 // Set execute permissions for project subdirectories
465 err = UpdateNestedDirectories(fullpath); // Sets execute for user, group and others
466 if (err)
467 return err;
468 } // projects directory
469
470 // Does slots directory exist?
471 strlcpy(fullpath, BOINCDataDirPath, MAXPATHLEN);
472 strlcat(fullpath, "/", MAXPATHLEN);
473 strlcat(fullpath, SLOTS_DIR, MAXPATHLEN);
474
475 result = stat(fullpath, &sbuf);
476 isDirectory = S_ISDIR(sbuf.st_mode);
477 if ((result == noErr) && (isDirectory)) {
478 // Set owner and group of slots directory and it's contents
479 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_project_group_name);
480 // chown -R boinc_master:boinc_project "/Library/Application Support/BOINC Data/slots"
481 err = DoSudoPosixSpawn(chownPath, "-Rh", buf1, fullpath, NULL, NULL, NULL);
482 if (err)
483 return err;
484
485 #if 0 // Redundant if the same as slots directory's contents
486 // Set owner and group of slots directory itself
487 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_project_group_name);
488 // chown boinc_master:boinc_project "/Library/Application Support/BOINC Data/slots"
489 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
490 if (err)
491 return err;
492 #endif
493
494 // Set permissions of slot directories' contents
495 // Contents of slot directories must be world-readable so BOINC Client can read
496 // files written by projects which have user boinc_project and group boinc_project
497 // chmod -R u+rw,g+rw,o+r-w "/Library/Application Support/BOINC Data/slots"
498 // 0664 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
499 // set read and write permission for user and group, no access for others (leaves execute bits unchanged)
500 err = DoSudoPosixSpawn(chmodPath, "-R", "u+rw,g+rw,o+r-w", fullpath, NULL, NULL, NULL);
501 if (err)
502 return err;
503
504 // Set permissions for slots directory itself (not its contents)
505 // chmod u=rwx,g=rwx,o= "/Library/Application Support/BOINC Data/slots"
506 // 0770 = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP
507 // Set read, write and execute permission for user & group, no access for others
508 err = DoSudoPosixSpawn(chmodPath, "u=rwx,g=rwx,o=", fullpath, NULL, NULL, NULL, NULL);
509 if (err)
510 return err;
511
512 // Set execute permissions for slot subdirectories
513 err = UpdateNestedDirectories(fullpath); // Sets execute for user, group and others
514 if (err)
515 return err;
516 } // slots directory
517
518 // Does locale directory exist?
519 strlcpy(fullpath, BOINCDataDirPath, MAXPATHLEN);
520 strlcat(fullpath, "/locale", MAXPATHLEN);
521
522 result = stat(fullpath, &sbuf);
523 isDirectory = S_ISDIR(sbuf.st_mode);
524 if ((result == noErr) && (isDirectory)) {
525 #if 0 // Redundant if we already set contents of BOINC Data directory to boinc_master:boinc_master
526 // Set owner and group of locale directory and all its contents
527 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name);
528 // chown -R boinc_master:boinc_master "/Library/Application Support/BOINC Data/locale"
529 err = DoSudoPosixSpawn(chownPath, "-R", buf1, fullpath, NULL, NULL, NULL);
530 if (err)
531 return err;
532 #endif
533
534 // chmod -R u+r-w,g+r-w,o+r-w "/Library/Application Support/BOINC Data/locale"
535 // 0550 = S_IRUSR | S_IXUSR | S_IRGRP | S_IXUSR | S_IROTH | S_IXOTH
536 // Set execute permission for user, group, and others if it was set for any
537 err = DoSudoPosixSpawn(chmodPath, "-R", "+X", fullpath, NULL, NULL, NULL);
538 // Set read-only permission for user, group, and others (leaves execute bits unchanged)
539 err = DoSudoPosixSpawn(chmodPath, "-R", "u+r-w,g+r-w,o+r-w", fullpath, NULL, NULL, NULL);
540 if (err)
541 return err;
542 } // locale directory
543
544 // Does switcher directory exist?
545 strlcpy(fullpath, BOINCDataDirPath, MAXPATHLEN);
546 strlcat(fullpath, "/", MAXPATHLEN);
547 strlcat(fullpath, SWITCHER_DIR, MAXPATHLEN);
548
549 result = stat(fullpath, &sbuf);
550 isDirectory = S_ISDIR(sbuf.st_mode);
551 if ((result == noErr) && (isDirectory)) {
552 #if 0 // Redundant if we already set contents of BOINC Data directory to boinc_master:boinc_master
553 // Set owner and group of switcher directory
554 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_master_group_name);
555 // chown boinc_master:boinc_master "/Library/Application Support/BOINC Data/switcher"
556 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
557 if (err)
558 return err;
559 #endif
560
561 // chmod u=rx,g=rx,o= "/Library/Application Support/BOINC Data/switcher"
562 // 0550 = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP
563 // Set read and execute permission for user and group, no access for others
564 err = DoSudoPosixSpawn(chmodPath, "u=rx,g=rx,o=", fullpath, NULL, NULL, NULL, NULL);
565 if (err)
566 return err;
567 } // switcher directory
568
569 strlcat(fullpath, "/", MAXPATHLEN);
570 strlcat(fullpath, SWITCHER_FILE_NAME, MAXPATHLEN);
571 result = stat(fullpath, &sbuf);
572 isDirectory = S_ISDIR(sbuf.st_mode);
573 if ((result == noErr) && (! isDirectory)) {
574 // Set owner and group of switcher application
575 sprintf(buf1, "root:%s", boinc_master_group_name);
576 // chown root:boinc_master "/Library/Application Support/BOINC Data/switcher/switcher"
577 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
578 if (err)
579 return err;
580
581 // Set permissions of switcher application
582 // chmod u=s,g=rx,o= "/Library/Application Support/BOINC Data/switcher/switcher"
583 // 04050 = S_ISUID | S_IRGRP | S_IXGRP
584 // Set setuid-on-execution plus read and execute permission for group boinc_master only
585 err = DoSudoPosixSpawn(chmodPath, "u=s,g=rx,o=", fullpath, NULL, NULL, NULL, NULL);
586 if (err)
587 return err;
588 } // switcher application
589
590 strlcpy(fullpath, BOINCDataDirPath, MAXPATHLEN);
591 strlcat(fullpath, "/", MAXPATHLEN);
592 strlcat(fullpath, SWITCHER_DIR, MAXPATHLEN);
593 strlcat(fullpath, "/", MAXPATHLEN);
594 strlcat(fullpath, SETPROJECTGRP_FILE_NAME, MAXPATHLEN);
595 result = stat(fullpath, &sbuf);
596 isDirectory = S_ISDIR(sbuf.st_mode);
597 if ((result == noErr) && (! isDirectory)) {
598 // Set owner and group of setprojectgrp application
599 sprintf(buf1, "%s:%s", boinc_master_user_name, boinc_project_group_name);
600 // chown boinc_master:boinc_project "/Library/Application Support/BOINC Data/switcher/setprojectgrp"
601 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
602 if (err)
603 return err;
604
605 // Set permissions of setprojectgrp application
606 // chmod u=rx,g=s,o= "/Library/Application Support/BOINC Data/switcher/setprojectgrp"
607 // 02500 = S_ISGID | S_IRUSR | S_IXUSR
608 // Set setgid-on-execution plus read and execute permission for user only
609 err = DoSudoPosixSpawn(chmodPath, "u=rx,g=s,o=", fullpath, NULL, NULL, NULL, NULL);
610 if (err)
611 return err;
612 } // setprojectgrp application
613
614 #ifdef __APPLE__
615 #if 0 // AppStats is deprecated as of version 5.8.15
616 strlcpy(fullpath, BOINCDataDirPath, MAXPATHLEN);
617 strlcat(fullpath, "/", MAXPATHLEN);
618 strlcat(fullpath, SWITCHER_DIR, MAXPATHLEN);
619 strlcat(fullpath, "/", MAXPATHLEN);
620 strlcat(fullpath, APP_STATS_FILE_NAME, MAXPATHLEN);
621 result = stat(fullpath, &sbuf);
622 isDirectory = S_ISDIR(sbuf.st_mode);
623 if ((result == noErr) && (! isDirectory)) {
624 // Set owner and group of AppStats application (must be setuid root)
625 sprintf(buf1, "root:%s", boinc_master_group_name);
626 // chown root:boinc_project "/Library/Application Support/BOINC Data/switcher/AppStats"
627 err = DoSudoPosixSpawn(chownPath, buf1, fullpath, NULL, NULL, NULL, NULL);
628 if (err)
629 return err;
630
631 // Set permissions of AppStats application
632 // chmod u=rsx,g=rx,o= "/Library/Application Support/BOINC Data/switcher/AppStats"
633 // 04550 = S_ISUID | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP
634 // Set setuid-on-execution plus read and execute permission for user and group
635 err = DoSudoPosixSpawn(chmodPath, "u=rsx,g=rx,o=", fullpath, NULL, NULL, NULL, NULL);
636 if (err)
637 return err;
638 } // setprojectgrp application
639 #endif
640 #endif // __APPLE__
641
642 return noErr;
643 }
644
645
646 // make all *.xml files not world-readable to keep authenticators private
MakeXMLFilesPrivate(char * basepath)647 static OSStatus MakeXMLFilesPrivate(char * basepath) {
648 char fullpath[MAXPATHLEN];
649 OSStatus retval = 0;
650 DIR *dirp;
651 int len;
652 dirent *dp;
653
654 dirp = opendir(basepath);
655 if (dirp == NULL) // Should never happen
656 return -1;
657
658 while (true) {
659 dp = readdir(dirp);
660 if (dp == NULL)
661 break; // End of list
662
663 if (dp->d_name[0] == '.')
664 continue; // Ignore names beginning with '.'
665
666 len = strlen(dp->d_name);
667 if (len < 5)
668 continue;
669
670 if (strcmp(dp->d_name+len-4, ".xml"))
671 continue;
672
673 strlcpy(fullpath, basepath, sizeof(fullpath));
674 strlcat(fullpath, "/", sizeof(fullpath));
675 strlcat(fullpath, dp->d_name, sizeof(fullpath));
676
677 // chmod u+rw,g+rw,o= "/Library/Application Support/BOINC Data/????.xml"
678 // 0660 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP0
679 // Set read and write permission for user and group, no access for others
680 retval = DoSudoPosixSpawn(chmodPath, "u+rw,g+rw,o=", fullpath, NULL, NULL, NULL, NULL);
681 if (retval)
682 break;
683 } // End while (true)
684
685 closedir(dirp);
686
687 return retval;
688 }
689
690
UpdateNestedDirectories(char * basepath)691 static OSStatus UpdateNestedDirectories(char * basepath) {
692 Boolean isDirectory;
693 char fullpath[MAXPATHLEN];
694 struct stat sbuf;
695 OSStatus retval = 0;
696 DIR *dirp;
697 dirent *dp;
698
699 dirp = opendir(basepath);
700 if (dirp == NULL) // Should never happen
701 return -1;
702
703 while (true) {
704 dp = readdir(dirp);
705 if (dp == NULL)
706 break; // End of list
707
708 if (dp->d_name[0] == '.')
709 continue; // Ignore names beginning with '.'
710
711 strlcpy(fullpath, basepath, sizeof(fullpath));
712 strlcat(fullpath, "/", sizeof(fullpath));
713 strlcat(fullpath, dp->d_name, sizeof(fullpath));
714
715 retval = stat(fullpath, &sbuf);
716 if (retval) {
717 if (lstat(fullpath, &sbuf) == 0) {
718 // A broken symlink in a slot directory may be OK if slot is no longer in use
719 if (S_ISLNK(sbuf.st_mode)) {
720 retval = 0;
721 continue;
722 }
723 }
724 break; // Should never happen
725 }
726 isDirectory = S_ISDIR(sbuf.st_mode);
727
728 if (isDirectory) {
729 // chmod u=rwx,g=rwx,o=rx fullpath
730 // 0775 = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH
731 // Set read, write and execute permission for user & group; read and execute permission for others
732 retval = DoSudoPosixSpawn(chmodPath, "u=rwx,g=rwx,o=rx", fullpath, NULL, NULL, NULL, NULL);
733 if (retval)
734 break;
735
736 retval = UpdateNestedDirectories(fullpath);
737 if (retval)
738 break;
739 } else {
740 // Since we are changing ownership from boinc_project to boinc_master,
741 // make sure executable-by-group bit is set if executable-by-owner is set
742 if ((sbuf.st_mode & 0110) == 0100) { // If executable by owner but not by group
743 retval = DoSudoPosixSpawn(chmodPath, "g+x", fullpath, NULL, NULL, NULL, NULL);
744 }
745 }
746
747 } // End while (true)
748
749 closedir(dirp);
750
751 return retval;
752 }
753
754
CreateUserAndGroup(char * user_name,char * group_name)755 static OSStatus CreateUserAndGroup(char * user_name, char * group_name) {
756 OSStatus err = noErr;
757 passwd *pw = NULL;
758 group *grp = NULL;
759 uid_t userid = 0;
760 gid_t groupid = 0;
761 gid_t usergid = 0;
762 Boolean userExists = false;
763 Boolean groupExists = false;
764 short i;
765 static short start_id = MIN_ID;
766 char buf1[80];
767 char buf2[80];
768 char buf3[80];
769 char buf4[80];
770
771 // OS 10.4 has problems with Accounts pane if we create uid or gid > 501
772 pw = getpwnam(user_name);
773 if (pw) {
774 userid = pw->pw_uid;
775 userExists = true;
776 }
777
778 grp = getgrnam(group_name);
779 if (grp) {
780 groupid = grp->gr_gid;
781 groupExists = true;
782 }
783
784 sprintf(buf1, "/groups/%s", group_name);
785 sprintf(buf2, "/users/%s", user_name);
786
787 if ( userExists && groupExists )
788 goto setRealName; // User and group already exist
789
790 // If only user or only group exists, try to use the same ID for the one we create
791 if (userExists) { // User exists but group does not
792 usergid = pw->pw_gid;
793 if (usergid) {
794 grp = getgrgid(usergid);
795 if (grp == NULL) // Set the group ID = users existing group if this group ID is available
796 groupid = usergid;
797 }
798 if (groupid == 0) {
799 grp = getgrgid(userid);
800 if (grp == NULL) // Set the group ID = user ID if this group ID is available
801 groupid = userid;
802 }
803 } else {
804 if (groupExists) { // Group exists but user does not
805 pw = getpwuid(groupid);
806 if (pw == NULL) // Set the user ID = group ID if this user ID is available
807 userid = groupid;
808 }
809 }
810
811 // We need to find an available user ID, group ID, or both. Find a value that is currently
812 // neither a user ID or a group ID.
813 // If we need both a new user ID and a new group ID, finds a value that can be used for both.
814 if ( (userid == 0) || (groupid == 0) ) {
815 for(i=start_id; ; i++) {
816 if ((uid_t)i != userid) {
817 pw = getpwuid((uid_t)i);
818 if (pw)
819 continue; // Already exists as a user ID of a different user
820 }
821
822 if ((gid_t)i != groupid) {
823 grp = getgrgid((gid_t)i);
824 if (grp)
825 continue; // Already exists as a group ID of a different group
826 }
827
828 if (! userExists)
829 userid = (uid_t)i;
830 if (! groupExists)
831 groupid = (gid_t)i;
832
833 start_id = i + 1; // Start with next higher value next time
834
835 break; // Success!
836 }
837 }
838
839 sprintf(buf3, "%d", groupid);
840 sprintf(buf4, "%d", userid);
841
842 if (! groupExists) { // If we need to create group
843 // Something like "dscl . -create /groups/boinc_master"
844 err = DoSudoPosixSpawn(dsclPath, ".", "-create", buf1, NULL, NULL, NULL);
845 if (err)
846 return err;
847
848 // Something like "dscl . -create /groups/boinc_master gid 33"
849 err = DoSudoPosixSpawn(dsclPath, ".", "-create", buf1, "gid", buf3, NULL);
850 if (err)
851 return err;
852 } // if (! groupExists)
853
854 if (! userExists) { // If we need to create user
855 // Something like "dscl . -create /users/boinc_master"
856 err = DoSudoPosixSpawn(dsclPath, ".", "-create", buf2, NULL, NULL, NULL);
857 if (err)
858 return err;
859
860 // Something like "dscl . -create /users/boinc_master uid 33"
861 err = DoSudoPosixSpawn(dsclPath, ".", "-create", buf2, "uid", buf4, NULL);
862 if (err)
863 return err;
864
865 // Prevent a security hole by not allowing a login from this user
866 // Something like "dscl . -create /users/boinc_master shell /usr/bin/false"
867 err = DoSudoPosixSpawn(dsclPath, ".", "-create", buf2, "shell", "/usr/bin/false", NULL);
868 if (err)
869 return err;
870
871 // Something like "dscl . -create /users/boinc_master home /var/empty"
872 err = DoSudoPosixSpawn(dsclPath, ".", "-create", buf2, "home", "/var/empty", NULL);
873 if (err)
874 return err;
875 } // if (! userExists)
876
877 // Always set the user gid if we created either the user or the group or both
878 // Something like "dscl . -create /users/boinc_master gid 33"
879 err = DoSudoPosixSpawn(dsclPath, ".", "-create", buf2, "gid", buf3, NULL);
880 if (err)
881 return err;
882
883 setRealName:
884 // Always set the RealName field to an empty string
885 // Note: create RealName with empty string fails under OS 10.7, but
886 // creating it with non-empty string and changing to empty string does work.
887 //
888 // Something like "dscl . -create /users/boinc_master RealName tempName"
889 err = DoSudoPosixSpawn(dsclPath, ".", "-create", buf2, "RealName", user_name, NULL);
890 if (err)
891 return err;
892
893 // Something like 'dscl . -change /users/boinc_master RealName ""'
894 err = DoSudoPosixSpawn(dsclPath, ".", "-change", buf2, "RealName", user_name, "");
895 if (err)
896 return err;
897
898 err = ResynchDSSystem();
899 if (err != noErr)
900 return err;
901
902 SleepSeconds(2.0);
903
904 return noErr;
905 }
906
907
AddAdminUserToGroups(char * user_name,bool add_to_boinc_project)908 int AddAdminUserToGroups(char *user_name, bool add_to_boinc_project) {
909 #ifndef _DEBUG
910 char buf1[80];
911 OSStatus err = noErr;
912
913 sprintf(buf1, "/groups/%s", boinc_master_group_name);
914
915 // "dscl . -merge /groups/boinc_master users user_name"
916 err = DoSudoPosixSpawn(dsclPath, ".", "-merge", buf1, "users", user_name, NULL);
917 if (err)
918 return err;
919
920 if (add_to_boinc_project) {
921 sprintf(buf1, "/groups/%s", boinc_project_group_name);
922
923 // "dscl . -merge /groups/boinc_project users user_name"
924 err = DoSudoPosixSpawn(dsclPath, ".", "-merge", buf1, "users", user_name, NULL);
925 if (err)
926 return err;
927 }
928
929 err = ResynchDSSystem();
930 if (err != noErr)
931 return err;
932
933 #endif // ! _DEBUG
934 return noErr;
935 }
936
937
ResynchDSSystem()938 OSStatus ResynchDSSystem() {
939 OSStatus err = noErr;
940
941 err = DoSudoPosixSpawn("/usr/bin/dscacheutil", "-flushcache", NULL, NULL, NULL, NULL, NULL);
942 err = DoSudoPosixSpawn("/usr/bin/dsmemberutil", "flushcache", NULL, NULL, NULL, NULL, NULL);
943 return noErr;
944 }
945
946
947 #ifdef _DEBUG
948 // GDB can't attach to applications which are running as a diferent user or group so
949 // it ignores the S_ISUID and S_ISGID permisison bits when launching an application.
950 // To work around this, the _DEBUG version uses the current user and group.
SetFakeMasterNames()951 static OSStatus SetFakeMasterNames() {
952 passwd *pw;
953 group *grp;
954 gid_t boinc_master_gid;
955 uid_t boinc_master_uid;
956
957 boinc_master_uid = geteuid();
958 pw = getpwuid(boinc_master_uid);
959 if (pw == NULL)
960 return -1; // Should never happen
961 strlcpy(boinc_master_user_name, pw->pw_name, sizeof(boinc_master_user_name));
962
963 boinc_master_gid = getegid();
964 grp = getgrgid(boinc_master_gid);
965 if (grp == NULL)
966 return -1;
967 strlcpy(boinc_master_group_name, grp->gr_name, sizeof(boinc_master_group_name));
968
969 #ifndef DEBUG_WITH_FAKE_PROJECT_USER_AND_GROUP
970 // For better debugging of SANDBOX permissions logic
971 strlcpy(boinc_project_user_name, REAL_BOINC_PROJECT_NAME, sizeof(boinc_project_user_name));
972 strlcpy(boinc_project_group_name, REAL_BOINC_PROJECT_NAME, sizeof(boinc_project_group_name));
973 #else
974 // For easier debugging of project applications
975 strlcpy(boinc_project_user_name, pw->pw_name, sizeof(boinc_project_user_name));
976 strlcpy(boinc_project_group_name, grp->gr_name, sizeof(boinc_project_group_name));
977 #endif
978
979 return noErr;
980 }
981 #endif
982
983
DoSudoPosixSpawn(const char * pathToTool,char * arg1,char * arg2,char * arg3,char * arg4,char * arg5,char * arg6)984 static OSStatus DoSudoPosixSpawn(const char *pathToTool, char *arg1, char *arg2, char *arg3, char *arg4, char *arg5, char *arg6) {
985 short i;
986 char *args[9];
987 char toolName[1024];
988 pid_t thePid = 0;
989 int result = 0;
990 int status = 0;
991 extern char **environ;
992
993 for (i=0; i<5; i++) { // Retry 5 times if error (is that still necessary?)
994 strlcpy(toolName, pathToTool, sizeof(toolName));
995 args[0] = "/usr/bin/sudo";
996 args[1] = toolName;
997 args[2] = arg1;
998 args[3] = arg2;
999 args[4] = arg3;
1000 args[5] = arg4;
1001 args[6] = arg5;
1002 args[7] = arg6;
1003 args[8] = NULL;
1004
1005 #if VERBOSE_TEST
1006 print_to_log_file("***********");
1007 for (int i=0; i<8; ++i) {
1008 if (args[i] == NULL) break;
1009 print_to_log_file("argv[%d]=%s", i, args[i]);
1010 }
1011 print_to_log_file("***********\n");
1012 #endif
1013
1014 errno = 0;
1015
1016 result = posix_spawnp(&thePid, "/usr/bin/sudo", NULL, NULL, args, environ);
1017 #if VERBOSE_TEST
1018 print_to_log_file("callPosixSpawn: posix_spawnp returned %d: %s", result, strerror(result));
1019 #endif
1020 if (result) {
1021 return result;
1022 }
1023 // CAF int val =
1024 waitpid(thePid, &status, WUNTRACED);
1025 // CAF if (val < 0) printf("first waitpid returned %d\n", val);
1026 if (status != 0) {
1027 #if VERBOSE_TEST
1028 print_to_log_file("waitpid() returned status=%d", status);
1029 #endif
1030 result = status;
1031 } else {
1032 if (WIFEXITED(status)) {
1033 result = WEXITSTATUS(status);
1034 if (result == 1) {
1035 #if VERBOSE_TEST
1036 print_to_log_file("WEXITSTATUS(status) returned 1, errno=%d: %s", errno, strerror(errno));
1037 #endif
1038 result = errno;
1039 }
1040 #if VERBOSE_TEST
1041 else if (result) {
1042 print_to_log_file("WEXITSTATUS(status) returned %d", result);
1043 }
1044 #endif
1045 } // end if (WIFEXITED(status)) else
1046 } // end if waitpid returned 0 sstaus else
1047
1048 #if 0
1049 if (strcmp(arg2, "-R") == 0)
1050 SleepSeconds(DELAY_SECONDS_R);
1051 else
1052 SleepSeconds(DELAY_SECONDS);
1053 #endif
1054 if (result == 0)
1055 break;
1056 }
1057 if (result != 0)
1058 ShowSecurityError("\"%s %s %s %s %s %s\" returned error %d", pathToTool,
1059 arg1 ? arg1 : "", arg2 ? arg2 : "", arg3 ? arg3 : "",
1060 arg4 ? arg4 : "", arg5 ? arg5 : "", result);
1061
1062 return result;
1063 }
1064
1065
1066
ShowSecurityError(const char * format,...)1067 void ShowSecurityError(const char *format, ...) {
1068 va_list args;
1069
1070 #ifdef __x86_64__
1071 va_start(args, format);
1072 vfprintf(stderr, format, args);
1073 va_end(args);
1074 #else
1075 char s[1024];
1076 short itemHit;
1077 AlertStdAlertParamRec alertParams;
1078 ModalFilterUPP ErrorDlgFilterProcUPP;
1079
1080 va_start(args, format);
1081 s[0] = vsprintf(s+1, format, args);
1082 va_end(args);
1083
1084 ErrorDlgFilterProcUPP = NewModalFilterUPP(ErrorDlgFilterProc);
1085
1086 alertParams.movable = true;
1087 alertParams.helpButton = false;
1088 alertParams.filterProc = ErrorDlgFilterProcUPP;
1089 alertParams.defaultText = "\pOK";
1090 alertParams.cancelText = NULL;
1091 alertParams.otherText = NULL;
1092 alertParams.defaultButton = kAlertStdAlertOKButton;
1093 alertParams.cancelButton = 0;
1094 alertParams.position = kWindowDefaultPosition;
1095
1096 BringAppToFront();
1097
1098 StandardAlert (kAlertStopAlert, (StringPtr)s, NULL, &alertParams, &itemHit);
1099
1100 DisposeModalFilterUPP(ErrorDlgFilterProcUPP);
1101 #endif
1102 }
1103
1104
1105 #ifndef __x86_64__
ErrorDlgFilterProc(DialogPtr theDialog,EventRecord * theEvent,short * theItemHit)1106 static pascal Boolean ErrorDlgFilterProc(DialogPtr theDialog, EventRecord *theEvent, short *theItemHit) {
1107 // We need this because this is a command-line application so it does not get normal events
1108 if (GetCurrentEventButtonState()) {
1109 *theItemHit = kStdOkItemIndex;
1110 return true;
1111 }
1112
1113 return StdFilterProc(theDialog, theEvent, theItemHit);
1114 }
1115 #endif
1116
1117
1118 // return time of day (seconds since 1970) as a double
1119 //
dtime(void)1120 static double dtime(void) {
1121 struct timeval tv;
1122 gettimeofday(&tv, 0);
1123 return tv.tv_sec + (tv.tv_usec/1.e6);
1124 }
1125
1126 // Uses usleep to sleep for full duration even if a signal is received
SleepSeconds(double seconds)1127 static void SleepSeconds(double seconds) {
1128 double end_time = dtime() + seconds - 0.01;
1129 // sleep() and usleep() can be interrupted by SIGALRM,
1130 // so we may need multiple calls
1131 //
1132 while (1) {
1133 if (seconds >= 1) {
1134 sleep((unsigned int) seconds);
1135 } else {
1136 usleep((int)fmod(seconds*1000000, 1000000));
1137 }
1138 seconds = end_time - dtime();
1139 if (seconds <= 0) break;
1140 }
1141 }
1142