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