Annotation of src/usr.bin/chpass/chpass.c, Revision 1.48
1.48 ! deraadt 1: /* $OpenBSD: chpass.c,v 1.47 2021/07/12 15:09:19 beck Exp $ */
1.2 deraadt 2: /* $NetBSD: chpass.c,v 1.8 1996/05/15 21:50:43 jtc Exp $ */
1.1 deraadt 3:
4: /*-
5: * Copyright (c) 1988, 1993, 1994
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
1.27 millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 17: * may be used to endorse or promote products derived from this software
18: * without specific prior written permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30: * SUCH DAMAGE.
31: */
32:
1.17 millert 33: #include <sys/resource.h>
1.1 deraadt 34: #include <sys/stat.h>
35: #include <sys/time.h>
1.17 millert 36: #include <sys/uio.h>
1.1 deraadt 37:
38: #include <err.h>
39: #include <errno.h>
40: #include <fcntl.h>
1.28 avsm 41: #include <paths.h>
1.1 deraadt 42: #include <pwd.h>
1.17 millert 43: #include <signal.h>
1.1 deraadt 44: #include <stdio.h>
45: #include <stdlib.h>
46: #include <string.h>
47: #include <unistd.h>
1.2 deraadt 48: #include <util.h>
1.1 deraadt 49:
50: #include "chpass.h"
51:
1.25 deraadt 52: extern char *__progname;
53:
1.17 millert 54: enum { NEWSH, LOADENTRY, EDITENTRY } op;
1.1 deraadt 55: uid_t uid;
56:
1.21 millert 57: void baduser(void);
58: void kbintr(int);
59: void usage(void);
1.1 deraadt 60:
61: int
1.23 deraadt 62: main(int argc, char *argv[])
1.1 deraadt 63: {
1.33 otto 64: struct passwd *pw = NULL, *opw = NULL, lpw;
1.20 millert 65: int i, ch, pfd, tfd, dfd;
1.35 tobias 66: char *tz, *arg = NULL;
1.17 millert 67: sigset_t fullset;
1.1 deraadt 68:
1.35 tobias 69: /* We need to use the system timezone for date conversions. */
70: if ((tz = getenv("TZ")) != NULL) {
71: unsetenv("TZ");
72: tzset();
73: setenv("TZ", tz, 1);
74: }
1.1 deraadt 75:
76: op = EDITENTRY;
1.43 deraadt 77: while ((ch = getopt(argc, argv, "a:s:")) != -1)
1.1 deraadt 78: switch(ch) {
79: case 'a':
80: op = LOADENTRY;
81: arg = optarg;
82: break;
83: case 's':
84: op = NEWSH;
85: arg = optarg;
86: break;
87: case '?':
88: default:
89: usage();
90: }
91: argc -= optind;
92: argv += optind;
93:
94: uid = getuid();
95:
96: if (op == EDITENTRY || op == NEWSH)
97: switch(argc) {
98: case 0:
1.42 tedu 99: pw = getpwuid_shadow(uid);
1.1 deraadt 100: if (!pw)
1.22 mpech 101: errx(1, "unknown user: uid %u", uid);
1.1 deraadt 102: break;
103: case 1:
1.42 tedu 104: pw = getpwnam_shadow(*argv);
1.1 deraadt 105: if (!pw)
106: errx(1, "unknown user: %s", *argv);
107: if (uid && uid != pw->pw_uid)
108: baduser();
109: break;
110: default:
111: usage();
112: }
113:
114: if (op == LOADENTRY) {
1.33 otto 115: if (argc != 0)
116: errx(1, "option -a does not accept user argument");
1.1 deraadt 117: if (uid)
118: baduser();
119: pw = &lpw;
1.9 kstailey 120: if (!pw_scan(arg, pw, NULL))
1.1 deraadt 121: exit(1);
1.42 tedu 122: opw = getpwnam_shadow(pw->pw_name);
1.1 deraadt 123: }
1.33 otto 124: if (opw == NULL && (opw = pw_dup(pw)) == NULL)
1.30 millert 125: err(1, NULL);
1.1 deraadt 126:
1.2 deraadt 127: /* Edit the user passwd information if requested. */
1.1 deraadt 128: if (op == EDITENTRY) {
1.29 espie 129: char tempname[] = _PATH_VARTMP "pw.XXXXXXXXXX";
1.26 millert 130: int edit_status;
1.24 millert 131:
1.30 millert 132: if ((pw = pw_dup(pw)) == NULL)
133: pw_error(NULL, 1, 1);
1.40 guenther 134: dfd = mkostemp(tempname, O_CLOEXEC);
135: if (dfd == -1)
1.2 deraadt 136: pw_error(tempname, 1, 1);
137: display(tempname, dfd, pw);
1.43 deraadt 138:
1.45 mestre 139: if (unveil(_PATH_BSHELL, "x") == -1)
1.47 beck 140: err(1, "unveil %s", _PATH_BSHELL);
1.45 mestre 141: if (unveil(_PATH_SHELLS, "r") == -1)
1.47 beck 142: err(1, "unveil %s", _PATH_SHELLS);
1.45 mestre 143: if (unveil(tempname, "rc") == -1)
1.47 beck 144: err(1, "unveil %s", tempname);
1.45 mestre 145: if (pledge("stdio rpath wpath cpath id proc exec unveil",
1.43 deraadt 146: NULL) == -1)
147: err(1, "pledge");
148:
1.26 millert 149: edit_status = edit(tempname, pw);
1.24 millert 150: close(dfd);
151: unlink(tempname);
1.26 millert 152:
153: switch (edit_status) {
154: case EDIT_OK:
155: break;
156: case EDIT_NOCHANGE:
157: pw_error(NULL, 0, 0);
158: break;
159: case EDIT_ERROR:
160: default:
161: pw_error(tempname, 1, 1);
162: break;
163: }
1.31 wilfried 164: }
165:
166: if (op == NEWSH) {
1.45 mestre 167: if (unveil(_PATH_SHELLS, "r") == -1)
1.47 beck 168: err(1, "unveil %s", _PATH_SHELLS);
1.45 mestre 169: if (pledge("stdio rpath wpath cpath id proc exec unveil",
1.43 deraadt 170: NULL) == -1)
171: err(1, "pledge");
172:
1.31 wilfried 173: /* protect p_shell -- it thinks NULL is /bin/sh */
174: if (!arg[0])
175: usage();
176: if (p_shell(arg, pw, NULL))
177: pw_error(NULL, 0, 1);
1.1 deraadt 178: }
1.2 deraadt 179:
1.17 millert 180: /* Drop user's real uid and block all signals to avoid a DoS. */
181: setuid(0);
182: sigfillset(&fullset);
183: sigdelset(&fullset, SIGINT);
184: sigprocmask(SIG_BLOCK, &fullset, NULL);
185:
1.46 semarie 186: if (unveil(_PATH_MASTERPASSWD_LOCK, "rwc") == -1)
1.47 beck 187: err(1, "unveil %s", _PATH_MASTERPASSWD_LOCK);
1.45 mestre 188: if (unveil(_PATH_MASTERPASSWD, "r") == -1)
1.47 beck 189: err(1, "unveil %s", _PATH_MASTERPASSWD);
1.45 mestre 190: if (unveil(_PATH_PWD_MKDB, "x") == -1)
1.47 beck 191: err(1, "unveil %s", _PATH_PWD_MKDB);
1.43 deraadt 192: if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1)
193: err(1, "pledge");
194:
1.17 millert 195: /* Get the passwd lock file and open the passwd file for reading. */
196: pw_init();
1.20 millert 197: for (i = 1; (tfd = pw_lock(0)) == -1; i++) {
198: if (i == 4)
1.38 schwarze 199: (void)fputs("Attempting to lock password file, "
1.20 millert 200: "please wait or press ^C to abort", stderr);
201: (void)signal(SIGINT, kbintr);
202: if (i % 16 == 0)
1.17 millert 203: fputc('.', stderr);
1.20 millert 204: usleep(250000);
1.17 millert 205: (void)signal(SIGINT, SIG_IGN);
206: }
1.20 millert 207: if (i >= 4)
208: fputc('\n', stderr);
1.48 ! deraadt 209: pfd = open(_PATH_MASTERPASSWD, O_RDONLY|O_CLOEXEC);
1.39 okan 210: if (pfd == -1)
1.17 millert 211: pw_error(_PATH_MASTERPASSWD, 1, 1);
212:
1.43 deraadt 213: /* Copy the passwd file to the lock file, updating pw. */
214: pw_copy(pfd, tfd, pw, opw);
1.2 deraadt 215:
1.43 deraadt 216: /* If username changed we need to rebuild the entire db. */
217: arg = !strcmp(opw->pw_name, pw->pw_name) ? pw->pw_name : NULL;
1.1 deraadt 218:
1.43 deraadt 219: /* Now finish the passwd file update. */
220: if (pw_mkdb(arg, 0) == -1)
221: pw_error(NULL, 0, 1);
1.1 deraadt 222: exit(0);
223: }
224:
225: void
1.23 deraadt 226: baduser(void)
1.1 deraadt 227: {
228:
229: errx(1, "%s", strerror(EACCES));
1.14 millert 230: }
231:
1.32 deraadt 232: /* ARGSUSED */
1.14 millert 233: void
1.23 deraadt 234: kbintr(int signo)
1.17 millert 235: {
1.44 deraadt 236: dprintf(STDERR_FILENO, "\n%s: %s unchanged\n",
237: __progname, _PATH_MASTERPASSWD);
1.17 millert 238: _exit(1);
1.1 deraadt 239: }
240:
241: void
1.23 deraadt 242: usage(void)
1.1 deraadt 243: {
244:
1.34 jmc 245: (void)fprintf(stderr, "usage: %s [-s newshell] [user]\n", __progname);
1.36 sobrado 246: (void)fprintf(stderr, " %s -a list\n", __progname);
1.1 deraadt 247: exit(1);
248: }