]> git.saurik.com Git - apple/network_cmds.git/blob - rpc_yppasswdd.tproj/passwd.c
network_cmds-201.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 * 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.
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,
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.
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