1 /* $NetBSD: chpass.c,v 1.18 1999/02/08 22:21:44 mjl 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.18 1999/02/08 22:21:44 mjl 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 #ifdef YP 191 if (use_yp) 192 pw = ypgetpwnam(username); 193 else 194 #endif /* YP */ 195 pw = getpwnam(username); 196 if (pw == NULL) 197 errx(1, "unknown user: %s", username); 198 if (uid && uid != pw->pw_uid) 199 baduser(); 200 } else { 201 #ifdef YP 202 if (use_yp) 203 pw = ypgetpwuid(uid); 204 else 205 #endif /* YP */ 206 pw = getpwuid(uid); 207 if (pw == NULL) 208 errx(1, "unknown user: uid %u\n", uid); 209 } 210 211 /* Make a copy for later verification */ 212 old_pw = *pw; 213 old_pw.pw_gecos = strdup(old_pw.pw_gecos); 214 } 215 216 if (op == NEWSH) { 217 /* protect p_shell -- it thinks NULL is /bin/sh */ 218 if (!arg[0]) 219 usage(); 220 if (p_shell(arg, pw, NULL)) 221 (*Pw_error)(NULL, 0, 1); 222 } 223 224 if (op == LOADENTRY) { 225 if (uid) 226 baduser(); 227 pw = &lpw; 228 if (!pw_scan(arg, pw, NULL)) 229 exit(1); 230 } 231 232 /* Edit the user passwd information if requested. */ 233 if (op == EDITENTRY) { 234 dfd = mkstemp(tempname); 235 if (dfd < 0 || fcntl(dfd, F_SETFD, 1) < 0) 236 (*Pw_error)(tempname, 1, 1); 237 display(tempname, dfd, pw); 238 edit(tempname, pw); 239 (void)unlink(tempname); 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, _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, _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() < 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 #ifdef YP 300 (void)fprintf(stderr, "usage: chpass [-a list] [-s shell] [-l]%s [user]\n", use_yp?" [-y]":""); 301 #else 302 (void)fprintf(stderr, "usage: chpass [-a list] [-s shell] [user]\n"); 303 #endif 304 exit(1); 305 } 306