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