1 /* $NetBSD: chpass.c,v 1.20 2000/10/27 16:16:03 phil Exp $ */ 2 3 /*- 4 * Copyright (c) 1988, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 __COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\n\ 39 The Regents of the University of California. All rights reserved.\n"); 40 #endif /* not lint */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)chpass.c 8.4 (Berkeley) 4/2/94"; 45 #else 46 __RCSID("$NetBSD: chpass.c,v 1.20 2000/10/27 16:16:03 phil Exp $"); 47 #endif 48 #endif /* not lint */ 49 50 #include <sys/param.h> 51 #include <sys/stat.h> 52 #include <sys/time.h> 53 #include <sys/resource.h> 54 55 #include <ctype.h> 56 #include <err.h> 57 #include <errno.h> 58 #include <fcntl.h> 59 #include <pwd.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 #include <util.h> 65 66 #include "chpass.h" 67 #include "pathnames.h" 68 69 extern char *__progname; /* from crt0.o */ 70 71 char *tempname; 72 uid_t uid; 73 int use_yp; 74 int yflag; 75 76 void (*Pw_error) __P((const char *, int, int)); 77 78 #ifdef YP 79 extern int _yp_check __P((char **)); /* buried deep inside libc */ 80 #endif 81 82 void baduser __P((void)); 83 int main __P((int, char **)); 84 void usage __P((void)); 85 86 int 87 main(argc, argv) 88 int argc; 89 char **argv; 90 { 91 enum { NEWSH, LOADENTRY, EDITENTRY } op; 92 struct passwd *pw, lpw, old_pw; 93 int ch, pfd, tfd, dfd; 94 char *arg, *username = NULL, tempname[] = "/etc/pw.XXXXXX"; 95 96 #ifdef __GNUC__ 97 pw = NULL; /* XXX gcc -Wuninitialized */ 98 arg = NULL; 99 #endif 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 case 'l': 116 use_yp = 0; 117 break; 118 case 'y': 119 #ifdef YP 120 if (!use_yp) 121 errx(1, "YP not in use."); 122 yflag = 1; 123 #else 124 errx(1, "YP support not compiled in."); 125 #endif 126 break; 127 default: 128 usage(); 129 } 130 argc -= optind; 131 argv += optind; 132 133 uid = getuid(); 134 switch (argc) { 135 case 0: 136 /* nothing */ 137 break; 138 139 case 1: 140 username = argv[0]; 141 break; 142 143 default: 144 usage(); 145 } 146 147 #ifdef YP 148 /* 149 * We need to determine if we _really_ want to use YP. 150 * If we defaulted to YP (i.e. were not given the -y flag), 151 * and the master is not running rpc.yppasswdd, we check 152 * to see if the user exists in the local passwd database. 153 * If so, we use it, otherwise we error out. 154 */ 155 if (use_yp && yflag == 0) { 156 if (check_yppasswdd()) { 157 /* 158 * We weren't able to contact rpc.yppasswdd. 159 * Check to see if we're in the local 160 * password database. If we are, use it. 161 */ 162 if (username != NULL) 163 pw = getpwnam(username); 164 else 165 pw = getpwuid(uid); 166 if (pw != NULL) 167 use_yp = 0; 168 else { 169 errx(1, "master YP server not running yppasswd daemon.\n\t%s\n", 170 "Can't change password."); 171 } 172 } 173 } 174 #endif 175 176 #ifdef YP 177 if (use_yp) 178 Pw_error = yppw_error; 179 else 180 #endif 181 Pw_error = pw_error; 182 183 #ifdef YP 184 if (op == LOADENTRY && use_yp) 185 errx(1, "cannot load entry using YP.\n\tUse the -l flag to load local."); 186 #endif 187 188 if (op == EDITENTRY || op == NEWSH) { 189 if (username != NULL) { 190 pw = getpwnam(username); 191 if (pw == NULL) 192 errx(1, "unknown user: %s", username); 193 if (uid && uid != pw->pw_uid) 194 baduser(); 195 } else { 196 pw = getpwuid(uid); 197 if (pw == NULL) 198 errx(1, "unknown user: uid %u\n", uid); 199 } 200 201 /* Make a copy for later verification */ 202 old_pw = *pw; 203 old_pw.pw_gecos = strdup(old_pw.pw_gecos); 204 } 205 206 if (op == NEWSH) { 207 /* protect p_shell -- it thinks NULL is /bin/sh */ 208 if (!arg[0]) 209 usage(); 210 if (p_shell(arg, pw, NULL)) 211 (*Pw_error)(NULL, 0, 1); 212 } 213 214 if (op == LOADENTRY) { 215 if (uid) 216 baduser(); 217 pw = &lpw; 218 if (!pw_scan(arg, pw, NULL)) 219 exit(1); 220 } 221 222 /* Edit the user passwd information if requested. */ 223 if (op == EDITENTRY) { 224 dfd = mkstemp(tempname); 225 if (dfd < 0 || fcntl(dfd, F_SETFD, 1) < 0) 226 (*Pw_error)(tempname, 1, 1); 227 display(tempname, dfd, pw); 228 edit(tempname, pw); 229 (void)unlink(tempname); 230 } 231 232 #ifdef YP 233 if (use_yp) { 234 if (pw_yp(pw, uid)) 235 yppw_error((char *)NULL, 0, 1); 236 else 237 exit(0); 238 /* Will not exit from this if. */ 239 } 240 #endif /* YP */ 241 242 243 /* 244 * Get the passwd lock file and open the passwd file for 245 * reading. 246 */ 247 pw_init(); 248 tfd = pw_lock(0); 249 if (tfd < 0) { 250 if (errno != EEXIST) 251 err(1, "%s", _PATH_MASTERPASSWD_LOCK); 252 warnx("The passwd file is busy, waiting..."); 253 tfd = pw_lock(10); 254 if (tfd < 0) { 255 if (errno != EEXIST) 256 err(1, "%s", _PATH_MASTERPASSWD_LOCK); 257 errx(1, "The passwd file is still busy, " 258 "try again later."); 259 } 260 } 261 if (fcntl(tfd, F_SETFD, 1) < 0) 262 pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1); 263 264 pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); 265 if (pfd < 0 || fcntl(pfd, F_SETFD, 1) < 0) 266 pw_error(_PATH_MASTERPASSWD, 1, 1); 267 268 /* Copy the passwd file to the lock file, updating pw. */ 269 pw_copy(pfd, tfd, pw, (op == LOADENTRY) ? NULL : &old_pw); 270 271 /* Now finish the passwd file update. */ 272 if (pw_mkdb() < 0) 273 pw_error(NULL, 0, 1); 274 275 exit(0); 276 } 277 278 void 279 baduser() 280 { 281 282 errx(1, "%s", strerror(EACCES)); 283 } 284 285 void 286 usage() 287 { 288 289 #ifdef YP 290 (void)fprintf(stderr, "usage: chpass [-a list] [-s shell] [-l]%s [user]\n", use_yp?" [-y]":""); 291 #else 292 (void)fprintf(stderr, "usage: chpass [-a list] [-s shell] [user]\n"); 293 #endif 294 exit(1); 295 } 296