1 /* $OpenBSD: chpass.c,v 1.20 2001/08/27 02:57:07 millert 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. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 static char copyright[] = 39 "@(#) Copyright (c) 1988, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 #if 0 45 static char sccsid[] = "@(#)chpass.c 8.4 (Berkeley) 4/2/94"; 46 #else 47 static char rcsid[] = "$OpenBSD: chpass.c,v 1.20 2001/08/27 02:57:07 millert Exp $"; 48 #endif 49 #endif /* not lint */ 50 51 #include <sys/param.h> 52 #include <sys/resource.h> 53 #include <sys/stat.h> 54 #include <sys/time.h> 55 #include <sys/uio.h> 56 57 #include <err.h> 58 #include <errno.h> 59 #include <fcntl.h> 60 #include <pwd.h> 61 #include <signal.h> 62 #include <stdio.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <unistd.h> 66 #include <util.h> 67 68 #include "chpass.h" 69 #include "pathnames.h" 70 71 char tempname[] = __CONCAT(_PATH_VARTMP,"pw.XXXXXXXX"); 72 enum { NEWSH, LOADENTRY, EDITENTRY } op; 73 uid_t uid; 74 75 extern char *__progname; 76 77 #ifdef YP 78 int use_yp; 79 int force_yp = 0; 80 extern struct passwd *ypgetpwnam(), *ypgetpwuid(); 81 int _yp_check __P((char **)); 82 int pw_yp __P((struct passwd *, uid_t)); 83 #endif 84 85 void baduser __P((void)); 86 void tempcleanup __P((void)); 87 void kbintr __P((int)); 88 void usage __P((void)); 89 90 int 91 main(argc, argv) 92 int argc; 93 char **argv; 94 { 95 struct passwd *pw, lpw; 96 int i, ch, pfd, tfd, dfd; 97 char *arg; 98 sigset_t fullset; 99 100 #ifdef YP 101 use_yp = _yp_check(NULL); 102 #endif 103 104 op = EDITENTRY; 105 while ((ch = getopt(argc, argv, "a:s:ly")) != -1) 106 switch(ch) { 107 case 'a': 108 op = LOADENTRY; 109 arg = optarg; 110 break; 111 case 's': 112 op = NEWSH; 113 arg = optarg; 114 break; 115 #ifdef YP 116 case 'l': 117 use_yp = 0; 118 break; 119 case 'y': 120 if (!use_yp) { 121 warnx("YP not in use."); 122 usage(); 123 } 124 force_yp = 1; 125 break; 126 #endif 127 case '?': 128 default: 129 usage(); 130 } 131 argc -= optind; 132 argv += optind; 133 134 #ifdef YP 135 if (op == LOADENTRY && use_yp) 136 errx(1, "cannot load entry using NIS.\n\tUse the -l flag to load local."); 137 #endif 138 uid = getuid(); 139 140 if (op == EDITENTRY || op == NEWSH) 141 switch(argc) { 142 case 0: 143 pw = getpwuid(uid); 144 #ifdef YP 145 if (pw && !force_yp) 146 use_yp = 0; 147 else if (use_yp) 148 pw = ypgetpwuid(uid); 149 #endif /* YP */ 150 if (!pw) 151 errx(1, "unknown user: uid %u\n", uid); 152 break; 153 case 1: 154 pw = getpwnam(*argv); 155 #ifdef YP 156 if (pw && !force_yp) 157 use_yp = 0; 158 else if (use_yp) 159 pw = ypgetpwnam(*argv); 160 #endif /* YP */ 161 if (!pw) 162 errx(1, "unknown user: %s", *argv); 163 if (uid && uid != pw->pw_uid) 164 baduser(); 165 break; 166 default: 167 usage(); 168 } 169 170 if (op == NEWSH) { 171 /* protect p_shell -- it thinks NULL is /bin/sh */ 172 if (!arg[0]) 173 usage(); 174 if (p_shell(arg, pw, NULL)) 175 pw_error(NULL, 0, 1); 176 } 177 178 if (op == LOADENTRY) { 179 if (uid) 180 baduser(); 181 pw = &lpw; 182 if (!pw_scan(arg, pw, NULL)) 183 exit(1); 184 } 185 186 /* Edit the user passwd information if requested. */ 187 if (op == EDITENTRY) { 188 dfd = mkstemp(tempname); 189 if (dfd == -1 || fcntl(dfd, F_SETFD, 1) == -1) 190 pw_error(tempname, 1, 1); 191 atexit(tempcleanup); 192 display(tempname, dfd, pw); 193 edit(tempname, pw); 194 } 195 196 /* Drop user's real uid and block all signals to avoid a DoS. */ 197 setuid(0); 198 sigfillset(&fullset); 199 sigdelset(&fullset, SIGINT); 200 sigprocmask(SIG_BLOCK, &fullset, NULL); 201 202 /* Get the passwd lock file and open the passwd file for reading. */ 203 pw_init(); 204 for (i = 1; (tfd = pw_lock(0)) == -1; i++) { 205 if (i == 4) 206 (void)fputs("Attempting lock password file, " 207 "please wait or press ^C to abort", stderr); 208 (void)signal(SIGINT, kbintr); 209 if (i % 16 == 0) 210 fputc('.', stderr); 211 usleep(250000); 212 (void)signal(SIGINT, SIG_IGN); 213 } 214 if (i >= 4) 215 fputc('\n', stderr); 216 pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); 217 if (pfd == -1 || fcntl(pfd, F_SETFD, 1) == -1) 218 pw_error(_PATH_MASTERPASSWD, 1, 1); 219 220 #ifdef YP 221 if (use_yp) { 222 if (pw_yp(pw, uid)) 223 pw_error(NULL, 0, 1); 224 else { 225 pw_abort(); 226 exit(0); 227 } 228 } else 229 #endif /* YP */ 230 { 231 /* Copy the passwd file to the lock file, updating pw. */ 232 pw_copy(pfd, tfd, pw); 233 234 /* Now finish the passwd file update. */ 235 if (pw_mkdb(pw->pw_name, 0) == -1) 236 pw_error(NULL, 0, 1); 237 } 238 239 exit(0); 240 } 241 242 void 243 baduser() 244 { 245 246 errx(1, "%s", strerror(EACCES)); 247 } 248 249 void 250 tempcleanup() 251 { 252 253 unlink(tempname); 254 } 255 256 void 257 kbintr(signo) 258 int signo; 259 { 260 struct iovec iv[5]; 261 262 iv[0].iov_base = "\n"; 263 iv[0].iov_len = 1; 264 iv[1].iov_base = __progname; 265 iv[1].iov_len = strlen(__progname); 266 iv[2].iov_base = ": "; 267 iv[2].iov_len = 2; 268 iv[3].iov_base = _PATH_MASTERPASSWD; 269 iv[3].iov_len = sizeof(_PATH_MASTERPASSWD) - 1; 270 iv[4].iov_base = " unchanged\n"; 271 iv[4].iov_len = 11; 272 writev(STDERR_FILENO, iv, 5); 273 274 if (op == EDITENTRY) 275 unlink(tempname); 276 277 _exit(1); 278 } 279 280 void 281 usage() 282 { 283 284 #ifdef YP 285 (void)fprintf(stderr, 286 "usage: %s [-l%s] [-a list] [-s newshell] [user]\n", 287 __progname, use_yp ? "y" : ""); 288 #else 289 (void)fprintf(stderr, "usage: %s [-a list] [-s newshell] [user]\n", 290 __progname); 291 #endif 292 exit(1); 293 } 294