Annotation of src/usr.bin/chpass/chpass.c, Revision 1.44
1.44 ! deraadt 1: /* $OpenBSD: chpass.c,v 1.43 2015/11/26 19:01:47 deraadt 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:
139: if (pledge("stdio rpath wpath cpath id proc exec",
140: NULL) == -1)
141: err(1, "pledge");
142:
1.26 millert 143: edit_status = edit(tempname, pw);
1.24 millert 144: close(dfd);
145: unlink(tempname);
1.26 millert 146:
147: switch (edit_status) {
148: case EDIT_OK:
149: break;
150: case EDIT_NOCHANGE:
151: pw_error(NULL, 0, 0);
152: break;
153: case EDIT_ERROR:
154: default:
155: pw_error(tempname, 1, 1);
156: break;
157: }
1.31 wilfried 158: }
159:
160: if (op == NEWSH) {
1.43 deraadt 161: if (pledge("stdio rpath wpath cpath id proc exec",
162: NULL) == -1)
163: err(1, "pledge");
164:
1.31 wilfried 165: /* protect p_shell -- it thinks NULL is /bin/sh */
166: if (!arg[0])
167: usage();
168: if (p_shell(arg, pw, NULL))
169: pw_error(NULL, 0, 1);
1.1 deraadt 170: }
1.2 deraadt 171:
1.17 millert 172: /* Drop user's real uid and block all signals to avoid a DoS. */
173: setuid(0);
174: sigfillset(&fullset);
175: sigdelset(&fullset, SIGINT);
176: sigprocmask(SIG_BLOCK, &fullset, NULL);
177:
1.43 deraadt 178: if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1)
179: err(1, "pledge");
180:
1.17 millert 181: /* Get the passwd lock file and open the passwd file for reading. */
182: pw_init();
1.20 millert 183: for (i = 1; (tfd = pw_lock(0)) == -1; i++) {
184: if (i == 4)
1.38 schwarze 185: (void)fputs("Attempting to lock password file, "
1.20 millert 186: "please wait or press ^C to abort", stderr);
187: (void)signal(SIGINT, kbintr);
188: if (i % 16 == 0)
1.17 millert 189: fputc('.', stderr);
1.20 millert 190: usleep(250000);
1.17 millert 191: (void)signal(SIGINT, SIG_IGN);
192: }
1.20 millert 193: if (i >= 4)
194: fputc('\n', stderr);
1.39 okan 195: pfd = open(_PATH_MASTERPASSWD, O_RDONLY|O_CLOEXEC, 0);
196: if (pfd == -1)
1.17 millert 197: pw_error(_PATH_MASTERPASSWD, 1, 1);
198:
1.43 deraadt 199: /* Copy the passwd file to the lock file, updating pw. */
200: pw_copy(pfd, tfd, pw, opw);
1.2 deraadt 201:
1.43 deraadt 202: /* If username changed we need to rebuild the entire db. */
203: arg = !strcmp(opw->pw_name, pw->pw_name) ? pw->pw_name : NULL;
1.1 deraadt 204:
1.43 deraadt 205: /* Now finish the passwd file update. */
206: if (pw_mkdb(arg, 0) == -1)
207: pw_error(NULL, 0, 1);
1.1 deraadt 208: exit(0);
209: }
210:
211: void
1.23 deraadt 212: baduser(void)
1.1 deraadt 213: {
214:
215: errx(1, "%s", strerror(EACCES));
1.14 millert 216: }
217:
1.32 deraadt 218: /* ARGSUSED */
1.14 millert 219: void
1.23 deraadt 220: kbintr(int signo)
1.17 millert 221: {
1.44 ! deraadt 222: dprintf(STDERR_FILENO, "\n%s: %s unchanged\n",
! 223: __progname, _PATH_MASTERPASSWD);
1.17 millert 224: _exit(1);
1.1 deraadt 225: }
226:
227: void
1.23 deraadt 228: usage(void)
1.1 deraadt 229: {
230:
1.34 jmc 231: (void)fprintf(stderr, "usage: %s [-s newshell] [user]\n", __progname);
1.36 sobrado 232: (void)fprintf(stderr, " %s -a list\n", __progname);
1.1 deraadt 233: exit(1);
234: }