1 /* $NetBSD: chpass.c,v 1.27 2002/11/16 15:59:26 itojun 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.27 2002/11/16 15:59:26 itojun 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 static char tempname[] = "/etc/pw.XXXXXX"; 70 uid_t uid; 71 int use_yp; 72 73 void (*Pw_error) __P((const char *, int, int)); 74 75 #ifdef YP 76 extern int _yp_check __P((char **)); /* buried deep inside libc */ 77 #endif 78 79 void baduser __P((void)); 80 void cleanup __P((void)); 81 int main __P((int, char **)); 82 void usage __P((void)); 83 84 int 85 main(argc, argv) 86 int argc; 87 char **argv; 88 { 89 enum { NEWSH, LOADENTRY, EDITENTRY } op; 90 struct passwd *pw, lpw, old_pw; 91 int ch, dfd, pfd, tfd; 92 #ifdef YP 93 int yflag; 94 #endif 95 char *arg, *username = NULL; 96 97 #ifdef __GNUC__ 98 pw = NULL; /* XXX gcc -Wuninitialized */ 99 arg = NULL; 100 #endif 101 #ifdef YP 102 use_yp = _yp_check(NULL); 103 #endif 104 105 op = EDITENTRY; 106 while ((ch = getopt(argc, argv, "a:s:ly")) != -1) 107 switch (ch) { 108 case 'a': 109 op = LOADENTRY; 110 arg = optarg; 111 break; 112 case 's': 113 op = NEWSH; 114 arg = optarg; 115 break; 116 case 'l': 117 use_yp = 0; 118 break; 119 case 'y': 120 #ifdef YP 121 if (!use_yp) 122 errx(1, "YP not in use."); 123 yflag = 1; 124 #else 125 errx(1, "YP support not compiled in."); 126 #endif 127 break; 128 default: 129 usage(); 130 } 131 argc -= optind; 132 argv += optind; 133 134 uid = getuid(); 135 switch (argc) { 136 case 0: 137 /* nothing */ 138 break; 139 140 case 1: 141 username = argv[0]; 142 break; 143 144 default: 145 usage(); 146 } 147 148 #ifdef YP 149 /* 150 * We need to determine if we _really_ want to use YP. 151 * If we defaulted to YP (i.e. were not given the -y flag), 152 * and the master is not running rpc.yppasswdd, we check 153 * to see if the user exists in the local passwd database. 154 * If so, we use it, otherwise we error out. 155 */ 156 if (use_yp && yflag == 0) { 157 if (check_yppasswdd()) { 158 /* 159 * We weren't able to contact rpc.yppasswdd. 160 * Check to see if we're in the local 161 * password database. If we are, use it. 162 */ 163 if (username != NULL) 164 pw = getpwnam(username); 165 else 166 pw = getpwuid(uid); 167 if (pw != NULL) 168 use_yp = 0; 169 else { 170 warnx("master YP server not running yppasswd" 171 " daemon."); 172 errx(1, "Can't change password."); 173 } 174 } 175 } 176 #endif 177 178 #ifdef YP 179 if (use_yp) 180 Pw_error = yppw_error; 181 else 182 #endif 183 Pw_error = pw_error; 184 185 #ifdef YP 186 if (op == LOADENTRY && use_yp) 187 errx(1, "cannot load entry using YP.\n" 188 "\tUse the -l flag to load local."); 189 #endif 190 191 if (op == EDITENTRY || op == NEWSH) { 192 if (username != NULL) { 193 pw = getpwnam(username); 194 if (pw == NULL) 195 errx(1, "unknown user: %s", username); 196 if (uid && uid != pw->pw_uid) 197 baduser(); 198 } else { 199 pw = getpwuid(uid); 200 if (pw == NULL) 201 errx(1, "unknown user: uid %u", uid); 202 } 203 204 /* Make a copy for later verification */ 205 old_pw = *pw; 206 old_pw.pw_gecos = strdup(old_pw.pw_gecos); 207 if (!old_pw.pw_gecos) { 208 err(1, "strdup"); 209 /*NOTREACHED*/ 210 } 211 } 212 213 if (op == NEWSH) { 214 /* protect p_shell -- it thinks NULL is /bin/sh */ 215 if (!arg[0]) 216 usage(); 217 if (p_shell(arg, pw, NULL)) 218 (*Pw_error)(NULL, 0, 1); 219 } 220 221 if (op == LOADENTRY) { 222 if (uid) 223 baduser(); 224 pw = &lpw; 225 if (!pw_scan(arg, pw, NULL)) 226 exit(1); 227 } 228 229 /* Edit the user passwd information if requested. */ 230 if (op == EDITENTRY) { 231 dfd = mkstemp(tempname); 232 if (dfd < 0 || fcntl(dfd, F_SETFD, 1) < 0) 233 (*Pw_error)(tempname, 1, 1); 234 if (atexit(cleanup)) { 235 cleanup(); 236 errx(1, "couldn't register cleanup"); 237 } 238 display(tempname, dfd, pw); 239 edit(tempname, pw); 240 } 241 242 #ifdef YP 243 if (use_yp) { 244 if (pw_yp(pw, uid)) 245 yppw_error((char *)NULL, 0, 1); 246 else 247 exit(0); 248 /* Will not exit from this if. */ 249 } 250 #endif /* YP */ 251 252 253 /* 254 * Get the passwd lock file and open the passwd file for 255 * reading. 256 */ 257 pw_init(); 258 tfd = pw_lock(0); 259 if (tfd < 0) { 260 if (errno != EEXIST) 261 err(1, "%s", _PATH_MASTERPASSWD_LOCK); 262 warnx("The passwd file is busy, waiting..."); 263 tfd = pw_lock(10); 264 if (tfd < 0) { 265 if (errno != EEXIST) 266 err(1, "%s", _PATH_MASTERPASSWD_LOCK); 267 errx(1, "The passwd file is still busy, " 268 "try again later."); 269 } 270 } 271 if (fcntl(tfd, F_SETFD, 1) < 0) 272 pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1); 273 274 pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); 275 if (pfd < 0 || fcntl(pfd, F_SETFD, 1) < 0) 276 pw_error(_PATH_MASTERPASSWD, 1, 1); 277 278 /* Copy the passwd file to the lock file, updating pw. */ 279 pw_copy(pfd, tfd, pw, (op == LOADENTRY) ? NULL : &old_pw); 280 281 /* Now finish the passwd file update. */ 282 if (pw_mkdb(username, 0) < 0) 283 pw_error(NULL, 0, 1); 284 285 exit(0); 286 } 287 288 void 289 baduser() 290 { 291 292 errx(1, "%s", strerror(EACCES)); 293 } 294 295 void 296 usage() 297 { 298 299 (void)fprintf(stderr, 300 "usage: %s [-a list] [-s shell] [-l] [user]\n" 301 " %s [-a list] [-s shell] [-y] [user]\n", 302 getprogname(), getprogname()); 303 exit(1); 304 } 305 306 void 307 cleanup() 308 { 309 310 (void)unlink(tempname); 311 } 312