]>
Commit | Line | Data |
---|---|---|
1815bff5 A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
2fc1e207 A |
6 | * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.0 (the 'License'). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
1815bff5 A |
13 | * |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2fc1e207 A |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License." | |
1815bff5 A |
21 | * |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
ef8ad44b | 24 | #include <signal.h> |
1815bff5 | 25 | #include <stdio.h> |
20e66415 | 26 | #include <string.h> |
1815bff5 A |
27 | #include <stdlib.h> |
28 | #include <unistd.h> | |
29 | #include <pwd.h> | |
30 | #include <errno.h> | |
1376a029 A |
31 | #include <fcntl.h> |
32 | #include <sysexits.h> | |
33 | #include <sys/time.h> | |
34 | #include <sys/resource.h> | |
35 | #include <sys/stat.h> | |
36 | #include <sys/wait.h> | |
1815bff5 A |
37 | #include "stringops.h" |
38 | ||
1376a029 A |
39 | #define TEMP_FILE_TEMPLATE "/var/run/.pwtmpXXXXXX" |
40 | #define LOCK_FILE "/var/run/.passwd.lock" | |
1815bff5 A |
41 | |
42 | #define _PASSWD_FILE "/etc/master.passwd" | |
43 | #define _COMPAT_FILE "/etc/passwd" | |
44 | #define _PASSWD_FIELDS 10 | |
45 | #define BUFSIZE 8192 | |
46 | ||
47 | extern void getpasswd(char *, int, int, int, int, char *, char **, char**, char **); | |
48 | ||
2fc1e207 | 49 | //static int do_compat = 1; (unused) |
1815bff5 A |
50 | |
51 | char * | |
52 | getline(FILE *fp) | |
53 | { | |
54 | static char s[BUFSIZE]; | |
55 | int len; | |
56 | ||
57 | s[0] = '\0'; | |
58 | ||
59 | fgets(s, BUFSIZE, fp); | |
60 | if (s == NULL || s[0] == '\0') return NULL; | |
61 | ||
62 | if (s[0] == '#') return s; | |
63 | ||
64 | len = strlen(s) - 1; | |
65 | s[len] = '\0'; | |
66 | ||
67 | return s; | |
68 | } | |
69 | ||
70 | struct passwd * | |
71 | parse_user(char *line) | |
72 | { | |
73 | static struct passwd pw = {0}; | |
74 | char **tokens; | |
75 | int i, len; | |
76 | ||
77 | if (pw.pw_name != NULL) free(pw.pw_name); | |
78 | pw.pw_name = NULL; | |
79 | if (pw.pw_passwd != NULL) free(pw.pw_passwd); | |
80 | pw.pw_passwd = NULL; | |
81 | if (pw.pw_gecos != NULL) free(pw.pw_gecos); | |
82 | pw.pw_gecos = NULL; | |
83 | if (pw.pw_dir != NULL) free(pw.pw_dir); | |
84 | pw.pw_dir = NULL; | |
85 | if (pw.pw_shell != NULL) free(pw.pw_shell); | |
86 | pw.pw_shell = NULL; | |
87 | ||
88 | if (pw.pw_class != NULL) free(pw.pw_class); | |
89 | pw.pw_class = NULL; | |
90 | ||
91 | if (line == NULL) return (struct passwd *)NULL; | |
92 | tokens = explode(line, ':'); | |
93 | len = listLength(tokens); | |
94 | ||
95 | if (len != _PASSWD_FIELDS) | |
96 | { | |
97 | freeList(tokens); | |
98 | return (struct passwd *)NULL; | |
99 | } | |
100 | ||
101 | i = 0; | |
102 | pw.pw_name = tokens[i++]; | |
103 | pw.pw_passwd = tokens[i++]; | |
104 | pw.pw_uid = atoi(tokens[i]); | |
105 | free(tokens[i++]); | |
106 | pw.pw_gid = atoi(tokens[i]); | |
107 | free(tokens[i++]); | |
108 | pw.pw_class = tokens[i++]; | |
109 | pw.pw_change = atoi(tokens[i]); | |
110 | free(tokens[i++]); | |
111 | pw.pw_expire = atoi(tokens[i]); | |
112 | free(tokens[i++]); | |
113 | pw.pw_gecos = tokens[i++]; | |
114 | pw.pw_dir = tokens[i++]; | |
115 | pw.pw_shell = tokens[i++]; | |
116 | ||
117 | return &pw; | |
118 | } | |
119 | ||
120 | struct passwd * | |
121 | find_user(char *uname, FILE *fp) | |
122 | { | |
123 | char *line; | |
124 | struct passwd *pw; | |
125 | ||
126 | rewind(fp); | |
127 | ||
128 | while (NULL != (line = getline(fp))) | |
129 | { | |
130 | if (line[0] == '#') continue; | |
131 | pw = parse_user(line); | |
132 | if (pw == (struct passwd *)NULL) continue; | |
133 | if (!strcmp(uname, pw->pw_name)) return pw; | |
134 | } | |
135 | ||
136 | pw = parse_user(NULL); | |
137 | return (struct passwd *)NULL; | |
138 | } | |
139 | ||
140 | void | |
1376a029 | 141 | rewrite_file(char *pwname, FILE *fp, struct passwd *newpw, char *locn) |
1815bff5 A |
142 | { |
143 | char *line; | |
144 | struct passwd *pw; | |
145 | FILE *tfp, *cfp; | |
1376a029 | 146 | int fd; |
1815bff5 A |
147 | char fname[256]; |
148 | ||
1376a029 A |
149 | sprintf(fname, "%s.%.5d", TEMP_FILE_TEMPLATE, getpid()); |
150 | fd = mkstemps(fname, 6); | |
151 | if (fd == -1) | |
152 | { | |
153 | fprintf(stderr, "can't create temporary file \"%s\": ", fname); | |
154 | perror(""); | |
155 | exit(1); | |
156 | } | |
157 | if (fchmod(fd, (S_IRUSR | S_IWUSR)) != 0) | |
158 | { | |
159 | close(fd); | |
160 | unlink(fname); | |
161 | fprintf(stderr, "can't set permissions for temporary file \"%s\": ", fname); | |
162 | perror(""); | |
163 | exit(1); | |
164 | } | |
165 | tfp = fdopen(fd, "w+"); | |
1815bff5 A |
166 | if (tfp == NULL) |
167 | { | |
1376a029 A |
168 | close(fd); |
169 | unlink(fname); | |
1815bff5 A |
170 | fprintf(stderr, "can't write temporary file \"%s\": ", fname); |
171 | perror(""); | |
172 | exit(1); | |
173 | } | |
1376a029 | 174 | |
1815bff5 A |
175 | cfp = NULL; |
176 | if (!strcmp(pwname, _PASSWD_FILE)) | |
177 | { | |
178 | cfp = fopen(_COMPAT_FILE, "w"); | |
179 | if (cfp == NULL) | |
180 | { | |
181 | fprintf(stderr, "warning: can't write compatability file \"%s\": ", | |
182 | _COMPAT_FILE); | |
183 | perror(""); | |
184 | } | |
185 | } | |
186 | ||
187 | if (cfp != NULL) | |
188 | { | |
189 | fprintf(cfp, "#\n"); | |
190 | fprintf(cfp, "# 4.3BSD-compatable User Database\n"); | |
191 | fprintf(cfp, "#\n"); | |
192 | fprintf(cfp, "# Note that this file is not consulted for login.\n"); | |
193 | fprintf(cfp, "# It only exisits for compatability with 4.3BSD utilities.\n"); | |
194 | fprintf(cfp, "#\n"); | |
195 | fprintf(cfp, "# This file is automatically re-written by various system utilities.\n"); | |
196 | fprintf(cfp, "# Do not edit this file. Changes will be lost.\n"); | |
197 | fprintf(cfp, "#\n"); | |
198 | } | |
1376a029 | 199 | |
1815bff5 A |
200 | rewind(fp); |
201 | ||
202 | while (NULL != (line = getline(fp))) | |
203 | { | |
204 | if (line[0] == '#') | |
205 | { | |
206 | fprintf(tfp, "%s", line); | |
207 | continue; | |
208 | } | |
209 | ||
210 | pw = parse_user(line); | |
211 | if (pw == (struct passwd *)NULL) | |
212 | { | |
213 | fprintf(stderr, "warning: bad format for entry: \"%s\"\n", line); | |
214 | fprintf(tfp, "%s\n", line); | |
215 | if (cfp != NULL) fprintf(cfp, "%s\n", line); | |
216 | continue; | |
217 | } | |
218 | ||
219 | if (strcmp(newpw->pw_name, pw->pw_name)) | |
220 | { | |
221 | fprintf(tfp, "%s\n", line); | |
222 | if (cfp != NULL) fprintf(cfp, "%s\n", line); | |
223 | continue; | |
224 | } | |
2fc1e207 A |
225 | |
226 | fprintf(tfp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", | |
1815bff5 A |
227 | newpw->pw_name, newpw->pw_passwd, newpw->pw_uid, newpw->pw_gid, |
228 | newpw->pw_class, newpw->pw_change, newpw->pw_expire, | |
229 | newpw->pw_gecos, newpw->pw_dir, newpw->pw_shell); | |
230 | if (cfp != NULL) | |
231 | { | |
232 | fprintf(cfp, "%s:",newpw->pw_name); | |
233 | if ((newpw->pw_passwd == NULL) || (newpw->pw_passwd[0] == '\0')) | |
234 | fprintf(cfp, ":"); | |
235 | else | |
236 | fprintf(cfp, "*:"); | |
237 | fprintf(cfp, "%d:%d:%s:%s:%s\n", | |
238 | newpw->pw_uid, newpw->pw_gid, newpw->pw_gecos, | |
239 | newpw->pw_dir, newpw->pw_shell); | |
240 | } | |
241 | } | |
242 | ||
243 | if (cfp != NULL) fclose(cfp); | |
244 | fclose(fp); | |
1376a029 | 245 | |
1815bff5 A |
246 | rewind(tfp); |
247 | ||
1376a029 A |
248 | if (locn != NULL) { |
249 | if (seteuid(getuid()) != 0) { | |
250 | fprintf(stderr, "Unable to set privileges."); | |
251 | perror("seteuid"); | |
252 | exit(1); | |
253 | } | |
254 | } | |
1815bff5 A |
255 | fp = fopen(pwname, "w"); |
256 | if (fp == NULL) | |
257 | { | |
1376a029 | 258 | fprintf(stderr, "ERROR: can't update \"%s\"\n", pwname); |
1815bff5 A |
259 | fprintf(stderr, "new passwd file is \"%s\"\n", fname); |
260 | perror("open"); | |
261 | exit(1); | |
262 | } | |
1376a029 | 263 | |
1815bff5 A |
264 | while (NULL != (line = getline(tfp))) |
265 | { | |
266 | fprintf(fp, "%s", line); | |
267 | if (line[0] != '#') fprintf(fp, "\n"); | |
268 | } | |
269 | fclose(fp); | |
270 | fclose(tfp); | |
271 | unlink(fname); | |
272 | } | |
273 | ||
274 | int | |
1376a029 | 275 | _file_passwd_main(char *uname, char *locn) |
1815bff5 A |
276 | { |
277 | char *ne, *oc, *nc; | |
278 | FILE *fp; | |
279 | char *fname; | |
280 | struct passwd *pw; | |
281 | struct passwd newpw; | |
1376a029 | 282 | struct stat sb; |
1815bff5 | 283 | int uid; |
1376a029 A |
284 | uid_t euid; |
285 | ||
1815bff5 A |
286 | fname = _PASSWD_FILE; |
287 | if (locn != NULL) fname = locn; | |
1376a029 A |
288 | |
289 | umask((S_IRWXG | S_IRWXO)); | |
290 | ||
291 | if ( lstat(fname, &sb) != 0 ) | |
292 | { | |
293 | fprintf(stderr, "The file does not exist.\n"); | |
294 | exit(1); | |
295 | } | |
296 | ||
297 | euid = geteuid(); | |
298 | if (locn != NULL) { | |
299 | if (seteuid(getuid()) != 0) { | |
300 | fprintf(stderr, "Permission denied.\n"); | |
301 | exit(1); | |
302 | } | |
303 | } | |
1815bff5 | 304 | fp = fopen(fname, "a+"); |
1376a029 A |
305 | if (locn != NULL) { |
306 | seteuid(euid); | |
307 | } | |
308 | ||
1815bff5 A |
309 | if (fp == NULL) |
310 | { | |
311 | fprintf(stderr, "can't write to file \"%s\": ", fname); | |
312 | perror(""); | |
313 | exit(1); | |
314 | } | |
1376a029 A |
315 | if (fchmod(fileno(fp), (S_IRUSR | S_IWUSR)) != 0) |
316 | { | |
317 | fclose(fp); | |
318 | fprintf(stderr, "can't set permissions for file \"%s\": ", fname); | |
319 | perror(""); | |
320 | exit(1); | |
321 | } | |
322 | ||
1815bff5 A |
323 | pw = find_user(uname, fp); |
324 | if (pw == (struct passwd *)NULL) | |
325 | { | |
326 | fprintf(stderr, "user %s not found in file %s\n", uname, fname); | |
327 | exit(1); | |
328 | } | |
329 | ||
330 | uid = getuid(); | |
331 | if ((uid != 0) && (uid != pw->pw_uid)) | |
332 | { | |
333 | fprintf(stderr, "Permission denied\n"); | |
334 | exit(1); | |
335 | } | |
336 | ||
337 | /* | |
338 | * Get the new password | |
339 | */ | |
340 | getpasswd(uname, (uid == 0), 5, 0, 0, pw->pw_passwd, &ne, &oc, &nc); | |
341 | ||
342 | newpw.pw_name = copyString(pw->pw_name); | |
343 | newpw.pw_passwd = copyString(ne); | |
344 | newpw.pw_uid = pw->pw_uid; | |
345 | newpw.pw_gid = pw->pw_gid; | |
346 | newpw.pw_class = copyString(pw->pw_class); | |
347 | newpw.pw_change = pw->pw_change; | |
348 | newpw.pw_expire = pw->pw_expire; | |
349 | newpw.pw_gecos = copyString(pw->pw_gecos); | |
350 | newpw.pw_dir = copyString(pw->pw_dir); | |
351 | newpw.pw_shell = copyString(pw->pw_shell); | |
352 | ||
353 | /* | |
354 | * Re-write the file | |
355 | */ | |
1376a029 | 356 | rewrite_file(fname, fp, &newpw, locn); |
1815bff5 A |
357 | |
358 | /* | |
359 | * Clean up memory | |
360 | */ | |
361 | pw = parse_user(NULL); | |
362 | free(newpw.pw_name); | |
363 | free(newpw.pw_passwd); | |
364 | free(newpw.pw_gecos); | |
365 | free(newpw.pw_dir); | |
366 | free(newpw.pw_shell); | |
367 | free(newpw.pw_class); | |
368 | ||
369 | fclose(fp); | |
370 | ||
371 | return 0; | |
372 | } | |
1376a029 A |
373 | |
374 | ||
375 | void sighandler(int inSignal) | |
376 | { | |
377 | unlink(LOCK_FILE); | |
378 | exit(1); | |
379 | } | |
380 | ||
381 | ||
382 | int | |
383 | file_passwd(char *uname, char *locn) | |
384 | { | |
385 | pid_t pid; | |
386 | int retVal = 0; | |
387 | int waitResult = 0; | |
388 | int retries = 0; | |
389 | struct stat sb; | |
390 | FILE *lockFile; | |
ef8ad44b | 391 | struct sigaction action = {{0}}; |
1376a029 A |
392 | struct rlimit rlim; |
393 | ||
394 | /* unlimit the resource limits */ | |
395 | rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; | |
396 | (void)setrlimit(RLIMIT_CPU, &rlim); | |
397 | (void)setrlimit(RLIMIT_FSIZE, &rlim); | |
398 | (void)setrlimit(RLIMIT_STACK, &rlim); | |
399 | (void)setrlimit(RLIMIT_DATA, &rlim); | |
400 | (void)setrlimit(RLIMIT_RSS, &rlim); | |
401 | (void)setrlimit(RLIMIT_NOFILE, &rlim); | |
402 | ||
403 | /* trap signals */ | |
404 | sigfillset( &action.sa_mask ); | |
405 | action.sa_flags = SA_RESTART; | |
406 | action.sa_handler = sighandler; | |
407 | sigaction(SIGHUP, &action, NULL); | |
408 | sigaction(SIGINT, &action, NULL); // ctrl-c | |
409 | sigaction(SIGQUIT, &action, NULL); | |
410 | sigaction(SIGABRT, &action, NULL); | |
411 | sigaction(SIGPIPE, &action, NULL); | |
412 | sigaction(SIGALRM, &action, NULL); | |
413 | sigaction(SIGTERM, &action, NULL); | |
414 | sigaction(SIGSTOP, &action, NULL); | |
415 | sigaction(SIGTSTP, &action, NULL); | |
416 | ||
417 | /* Check/create lock file */ | |
418 | for (retries = 0; retries < 5; retries++) | |
419 | { | |
420 | retVal = lstat(LOCK_FILE, &sb); | |
421 | if (retVal != 0) | |
422 | break; | |
423 | /* try in 100 milliseconds */ | |
424 | usleep(100000); | |
425 | } | |
426 | if (retVal == 0) | |
427 | { | |
428 | fprintf(stderr, "another passwd process is running.\n"); | |
429 | exit(EX_TEMPFAIL); | |
430 | } | |
431 | ||
432 | umask((S_IRWXG | S_IRWXO)); | |
433 | lockFile = fopen(LOCK_FILE, "w"); | |
434 | if (lockFile == NULL) | |
435 | { | |
436 | fprintf(stderr, "can't create lock file.\n"); | |
437 | exit(EX_CANTCREAT); | |
438 | } | |
439 | fprintf(lockFile, "%d\n", getpid()); | |
440 | fclose(lockFile); | |
441 | ||
442 | pid = fork(); | |
443 | if (pid == -1) | |
444 | { | |
445 | fprintf(stderr, "can't fork\n"); | |
446 | exit(EX_OSERR); | |
447 | } | |
448 | ||
449 | /* Handle the child */ | |
450 | if (pid == 0) | |
451 | { | |
452 | retVal = _file_passwd_main(uname, locn); | |
453 | exit(retVal); | |
454 | } | |
455 | ||
456 | /* Handle the parent */ | |
457 | waitResult = waitpid(pid, &retVal, 0); | |
458 | retVal = (waitResult == 0) ? WEXITSTATUS(retVal) : 1; | |
459 | ||
460 | /* delete lock file */ | |
461 | unlink(LOCK_FILE); | |
462 | ||
463 | return retVal; | |
464 | } | |
465 |