1 /* $OpenBSD: chpass.c,v 1.48 2021/10/24 21:24:16 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 (unveil(_PATH_BSHELL, "x") == -1) 140 err(1, "unveil %s", _PATH_BSHELL); 141 if (unveil(_PATH_SHELLS, "r") == -1) 142 err(1, "unveil %s", _PATH_SHELLS); 143 if (unveil(tempname, "rc") == -1) 144 err(1, "unveil %s", tempname); 145 if (pledge("stdio rpath wpath cpath id proc exec unveil", 146 NULL) == -1) 147 err(1, "pledge"); 148 149 edit_status = edit(tempname, pw); 150 close(dfd); 151 unlink(tempname); 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 } 164 } 165 166 if (op == NEWSH) { 167 if (unveil(_PATH_SHELLS, "r") == -1) 168 err(1, "unveil %s", _PATH_SHELLS); 169 if (pledge("stdio rpath wpath cpath id proc exec unveil", 170 NULL) == -1) 171 err(1, "pledge"); 172 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); 178 } 179 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 186 if (unveil(_PATH_MASTERPASSWD_LOCK, "rwc") == -1) 187 err(1, "unveil %s", _PATH_MASTERPASSWD_LOCK); 188 if (unveil(_PATH_MASTERPASSWD, "r") == -1) 189 err(1, "unveil %s", _PATH_MASTERPASSWD); 190 if (unveil(_PATH_PWD_MKDB, "x") == -1) 191 err(1, "unveil %s", _PATH_PWD_MKDB); 192 if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1) 193 err(1, "pledge"); 194 195 /* Get the passwd lock file and open the passwd file for reading. */ 196 pw_init(); 197 for (i = 1; (tfd = pw_lock(0)) == -1; i++) { 198 if (i == 4) 199 (void)fputs("Attempting to lock password file, " 200 "please wait or press ^C to abort", stderr); 201 (void)signal(SIGINT, kbintr); 202 if (i % 16 == 0) 203 fputc('.', stderr); 204 usleep(250000); 205 (void)signal(SIGINT, SIG_IGN); 206 } 207 if (i >= 4) 208 fputc('\n', stderr); 209 pfd = open(_PATH_MASTERPASSWD, O_RDONLY|O_CLOEXEC); 210 if (pfd == -1) 211 pw_error(_PATH_MASTERPASSWD, 1, 1); 212 213 /* Copy the passwd file to the lock file, updating pw. */ 214 pw_copy(pfd, tfd, pw, opw); 215 216 /* If username changed we need to rebuild the entire db. */ 217 arg = !strcmp(opw->pw_name, pw->pw_name) ? pw->pw_name : NULL; 218 219 /* Now finish the passwd file update. */ 220 if (pw_mkdb(arg, 0) == -1) 221 pw_error(NULL, 0, 1); 222 exit(0); 223 } 224 225 void 226 baduser(void) 227 { 228 229 errx(1, "%s", strerror(EACCES)); 230 } 231 232 /* ARGSUSED */ 233 void 234 kbintr(int signo) 235 { 236 dprintf(STDERR_FILENO, "\n%s: %s unchanged\n", 237 __progname, _PATH_MASTERPASSWD); 238 _exit(1); 239 } 240 241 void 242 usage(void) 243 { 244 245 (void)fprintf(stderr, "usage: %s [-s newshell] [user]\n", __progname); 246 (void)fprintf(stderr, " %s -a list\n", __progname); 247 exit(1); 248 } 249