1 /* $OpenBSD: passwd.c,v 1.56 2019/06/28 13:32:43 deraadt 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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/time.h>
35 #include <sys/resource.h>
36 #include <sys/wait.h>
37
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <ctype.h>
44 #include <pwd.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <paths.h>
48 #include <signal.h>
49 #include <limits.h>
50
51 #include "util.h"
52
53 static char pw_defdir[] = "/etc";
54 static char *pw_dir = pw_defdir;
55 static char *pw_lck;
56
57 char *
pw_file(const char * nm)58 pw_file(const char *nm)
59 {
60 const char *p = strrchr(nm, '/');
61 char *new_nm;
62
63 if (p)
64 p++;
65 else
66 p = nm;
67
68 if (asprintf(&new_nm, "%s/%s", pw_dir, p) == -1)
69 return NULL;
70 return new_nm;
71 }
72
73 void
pw_setdir(const char * dir)74 pw_setdir(const char *dir)
75 {
76 char *p;
77
78 if (strcmp (dir, pw_dir) == 0)
79 return;
80 if (pw_dir != pw_defdir)
81 free(pw_dir);
82 pw_dir = strdup(dir);
83 if (pw_lck) {
84 p = pw_file(pw_lck);
85 free(pw_lck);
86 pw_lck = p;
87 }
88 }
89
90
91 int
pw_lock(int retries)92 pw_lock(int retries)
93 {
94 int i, fd;
95 mode_t old_mode;
96
97 if (!pw_lck) {
98 errno = EINVAL;
99 return (-1);
100 }
101 /* Acquire the lock file. */
102 old_mode = umask(0);
103 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600);
104 for (i = 0; i < retries && fd == -1 && errno == EEXIST; i++) {
105 sleep(1);
106 fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600);
107 }
108 (void) umask(old_mode);
109 return (fd);
110 }
111
112 int
pw_mkdb(char * username,int flags)113 pw_mkdb(char *username, int flags)
114 {
115 int pstat, ac;
116 pid_t pid;
117 char *av[8];
118 struct stat sb;
119
120 if (pw_lck == NULL)
121 return(-1);
122
123 /* A zero length passwd file is never ok */
124 if (stat(pw_lck, &sb) == 0 && sb.st_size == 0) {
125 warnx("%s is zero length", pw_lck);
126 return (-1);
127 }
128
129 ac = 0;
130 av[ac++] = "pwd_mkdb";
131 av[ac++] = "-d";
132 av[ac++] = pw_dir;
133 if (flags & _PASSWORD_SECUREONLY)
134 av[ac++] = "-s";
135 else if (!(flags & _PASSWORD_OMITV7))
136 av[ac++] = "-p";
137 if (username) {
138 av[ac++] = "-u";
139 av[ac++] = username;
140 }
141 av[ac++] = pw_lck;
142 av[ac] = NULL;
143
144 pid = vfork();
145 if (pid == -1)
146 return (-1);
147 if (pid == 0) {
148 if (pw_lck)
149 execv(_PATH_PWD_MKDB, av);
150 _exit(1);
151 }
152 pid = waitpid(pid, &pstat, 0);
153 if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0)
154 return (-1);
155 return (0);
156 }
157
158 int
pw_abort(void)159 pw_abort(void)
160 {
161 return (pw_lck ? unlink(pw_lck) : -1);
162 }
163
164 /* Everything below this point is intended for the convenience of programs
165 * which allow a user to interactively edit the passwd file. Errors in the
166 * routines below will cause the process to abort. */
167
168 static pid_t editpid = -1;
169
170 static void
pw_cont(int signo)171 pw_cont(int signo)
172 {
173 int save_errno = errno;
174
175 if (editpid != -1)
176 kill(editpid, signo);
177 errno = save_errno;
178 }
179
180 void
pw_init(void)181 pw_init(void)
182 {
183 struct rlimit rlim;
184
185 /* Unlimited resource limits. */
186 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
187 (void)setrlimit(RLIMIT_CPU, &rlim);
188 (void)setrlimit(RLIMIT_FSIZE, &rlim);
189 (void)setrlimit(RLIMIT_STACK, &rlim);
190 (void)setrlimit(RLIMIT_DATA, &rlim);
191 (void)setrlimit(RLIMIT_RSS, &rlim);
192
193 /* Don't drop core (not really necessary, but GP's). */
194 rlim.rlim_cur = rlim.rlim_max = 0;
195 (void)setrlimit(RLIMIT_CORE, &rlim);
196
197 /* Turn off signals. */
198 (void)signal(SIGALRM, SIG_IGN);
199 (void)signal(SIGHUP, SIG_IGN);
200 (void)signal(SIGINT, SIG_IGN);
201 (void)signal(SIGPIPE, SIG_IGN);
202 (void)signal(SIGQUIT, SIG_IGN);
203 (void)signal(SIGTERM, SIG_IGN);
204 (void)signal(SIGCONT, pw_cont);
205
206 if (!pw_lck)
207 pw_lck = pw_file(_PATH_MASTERPASSWD_LOCK);
208 }
209
210 void
pw_edit(int notsetuid,const char * filename)211 pw_edit(int notsetuid, const char *filename)
212 {
213 int pstat;
214 char *p;
215 char *editor;
216 char *argp[] = {"sh", "-c", NULL, NULL};
217
218 if (!filename) {
219 filename = pw_lck;
220 if (!filename)
221 return;
222 }
223
224 if ((editor = getenv("EDITOR")) == NULL)
225 editor = _PATH_VI;
226
227 if (asprintf(&p, "%s %s", editor, filename) == -1)
228 return;
229 argp[2] = p;
230
231 switch (editpid = vfork()) {
232 case -1: /* error */
233 free(p);
234 return;
235 case 0: /* child */
236 if (notsetuid) {
237 setgid(getgid());
238 setuid(getuid());
239 }
240 execv(_PATH_BSHELL, argp);
241 _exit(127);
242 }
243
244 free(p);
245 for (;;) {
246 editpid = waitpid(editpid, (int *)&pstat, WUNTRACED);
247 if (editpid == -1)
248 pw_error(editor, 1, 1);
249 else if (WIFSTOPPED(pstat))
250 raise(WSTOPSIG(pstat));
251 else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
252 break;
253 else
254 pw_error(editor, 1, 1);
255 }
256 editpid = -1;
257 }
258
259 void
pw_prompt(void)260 pw_prompt(void)
261 {
262 int first, c;
263
264 (void)printf("re-edit the password file? [y]: ");
265 (void)fflush(stdout);
266 first = c = getchar();
267 while (c != '\n' && c != EOF)
268 c = getchar();
269 switch (first) {
270 case EOF:
271 putchar('\n');
272 /* FALLTHROUGH */
273 case 'n':
274 case 'N':
275 pw_error(NULL, 0, 0);
276 break;
277 }
278 }
279
280 static int
pw_equal(const struct passwd * pw1,const struct passwd * pw2)281 pw_equal(const struct passwd *pw1, const struct passwd *pw2)
282 {
283 return (strcmp(pw1->pw_name, pw2->pw_name) == 0 &&
284 pw1->pw_uid == pw2->pw_uid &&
285 pw1->pw_gid == pw2->pw_gid &&
286 strcmp(pw1->pw_class, pw2->pw_class) == 0 &&
287 pw1->pw_change == pw2->pw_change &&
288 pw1->pw_expire == pw2->pw_expire &&
289 strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 &&
290 strcmp(pw1->pw_dir, pw2->pw_dir) == 0 &&
291 strcmp(pw1->pw_shell, pw2->pw_shell) == 0);
292 }
293
294 static int
pw_write_entry(FILE * to,const struct passwd * pw)295 pw_write_entry(FILE *to, const struct passwd *pw)
296 {
297 char gidstr[16], uidstr[16];
298
299 /* Preserve gid/uid -1 */
300 if (pw->pw_gid == (gid_t)-1)
301 strlcpy(gidstr, "-1", sizeof(gidstr));
302 else
303 snprintf(gidstr, sizeof(gidstr), "%u", (u_int)pw->pw_gid);
304
305 if (pw->pw_uid == (uid_t)-1)
306 strlcpy(uidstr, "-1", sizeof(uidstr));
307 else
308 snprintf(uidstr, sizeof(uidstr), "%u", (u_int)pw->pw_uid);
309
310 return fprintf(to, "%s:%s:%s:%s:%s:%lld:%lld:%s:%s:%s\n",
311 pw->pw_name, pw->pw_passwd, uidstr, gidstr, pw->pw_class,
312 (long long)pw->pw_change, (long long)pw->pw_expire,
313 pw->pw_gecos, pw->pw_dir, pw->pw_shell);
314 }
315
316 void
pw_copy(int ffd,int tfd,const struct passwd * pw,const struct passwd * opw)317 pw_copy(int ffd, int tfd, const struct passwd *pw, const struct passwd *opw)
318 {
319 struct passwd tpw;
320 FILE *from, *to;
321 int done;
322 char *p, *ep, buf[8192];
323 char *master = pw_file(_PATH_MASTERPASSWD);
324
325 if (!master)
326 pw_error(NULL, 0, 1);
327 if (!(from = fdopen(ffd, "r")))
328 pw_error(master, 1, 1);
329 if (!(to = fdopen(tfd, "w")))
330 pw_error(pw_lck ? pw_lck : NULL, pw_lck ? 1 : 0, 1);
331
332 for (done = 0; fgets(buf, (int)sizeof(buf), from);) {
333 if ((ep = strchr(buf, '\n')) == NULL) {
334 warnx("%s: line too long", master);
335 pw_error(NULL, 0, 1);
336 }
337 if (done) {
338 if (fputs(buf, to))
339 goto fail;
340 continue;
341 }
342 if (!(p = strchr(buf, ':'))) {
343 warnx("%s: corrupted entry", master);
344 pw_error(NULL, 0, 1);
345 }
346 *p = '\0';
347 if (strcmp(buf, opw ? opw->pw_name : pw->pw_name)) {
348 *p = ':';
349 if (fputs(buf, to))
350 goto fail;
351 continue;
352 }
353 if (opw != NULL) {
354 *p = ':';
355 *ep = '\0';
356 if (!pw_scan(buf, &tpw, NULL))
357 pw_error(NULL, 0, 1);
358 if (!pw_equal(&tpw, opw)) {
359 warnx("%s: inconsistent entry", master);
360 pw_error(NULL, 0, 1);
361 }
362 }
363 if (pw_write_entry(to, pw) == -1)
364 goto fail;
365 done = 1;
366 }
367 if (!done && pw_write_entry(to, pw) == -1)
368 goto fail;
369
370 if (ferror(to) || fflush(to))
371 fail:
372 pw_error(NULL, 0, 1);
373 free(master);
374 (void)fclose(to);
375 }
376
377 int
pw_scan(char * bp,struct passwd * pw,int * flags)378 pw_scan(char *bp, struct passwd *pw, int *flags)
379 {
380 int root;
381 char *p, *sh;
382 const char *errstr;
383
384 if (flags != NULL)
385 *flags = 0;
386
387 if (!(p = strsep(&bp, ":")) || *p == '\0') /* login */
388 goto fmt;
389 pw->pw_name = p;
390 root = !strcmp(pw->pw_name, "root");
391
392 if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */
393 goto fmt;
394
395 if (!(p = strsep(&bp, ":"))) /* uid */
396 goto fmt;
397 pw->pw_uid = strtonum(p, -1, UID_MAX, &errstr);
398 if (errstr != NULL) {
399 if (*p != '\0') {
400 warnx("uid is %s", errstr);
401 return (0);
402 }
403 if (flags != NULL)
404 *flags |= _PASSWORD_NOUID;
405 }
406 if (root && pw->pw_uid) {
407 warnx("root uid should be 0");
408 return (0);
409 }
410
411 if (!(p = strsep(&bp, ":"))) /* gid */
412 goto fmt;
413 pw->pw_gid = strtonum(p, -1, GID_MAX, &errstr);
414 if (errstr != NULL) {
415 if (*p != '\0') {
416 warnx("gid is %s", errstr);
417 return (0);
418 }
419 if (flags != NULL)
420 *flags |= _PASSWORD_NOGID;
421 }
422
423 pw->pw_class = strsep(&bp, ":"); /* class */
424 if (!(p = strsep(&bp, ":"))) /* change */
425 goto fmt;
426 pw->pw_change = atoll(p);
427 if ((*p == '\0') && (flags != (int *)NULL))
428 *flags |= _PASSWORD_NOCHG;
429 if (!(p = strsep(&bp, ":"))) /* expire */
430 goto fmt;
431 pw->pw_expire = atoll(p);
432 if ((*p == '\0') && (flags != (int *)NULL))
433 *flags |= _PASSWORD_NOEXP;
434 pw->pw_gecos = strsep(&bp, ":"); /* gecos */
435 pw->pw_dir = strsep(&bp, ":"); /* directory */
436 if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */
437 goto fmt;
438
439 p = pw->pw_shell;
440 if (root && *p) { /* empty == /bin/sh */
441 for (setusershell();;) {
442 if (!(sh = getusershell())) {
443 warnx("warning, unknown root shell");
444 break;
445 }
446 if (!strcmp(p, sh))
447 break;
448 }
449 endusershell();
450 }
451
452 if ((p = strsep(&bp, ":"))) { /* too many */
453 fmt: warnx("corrupted entry");
454 return (0);
455 }
456
457 return (1);
458 }
459
460 __dead void
pw_error(const char * name,int error,int eval)461 pw_error(const char *name, int error, int eval)
462 {
463 char *master = pw_file(_PATH_MASTERPASSWD);
464
465 if (error) {
466 if (name)
467 warn("%s", name);
468 else
469 warn(NULL);
470 }
471 if (master) {
472 warnx("%s: unchanged", master);
473 free(master);
474 }
475
476 pw_abort();
477 exit(eval);
478 }
479