1 /* $OpenBSD: chpass.c,v 1.44 2017/12/08 17:04:15 deraadt Exp $ */ 2 /* $NetBSD: chpass.c,v 1.8 1996/05/15 21:50:43 jtc Exp $ */ 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. 16 * 3. Neither the name of the University nor the names of its contributors 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 33 #include <sys/resource.h> 34 #include <sys/stat.h> 35 #include <sys/time.h> 36 #include <sys/uio.h> 37 38 #include <err.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <paths.h> 42 #include <pwd.h> 43 #include <signal.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 #include <util.h> 49 50 #include "chpass.h" 51 52 extern char *__progname; 53 54 enum { NEWSH, LOADENTRY, EDITENTRY } op; 55 uid_t uid; 56 57 void baduser(void); 58 void kbintr(int); 59 void usage(void); 60 61 int 62 main(int argc, char *argv[]) 63 { 64 struct passwd *pw = NULL, *opw = NULL, lpw; 65 int i, ch, pfd, tfd, dfd; 66 char *tz, *arg = NULL; 67 sigset_t fullset; 68 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 } 75 76 op = EDITENTRY; 77 while ((ch = getopt(argc, argv, "a:s:")) != -1) 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: 99 pw = getpwuid_shadow(uid); 100 if (!pw) 101 errx(1, "unknown user: uid %u", uid); 102 break; 103 case 1: 104 pw = getpwnam_shadow(*argv); 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) { 115 if (argc != 0) 116 errx(1, "option -a does not accept user argument"); 117 if (uid) 118 baduser(); 119 pw = &lpw; 120 if (!pw_scan(arg, pw, NULL)) 121 exit(1); 122 opw = getpwnam_shadow(pw->pw_name); 123 } 124 if (opw == NULL && (opw = pw_dup(pw)) == NULL) 125 err(1, NULL); 126 127 /* Edit the user passwd information if requested. */ 128 if (op == EDITENTRY) { 129 char tempname[] = _PATH_VARTMP "pw.XXXXXXXXXX"; 130 int edit_status; 131 132 if ((pw = pw_dup(pw)) == NULL) 133 pw_error(NULL, 1, 1); 134 dfd = mkostemp(tempname, O_CLOEXEC); 135 if (dfd == -1) 136 pw_error(tempname, 1, 1); 137 display(tempname, dfd, pw); 138 139 if (pledge("stdio rpath wpath cpath id proc exec", 140 NULL) == -1) 141 err(1, "pledge"); 142 143 edit_status = edit(tempname, pw); 144 close(dfd); 145 unlink(tempname); 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 } 158 } 159 160 if (op == NEWSH) { 161 if (pledge("stdio rpath wpath cpath id proc exec", 162 NULL) == -1) 163 err(1, "pledge"); 164 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); 170 } 171 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 178 if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1) 179 err(1, "pledge"); 180 181 /* Get the passwd lock file and open the passwd file for reading. */ 182 pw_init(); 183 for (i = 1; (tfd = pw_lock(0)) == -1; i++) { 184 if (i == 4) 185 (void)fputs("Attempting to lock password file, " 186 "please wait or press ^C to abort", stderr); 187 (void)signal(SIGINT, kbintr); 188 if (i % 16 == 0) 189 fputc('.', stderr); 190 usleep(250000); 191 (void)signal(SIGINT, SIG_IGN); 192 } 193 if (i >= 4) 194 fputc('\n', stderr); 195 pfd = open(_PATH_MASTERPASSWD, O_RDONLY|O_CLOEXEC, 0); 196 if (pfd == -1) 197 pw_error(_PATH_MASTERPASSWD, 1, 1); 198 199 /* Copy the passwd file to the lock file, updating pw. */ 200 pw_copy(pfd, tfd, pw, opw); 201 202 /* If username changed we need to rebuild the entire db. */ 203 arg = !strcmp(opw->pw_name, pw->pw_name) ? pw->pw_name : NULL; 204 205 /* Now finish the passwd file update. */ 206 if (pw_mkdb(arg, 0) == -1) 207 pw_error(NULL, 0, 1); 208 exit(0); 209 } 210 211 void 212 baduser(void) 213 { 214 215 errx(1, "%s", strerror(EACCES)); 216 } 217 218 /* ARGSUSED */ 219 void 220 kbintr(int signo) 221 { 222 dprintf(STDERR_FILENO, "\n%s: %s unchanged\n", 223 __progname, _PATH_MASTERPASSWD); 224 _exit(1); 225 } 226 227 void 228 usage(void) 229 { 230 231 (void)fprintf(stderr, "usage: %s [-s newshell] [user]\n", __progname); 232 (void)fprintf(stderr, " %s -a list\n", __progname); 233 exit(1); 234 } 235