]>
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 | */ | |
24 | #include <stdio.h> | |
20e66415 | 25 | #include <string.h> |
1815bff5 A |
26 | #include <stdlib.h> |
27 | #include <unistd.h> | |
28 | #include <pwd.h> | |
29 | #include <errno.h> | |
d1ccfde8 A |
30 | #include <fcntl.h> |
31 | #include <sysexits.h> | |
32 | #include <sys/stat.h> | |
1815bff5 A |
33 | #include "stringops.h" |
34 | ||
d1ccfde8 A |
35 | #define TEMP_FILE_TEMPLATE "/var/run/.pwtmpXXXXXX" |
36 | #define LOCK_FILE "/var/run/.passwd.lock" | |
1815bff5 A |
37 | |
38 | #define _PASSWD_FILE "/etc/master.passwd" | |
39 | #define _COMPAT_FILE "/etc/passwd" | |
40 | #define _PASSWD_FIELDS 10 | |
41 | #define BUFSIZE 8192 | |
42 | ||
43 | extern void getpasswd(char *, int, int, int, int, char *, char **, char**, char **); | |
44 | ||
2fc1e207 | 45 | //static int do_compat = 1; (unused) |
1815bff5 A |
46 | |
47 | char * | |
48 | getline(FILE *fp) | |
49 | { | |
50 | static char s[BUFSIZE]; | |
51 | int len; | |
52 | ||
53 | s[0] = '\0'; | |
54 | ||
55 | fgets(s, BUFSIZE, fp); | |
56 | if (s == NULL || s[0] == '\0') return NULL; | |
57 | ||
58 | if (s[0] == '#') return s; | |
59 | ||
60 | len = strlen(s) - 1; | |
61 | s[len] = '\0'; | |
62 | ||
63 | return s; | |
64 | } | |
65 | ||
66 | struct passwd * | |
67 | parse_user(char *line) | |
68 | { | |
69 | static struct passwd pw = {0}; | |
70 | char **tokens; | |
71 | int i, len; | |
72 | ||
73 | if (pw.pw_name != NULL) free(pw.pw_name); | |
74 | pw.pw_name = NULL; | |
75 | if (pw.pw_passwd != NULL) free(pw.pw_passwd); | |
76 | pw.pw_passwd = NULL; | |
77 | if (pw.pw_gecos != NULL) free(pw.pw_gecos); | |
78 | pw.pw_gecos = NULL; | |
79 | if (pw.pw_dir != NULL) free(pw.pw_dir); | |
80 | pw.pw_dir = NULL; | |
81 | if (pw.pw_shell != NULL) free(pw.pw_shell); | |
82 | pw.pw_shell = NULL; | |
83 | ||
84 | if (pw.pw_class != NULL) free(pw.pw_class); | |
85 | pw.pw_class = NULL; | |
86 | ||
87 | if (line == NULL) return (struct passwd *)NULL; | |
88 | tokens = explode(line, ':'); | |
89 | len = listLength(tokens); | |
90 | ||
91 | if (len != _PASSWD_FIELDS) | |
92 | { | |
93 | freeList(tokens); | |
94 | return (struct passwd *)NULL; | |
95 | } | |
96 | ||
97 | i = 0; | |
98 | pw.pw_name = tokens[i++]; | |
99 | pw.pw_passwd = tokens[i++]; | |
100 | pw.pw_uid = atoi(tokens[i]); | |
101 | free(tokens[i++]); | |
102 | pw.pw_gid = atoi(tokens[i]); | |
103 | free(tokens[i++]); | |
104 | pw.pw_class = tokens[i++]; | |
105 | pw.pw_change = atoi(tokens[i]); | |
106 | free(tokens[i++]); | |
107 | pw.pw_expire = atoi(tokens[i]); | |
108 | free(tokens[i++]); | |
109 | pw.pw_gecos = tokens[i++]; | |
110 | pw.pw_dir = tokens[i++]; | |
111 | pw.pw_shell = tokens[i++]; | |
112 | ||
113 | return &pw; | |
114 | } | |
115 | ||
116 | struct passwd * | |
117 | find_user(char *uname, FILE *fp) | |
118 | { | |
119 | char *line; | |
120 | struct passwd *pw; | |
121 | ||
122 | rewind(fp); | |
123 | ||
124 | while (NULL != (line = getline(fp))) | |
125 | { | |
126 | if (line[0] == '#') continue; | |
127 | pw = parse_user(line); | |
128 | if (pw == (struct passwd *)NULL) continue; | |
129 | if (!strcmp(uname, pw->pw_name)) return pw; | |
130 | } | |
131 | ||
132 | pw = parse_user(NULL); | |
133 | return (struct passwd *)NULL; | |
134 | } | |
135 | ||
136 | void | |
d1ccfde8 | 137 | rewrite_file(char *pwname, FILE *fp, struct passwd *newpw, char *locn) |
1815bff5 A |
138 | { |
139 | char *line; | |
140 | struct passwd *pw; | |
141 | FILE *tfp, *cfp; | |
d1ccfde8 | 142 | int fd; |
1815bff5 A |
143 | char fname[256]; |
144 | ||
d1ccfde8 A |
145 | sprintf(fname, "%s.%.5d", TEMP_FILE_TEMPLATE, getpid()); |
146 | fd = mkstemps(fname, 6); | |
147 | if (fd == -1) | |
148 | { | |
149 | fprintf(stderr, "can't create temporary file \"%s\": ", fname); | |
150 | perror(""); | |
151 | exit(1); | |
152 | } | |
153 | if (fchmod(fd, (S_IRUSR | S_IWUSR)) != 0) | |
154 | { | |
155 | close(fd); | |
156 | unlink(fname); | |
157 | fprintf(stderr, "can't set permissions for temporary file \"%s\": ", fname); | |
158 | perror(""); | |
159 | exit(1); | |
160 | } | |
161 | tfp = fdopen(fd, "w+"); | |
1815bff5 A |
162 | if (tfp == NULL) |
163 | { | |
d1ccfde8 A |
164 | close(fd); |
165 | unlink(fname); | |
1815bff5 A |
166 | fprintf(stderr, "can't write temporary file \"%s\": ", fname); |
167 | perror(""); | |
168 | exit(1); | |
169 | } | |
d1ccfde8 | 170 | |
1815bff5 A |
171 | cfp = NULL; |
172 | if (!strcmp(pwname, _PASSWD_FILE)) | |
173 | { | |
174 | cfp = fopen(_COMPAT_FILE, "w"); | |
175 | if (cfp == NULL) | |
176 | { | |
177 | fprintf(stderr, "warning: can't write compatability file \"%s\": ", | |
178 | _COMPAT_FILE); | |
179 | perror(""); | |
180 | } | |
181 | } | |
182 | ||
183 | if (cfp != NULL) | |
184 | { | |
185 | fprintf(cfp, "#\n"); | |
186 | fprintf(cfp, "# 4.3BSD-compatable User Database\n"); | |
187 | fprintf(cfp, "#\n"); | |
188 | fprintf(cfp, "# Note that this file is not consulted for login.\n"); | |
189 | fprintf(cfp, "# It only exisits for compatability with 4.3BSD utilities.\n"); | |
190 | fprintf(cfp, "#\n"); | |
191 | fprintf(cfp, "# This file is automatically re-written by various system utilities.\n"); | |
192 | fprintf(cfp, "# Do not edit this file. Changes will be lost.\n"); | |
193 | fprintf(cfp, "#\n"); | |
194 | } | |
d1ccfde8 | 195 | |
1815bff5 A |
196 | rewind(fp); |
197 | ||
198 | while (NULL != (line = getline(fp))) | |
199 | { | |
200 | if (line[0] == '#') | |
201 | { | |
202 | fprintf(tfp, "%s", line); | |
203 | continue; | |
204 | } | |
205 | ||
206 | pw = parse_user(line); | |
207 | if (pw == (struct passwd *)NULL) | |
208 | { | |
209 | fprintf(stderr, "warning: bad format for entry: \"%s\"\n", line); | |
210 | fprintf(tfp, "%s\n", line); | |
211 | if (cfp != NULL) fprintf(cfp, "%s\n", line); | |
212 | continue; | |
213 | } | |
214 | ||
215 | if (strcmp(newpw->pw_name, pw->pw_name)) | |
216 | { | |
217 | fprintf(tfp, "%s\n", line); | |
218 | if (cfp != NULL) fprintf(cfp, "%s\n", line); | |
219 | continue; | |
220 | } | |
2fc1e207 A |
221 | |
222 | fprintf(tfp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", | |
1815bff5 A |
223 | newpw->pw_name, newpw->pw_passwd, newpw->pw_uid, newpw->pw_gid, |
224 | newpw->pw_class, newpw->pw_change, newpw->pw_expire, | |
225 | newpw->pw_gecos, newpw->pw_dir, newpw->pw_shell); | |
226 | if (cfp != NULL) | |
227 | { | |
228 | fprintf(cfp, "%s:",newpw->pw_name); | |
229 | if ((newpw->pw_passwd == NULL) || (newpw->pw_passwd[0] == '\0')) | |
230 | fprintf(cfp, ":"); | |
231 | else | |
232 | fprintf(cfp, "*:"); | |
233 | fprintf(cfp, "%d:%d:%s:%s:%s\n", | |
234 | newpw->pw_uid, newpw->pw_gid, newpw->pw_gecos, | |
235 | newpw->pw_dir, newpw->pw_shell); | |
236 | } | |
237 | } | |
238 | ||
239 | if (cfp != NULL) fclose(cfp); | |
240 | fclose(fp); | |
d1ccfde8 | 241 | |
1815bff5 A |
242 | rewind(tfp); |
243 | ||
d1ccfde8 A |
244 | if (locn != NULL) { |
245 | if (setuid(getuid()) != 0) { | |
246 | fprintf(stderr, "Unable to set privileges."); | |
247 | perror("setuid"); | |
248 | exit(1); | |
249 | } | |
250 | } | |
1815bff5 A |
251 | fp = fopen(pwname, "w"); |
252 | if (fp == NULL) | |
253 | { | |
d1ccfde8 | 254 | fprintf(stderr, "ERROR: can't update \"%s\"\n", pwname); |
1815bff5 A |
255 | fprintf(stderr, "new passwd file is \"%s\"\n", fname); |
256 | perror("open"); | |
257 | exit(1); | |
258 | } | |
d1ccfde8 | 259 | |
1815bff5 A |
260 | while (NULL != (line = getline(tfp))) |
261 | { | |
262 | fprintf(fp, "%s", line); | |
263 | if (line[0] != '#') fprintf(fp, "\n"); | |
264 | } | |
265 | fclose(fp); | |
266 | fclose(tfp); | |
267 | unlink(fname); | |
268 | } | |
269 | ||
270 | int | |
d1ccfde8 | 271 | _file_passwd_main(char *uname, char *locn) |
1815bff5 A |
272 | { |
273 | char *ne, *oc, *nc; | |
274 | FILE *fp; | |
275 | char *fname; | |
276 | struct passwd *pw; | |
277 | struct passwd newpw; | |
278 | int uid; | |
279 | ||
280 | fname = _PASSWD_FILE; | |
281 | if (locn != NULL) fname = locn; | |
d1ccfde8 A |
282 | |
283 | umask((S_IRWXG | S_IRWXO)); | |
1815bff5 A |
284 | fp = fopen(fname, "a+"); |
285 | if (fp == NULL) | |
286 | { | |
287 | fprintf(stderr, "can't write to file \"%s\": ", fname); | |
288 | perror(""); | |
289 | exit(1); | |
290 | } | |
d1ccfde8 A |
291 | if (fchmod(fileno(fp), (S_IRUSR | S_IWUSR)) != 0) |
292 | { | |
293 | fclose(fp); | |
294 | fprintf(stderr, "can't set permissions for file \"%s\": ", fname); | |
295 | perror(""); | |
296 | exit(1); | |
297 | } | |
298 | ||
1815bff5 A |
299 | pw = find_user(uname, fp); |
300 | if (pw == (struct passwd *)NULL) | |
301 | { | |
302 | fprintf(stderr, "user %s not found in file %s\n", uname, fname); | |
303 | exit(1); | |
304 | } | |
305 | ||
306 | uid = getuid(); | |
307 | if ((uid != 0) && (uid != pw->pw_uid)) | |
308 | { | |
309 | fprintf(stderr, "Permission denied\n"); | |
310 | exit(1); | |
311 | } | |
312 | ||
313 | /* | |
314 | * Get the new password | |
315 | */ | |
316 | getpasswd(uname, (uid == 0), 5, 0, 0, pw->pw_passwd, &ne, &oc, &nc); | |
317 | ||
318 | newpw.pw_name = copyString(pw->pw_name); | |
319 | newpw.pw_passwd = copyString(ne); | |
320 | newpw.pw_uid = pw->pw_uid; | |
321 | newpw.pw_gid = pw->pw_gid; | |
322 | newpw.pw_class = copyString(pw->pw_class); | |
323 | newpw.pw_change = pw->pw_change; | |
324 | newpw.pw_expire = pw->pw_expire; | |
325 | newpw.pw_gecos = copyString(pw->pw_gecos); | |
326 | newpw.pw_dir = copyString(pw->pw_dir); | |
327 | newpw.pw_shell = copyString(pw->pw_shell); | |
328 | ||
329 | /* | |
330 | * Re-write the file | |
331 | */ | |
d1ccfde8 | 332 | rewrite_file(fname, fp, &newpw, locn); |
1815bff5 A |
333 | |
334 | /* | |
335 | * Clean up memory | |
336 | */ | |
337 | pw = parse_user(NULL); | |
338 | free(newpw.pw_name); | |
339 | free(newpw.pw_passwd); | |
340 | free(newpw.pw_gecos); | |
341 | free(newpw.pw_dir); | |
342 | free(newpw.pw_shell); | |
343 | free(newpw.pw_class); | |
344 | ||
345 | fclose(fp); | |
346 | ||
347 | return 0; | |
348 | } | |
d1ccfde8 A |
349 | |
350 | ||
351 | void sighandler(int inSignal) | |
352 | { | |
353 | unlink(LOCK_FILE); | |
354 | exit(1); | |
355 | } | |
356 | ||
357 | ||
358 | int | |
359 | file_passwd(char *uname, char *locn) | |
360 | { | |
361 | pid_t pid; | |
362 | int retVal = 0; | |
363 | int waitResult = 0; | |
364 | int retries = 0; | |
365 | struct stat sb; | |
366 | FILE *lockFile; | |
367 | struct sigaction action = {0}; | |
368 | ||
369 | /* trap signals */ | |
370 | sigfillset( &action.sa_mask ); | |
371 | action.sa_flags = SA_RESTART; | |
372 | action.sa_handler = sighandler; | |
373 | sigaction(SIGHUP, &action, NULL); | |
374 | sigaction(SIGINT, &action, NULL); // ctrl-c | |
375 | sigaction(SIGQUIT, &action, NULL); | |
376 | sigaction(SIGABRT, &action, NULL); | |
377 | sigaction(SIGPIPE, &action, NULL); | |
378 | sigaction(SIGALRM, &action, NULL); | |
379 | sigaction(SIGTERM, &action, NULL); | |
380 | sigaction(SIGSTOP, &action, NULL); | |
381 | sigaction(SIGTSTP, &action, NULL); | |
382 | ||
383 | /* Check/create lock file */ | |
384 | for (retries = 0; retries < 5; retries++) | |
385 | { | |
386 | retVal = lstat(LOCK_FILE, &sb); | |
387 | if (retVal != 0) | |
388 | break; | |
389 | /* try in 100 milliseconds */ | |
390 | usleep(100000); | |
391 | } | |
392 | if (retVal == 0) | |
393 | { | |
394 | fprintf(stderr, "another passwd process is running.\n"); | |
395 | exit(EX_TEMPFAIL); | |
396 | } | |
397 | ||
398 | umask((S_IRWXG | S_IRWXO)); | |
399 | lockFile = fopen(LOCK_FILE, "w"); | |
400 | if (lockFile == NULL) | |
401 | { | |
402 | fprintf(stderr, "can't create lock file.\n"); | |
403 | exit(EX_CANTCREAT); | |
404 | } | |
405 | fprintf(lockFile, "%d\n", getpid()); | |
406 | fclose(lockFile); | |
407 | ||
408 | pid = fork(); | |
409 | if (pid == -1) | |
410 | { | |
411 | fprintf(stderr, "can't fork\n"); | |
412 | exit(EX_OSERR); | |
413 | } | |
414 | ||
415 | /* Handle the child */ | |
416 | if (pid == 0) | |
417 | { | |
418 | retVal = _file_passwd_main(uname, locn); | |
419 | exit(retVal); | |
420 | } | |
421 | ||
422 | /* Handle the parent */ | |
423 | waitResult = waitpid(pid, &retVal, 0); | |
424 | retVal = (waitResult == 0) ? WEXITSTATUS(retVal) : 1; | |
425 | ||
426 | /* delete lock file */ | |
427 | unlink(LOCK_FILE); | |
428 | ||
429 | return retVal; | |
430 | } | |
431 |