]> git.saurik.com Git - apple/network_cmds.git/blob - rpc_yppasswdd.tproj/passwd.c
network_cmds-115.tar.gz
[apple/network_cmds.git] / rpc_yppasswdd.tproj / passwd.c
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