]>
Commit | Line | Data |
---|---|---|
b7080c8e A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
ac2f15b3 A |
6 | * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. |
7 | * | |
8 | * This file contains Original Code and/or Modifications of Original Code | |
9 | * as defined in and that are subject to the Apple Public Source License | |
10 | * Version 2.0 (the 'License'). You may not use this file except in | |
11 | * compliance with the License. Please obtain a copy of the License at | |
12 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
13 | * file. | |
b7080c8e A |
14 | * |
15 | * The Original Code and all software distributed under the License are | |
16 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
17 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
18 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
ac2f15b3 A |
19 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
20 | * Please see the License for the specific language governing rights and | |
21 | * limitations under the License. | |
b7080c8e A |
22 | * |
23 | * @APPLE_LICENSE_HEADER_END@ | |
24 | */ | |
25 | /* $NetBSD: passwd.c,v 1.11 1997/12/31 05:47:15 thorpej Exp $ */ | |
26 | ||
27 | /* | |
28 | * Copyright (c) 1987, 1993, 1994, 1995 | |
29 | * The Regents of the University of California. All rights reserved. | |
30 | * | |
31 | * Redistribution and use in source and binary forms, with or without | |
32 | * modification, are permitted provided that the following conditions | |
33 | * are met: | |
34 | * 1. Redistributions of source code must retain the above copyright | |
35 | * notice, this list of conditions and the following disclaimer. | |
36 | * 2. Redistributions in binary form must reproduce the above copyright | |
37 | * notice, this list of conditions and the following disclaimer in the | |
38 | * documentation and/or other materials provided with the distribution. | |
39 | * 3. All advertising materials mentioning features or use of this software | |
40 | * must display the following acknowledgement: | |
41 | * This product includes software developed by the University of | |
42 | * California, Berkeley and its contributors. | |
43 | * 4. Neither the name of the University nor the names of its contributors | |
44 | * may be used to endorse or promote products derived from this software | |
45 | * without specific prior written permission. | |
46 | * | |
47 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
48 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
49 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
50 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
51 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
52 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
53 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
54 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
55 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
56 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
57 | * SUCH DAMAGE. | |
58 | */ | |
59 | ||
60 | #include <sys/cdefs.h> | |
61 | #if defined(LIBC_SCCS) && !defined(lint) | |
62 | __RCSID("$NetBSD: passwd.c,v 1.11 1997/12/31 05:47:15 thorpej Exp $"); | |
63 | #endif /* LIBC_SCCS and not lint */ | |
64 | ||
65 | #include <sys/types.h> | |
66 | #include <sys/stat.h> | |
67 | #include <sys/time.h> | |
68 | #include <sys/resource.h> | |
69 | #include <sys/wait.h> | |
70 | ||
71 | #include <ctype.h> | |
72 | #include <err.h> | |
73 | #include <fcntl.h> | |
74 | #include <unistd.h> | |
75 | #include <stdlib.h> | |
76 | #include <stdio.h> | |
77 | #include <string.h> | |
78 | #include <pwd.h> | |
79 | #include <errno.h> | |
80 | #include <paths.h> | |
81 | #include <signal.h> | |
82 | #include <limits.h> | |
83 | #include <util.h> | |
84 | ||
85 | static void pw_cont __P((int sig)); | |
86 | static int pw_equal __P((char *buf, struct passwd *old_pw)); | |
87 | ||
88 | int | |
89 | pw_lock(retries) | |
90 | int retries; | |
91 | { | |
92 | int i, fd; | |
93 | mode_t old_mode; | |
94 | ||
95 | /* Acquire the lock file. */ | |
96 | old_mode = umask(0); | |
97 | fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL, 0600); | |
98 | for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) { | |
99 | sleep(1); | |
100 | fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL, | |
101 | 0600); | |
102 | } | |
103 | umask(old_mode); | |
104 | return(fd); | |
105 | } | |
106 | ||
107 | int | |
108 | pw_mkdb() | |
109 | { | |
110 | int pstat; | |
111 | pid_t pid; | |
112 | struct stat sb; | |
113 | ||
114 | /* A zero length passwd file is never ok */ | |
115 | if (stat(_PATH_MASTERPASSWD_LOCK, &sb) == 0) { | |
116 | if (sb.st_size == 0) { | |
117 | warnx("%s is zero length", _PATH_MASTERPASSWD_LOCK); | |
118 | return (-1); | |
119 | } | |
120 | } | |
121 | ||
122 | pid = vfork(); | |
123 | if (pid == 0) { | |
124 | execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", | |
125 | _PATH_MASTERPASSWD_LOCK, NULL); | |
126 | _exit(1); | |
127 | } | |
128 | pid = waitpid(pid, &pstat, 0); | |
129 | if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) | |
130 | return(-1); | |
131 | return(0); | |
132 | } | |
133 | ||
134 | int | |
135 | pw_abort() | |
136 | { | |
137 | return(unlink(_PATH_MASTERPASSWD_LOCK)); | |
138 | } | |
139 | ||
140 | /* Everything below this point is intended for the convenience of programs | |
141 | * which allow a user to interactively edit the passwd file. Errors in the | |
142 | * routines below will cause the process to abort. */ | |
143 | ||
144 | static pid_t editpid = -1; | |
145 | ||
146 | static void | |
147 | pw_cont(sig) | |
148 | int sig; | |
149 | { | |
150 | ||
151 | if (editpid != -1) | |
152 | kill(editpid, sig); | |
153 | } | |
154 | ||
155 | void | |
156 | pw_init() | |
157 | { | |
158 | struct rlimit rlim; | |
159 | ||
160 | /* Unlimited resource limits. */ | |
161 | rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; | |
162 | (void)setrlimit(RLIMIT_CPU, &rlim); | |
163 | (void)setrlimit(RLIMIT_FSIZE, &rlim); | |
164 | (void)setrlimit(RLIMIT_STACK, &rlim); | |
165 | (void)setrlimit(RLIMIT_DATA, &rlim); | |
166 | (void)setrlimit(RLIMIT_RSS, &rlim); | |
167 | ||
168 | /* Don't drop core (not really necessary, but GP's). */ | |
169 | rlim.rlim_cur = rlim.rlim_max = 0; | |
170 | (void)setrlimit(RLIMIT_CORE, &rlim); | |
171 | ||
172 | /* Turn off signals. */ | |
173 | (void)signal(SIGALRM, SIG_IGN); | |
174 | (void)signal(SIGHUP, SIG_IGN); | |
175 | (void)signal(SIGINT, SIG_IGN); | |
176 | (void)signal(SIGPIPE, SIG_IGN); | |
177 | (void)signal(SIGQUIT, SIG_IGN); | |
178 | (void)signal(SIGTERM, SIG_IGN); | |
179 | (void)signal(SIGCONT, pw_cont); | |
180 | } | |
181 | ||
182 | void | |
183 | pw_edit(notsetuid, filename) | |
184 | int notsetuid; | |
185 | const char *filename; | |
186 | { | |
187 | int i, xargc, pstat; | |
188 | char *p, *editor; | |
189 | char **xargv; | |
190 | #ifdef __GNUC__ | |
191 | (void) &editor; | |
192 | #endif | |
193 | ||
194 | if (filename == NULL) | |
195 | filename = _PATH_MASTERPASSWD_LOCK; | |
196 | if ((editor = getenv("EDITOR")) == NULL) | |
197 | editor = strdup(_PATH_VI); | |
198 | else | |
199 | editor = strdup(editor); | |
200 | if ((p = strrchr(editor, '/'))) | |
201 | ++p; | |
202 | else | |
203 | p = editor; | |
204 | ||
205 | /* Scan editor string, count spaces, allocate arg vector. */ | |
206 | for (i = 0, xargc = 0; p[i] != '\0'; i++) { | |
207 | if (isspace(p[i])) { | |
208 | while (isspace(p[i++])) | |
209 | /* skip white space */ ; | |
210 | if (p[i] == '\0') | |
211 | break; | |
212 | xargc++; | |
213 | } | |
214 | } | |
215 | ||
216 | /* argv[0] + <xargc args> + filename + NULL */ | |
217 | xargv = (char **)malloc(sizeof(char *) * (xargc + 3)); | |
218 | if (xargv == NULL) | |
219 | pw_error("malloc failed", 1, 1); | |
220 | ||
221 | i = 0; | |
222 | xargv[i++] = p; | |
223 | for (; *p != '\0'; p++) { | |
224 | if (isspace(*p)) { | |
225 | while(isspace(*p)) | |
226 | *p++ = '\0'; /* blast whitespace */ | |
227 | if (*p == '\0') | |
228 | break; | |
229 | xargv[i++] = p; | |
230 | } | |
231 | } | |
232 | ||
233 | xargv[i++] = (char *)filename; | |
234 | xargv[i] = NULL; | |
235 | ||
236 | if (!(editpid = vfork())) { | |
237 | if (notsetuid) { | |
238 | setgid(getgid()); | |
239 | setuid(getuid()); | |
240 | } | |
241 | execvp(editor, xargv); | |
242 | _exit(1); | |
243 | } | |
244 | for (;;) { | |
245 | editpid = waitpid(editpid, (int *)&pstat, WUNTRACED); | |
246 | if (editpid == -1) | |
247 | pw_error(editor, 1, 1); | |
248 | else if (WIFSTOPPED(pstat)) | |
249 | raise(WSTOPSIG(pstat)); | |
250 | else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) | |
251 | break; | |
252 | else | |
253 | pw_error(editor, 1, 1); | |
254 | } | |
255 | editpid = -1; | |
256 | free(editor); | |
257 | free(xargv); | |
258 | } | |
259 | ||
260 | void | |
261 | pw_prompt() | |
262 | { | |
263 | int c; | |
264 | ||
265 | (void)printf("re-edit the password file? [y]: "); | |
266 | (void)fflush(stdout); | |
267 | c = getchar(); | |
268 | if (c != EOF && c != '\n') | |
269 | while (getchar() != '\n'); | |
270 | if (c == 'n') | |
271 | pw_error(NULL, 0, 0); | |
272 | } | |
273 | ||
274 | /* for use in pw_copy(). Compare a pw entry to a pw struct. */ | |
275 | static int | |
276 | pw_equal (buf, pw) | |
277 | char *buf; | |
278 | struct passwd *pw; | |
279 | { | |
280 | struct passwd buf_pw; | |
281 | int len = strlen (buf); | |
282 | if (buf[len-1] == '\n') | |
283 | buf[len-1] = '\0'; | |
284 | if (!pw_scan(buf, &buf_pw, NULL)) | |
285 | return 0; | |
286 | return !strcmp(pw->pw_name, buf_pw.pw_name) | |
287 | && pw->pw_uid == buf_pw.pw_uid | |
288 | && pw->pw_gid == buf_pw.pw_gid | |
289 | && !strcmp(pw->pw_class, buf_pw.pw_class) | |
290 | && (long)pw->pw_change == (long)buf_pw.pw_change | |
291 | && (long)pw->pw_expire == (long)buf_pw.pw_expire | |
292 | && !strcmp(pw->pw_gecos, buf_pw.pw_gecos) | |
293 | && !strcmp(pw->pw_dir, buf_pw.pw_dir) | |
294 | && !strcmp(pw->pw_shell, buf_pw.pw_shell); | |
295 | } | |
296 | ||
297 | void | |
298 | pw_copy(ffd, tfd, pw, old_pw) | |
299 | int ffd, tfd; | |
300 | struct passwd *pw, *old_pw; | |
301 | { | |
302 | FILE *from, *to; | |
303 | int done; | |
304 | char *p, buf[8192]; | |
305 | ||
306 | if (!(from = fdopen(ffd, "r"))) | |
307 | pw_error(_PATH_MASTERPASSWD, 1, 1); | |
308 | if (!(to = fdopen(tfd, "w"))) | |
309 | pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1); | |
310 | ||
311 | for (done = 0; fgets(buf, sizeof(buf), from);) { | |
312 | if (!strchr(buf, '\n')) { | |
313 | warnx("%s: line too long", _PATH_MASTERPASSWD); | |
314 | pw_error(NULL, 0, 1); | |
315 | } | |
316 | if (done) { | |
317 | (void)fprintf(to, "%s", buf); | |
318 | if (ferror(to)) | |
319 | goto err; | |
320 | continue; | |
321 | } | |
322 | if (buf[0] == '#') { | |
323 | /* skip comments for Rhapsody. */ | |
324 | continue; | |
325 | } | |
326 | if (!(p = strchr(buf, ':'))) { | |
327 | warnx("%s: corrupted entry", _PATH_MASTERPASSWD); | |
328 | pw_error(NULL, 0, 1); | |
329 | } | |
330 | *p = '\0'; | |
331 | if (strcmp(buf, pw->pw_name)) { | |
332 | *p = ':'; | |
333 | (void)fprintf(to, "%s", buf); | |
334 | if (ferror(to)) | |
335 | goto err; | |
336 | continue; | |
337 | } | |
338 | *p = ':'; | |
339 | if (old_pw && !pw_equal(buf, old_pw)) { | |
340 | warnx("%s: entry inconsistent", | |
341 | _PATH_MASTERPASSWD); | |
342 | pw_error(NULL, 0, 1); | |
343 | } | |
344 | (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", | |
345 | pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, | |
346 | pw->pw_class, (long)pw->pw_change, (long)pw->pw_expire, | |
347 | pw->pw_gecos, pw->pw_dir, pw->pw_shell); | |
348 | done = 1; | |
349 | if (ferror(to)) | |
350 | goto err; | |
351 | } | |
352 | /* Only append a new entry if real uid is root! */ | |
353 | if (!done) | |
354 | if (getuid() == 0) | |
355 | (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", | |
356 | pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, | |
357 | pw->pw_class, (long)pw->pw_change, | |
358 | (long)pw->pw_expire, pw->pw_gecos, pw->pw_dir, | |
359 | pw->pw_shell); | |
360 | else | |
361 | warnx("%s: changes not made, no such entry", | |
362 | _PATH_MASTERPASSWD); | |
363 | ||
364 | if (ferror(to)) | |
365 | err: pw_error(NULL, 1, 1); | |
366 | (void)fclose(to); | |
367 | } | |
368 | ||
369 | int | |
370 | pw_scan(bp, pw, flags) | |
371 | char *bp; | |
372 | struct passwd *pw; | |
373 | int *flags; | |
374 | { | |
375 | unsigned long id; | |
376 | int root; | |
377 | char *p, *sh, *ep; | |
378 | ||
379 | if (flags != (int *)NULL) | |
380 | *flags = 0; | |
381 | ||
382 | if (!(pw->pw_name = strsep(&bp, ":"))) /* login */ | |
383 | goto fmt; | |
384 | root = !strcmp(pw->pw_name, "root"); | |
385 | ||
386 | if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */ | |
387 | goto fmt; | |
388 | ||
389 | if (!(p = strsep(&bp, ":"))) /* uid */ | |
390 | goto fmt; | |
391 | id = strtoul(p, &ep, 10); | |
392 | if (root && id) { | |
393 | warnx("root uid should be 0"); | |
394 | return (0); | |
395 | } | |
396 | if (id > UID_MAX || *ep != '\0') { | |
397 | warnx("invalid uid '%s'", p); | |
398 | return (0); | |
399 | } | |
400 | pw->pw_uid = (uid_t)id; | |
401 | if ((*p == '\0') && (flags != (int *)NULL)) | |
402 | *flags |= _PASSWORD_NOUID; | |
403 | ||
404 | if (!(p = strsep(&bp, ":"))) /* gid */ | |
405 | goto fmt; | |
406 | id = strtoul(p, &ep, 10); | |
407 | if (id > GID_MAX || *ep != '\0') { | |
408 | warnx("invalid gid '%s'", p); | |
409 | return (0); | |
410 | } | |
411 | pw->pw_gid = (gid_t)id; | |
412 | if ((*p == '\0') && (flags != (int *)NULL)) | |
413 | *flags |= _PASSWORD_NOGID; | |
414 | ||
415 | pw->pw_class = strsep(&bp, ":"); /* class */ | |
416 | if (!(p = strsep(&bp, ":"))) /* change */ | |
417 | goto fmt; | |
418 | pw->pw_change = atol(p); | |
419 | if ((*p == '\0') && (flags != (int *)NULL)) | |
420 | *flags |= _PASSWORD_NOCHG; | |
421 | if (!(p = strsep(&bp, ":"))) /* expire */ | |
422 | goto fmt; | |
423 | pw->pw_expire = atol(p); | |
424 | if ((*p == '\0') && (flags != (int *)NULL)) | |
425 | *flags |= _PASSWORD_NOEXP; | |
426 | pw->pw_gecos = strsep(&bp, ":"); /* gecos */ | |
427 | pw->pw_dir = strsep(&bp, ":"); /* directory */ | |
428 | if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */ | |
429 | goto fmt; | |
430 | ||
431 | p = pw->pw_shell; | |
432 | if (root && *p) /* empty == /bin/sh */ | |
433 | for (setusershell();;) { | |
434 | if (!(sh = getusershell())) { | |
435 | warnx("warning, unknown root shell"); | |
436 | break; | |
437 | } | |
438 | if (!strcmp(p, sh)) | |
439 | break; | |
440 | } | |
441 | ||
442 | if ((p = strsep(&bp, ":"))) { /* too many */ | |
443 | fmt: warnx("corrupted entry"); | |
444 | return (0); | |
445 | } | |
446 | ||
447 | return (1); | |
448 | } | |
449 | ||
450 | void | |
451 | pw_error(name, err, eval) | |
452 | const char *name; | |
453 | int err, eval; | |
454 | { | |
455 | if (err) | |
456 | warn(name); | |
457 | ||
458 | warnx("%s: unchanged", _PATH_MASTERPASSWD); | |
459 | pw_abort(); | |
460 | exit(eval); | |
461 | } | |
462 |