1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (c) 1988, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * Copyright (c) 2002 Networks Associates Technology, Inc. 7 * All rights reserved. 8 * 9 * Portions of this software were developed for the FreeBSD Project by 10 * ThinkSec AS and NAI Labs, the Security Research Division of Network 11 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 12 * ("CBOSS"), as part of the DARPA CHATS research program. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 3. All advertising materials mentioning features or use of this software 23 * must display the following acknowledgement: 24 * This product includes software developed by the University of 25 * California, Berkeley and its contributors. 26 * 4. Neither the name of the University nor the names of its contributors 27 * may be used to endorse or promote products derived from this software 28 * without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 */ 42 43 #if 0 44 #ifndef lint 45 static const char copyright[] = 46 "@(#) Copyright (c) 1988, 1993, 1994\n\ 47 The Regents of the University of California. All rights reserved.\n"; 48 #endif /* not lint */ 49 50 #ifndef lint 51 static char sccsid[] = "@(#)chpass.c 8.4 (Berkeley) 4/2/94"; 52 #endif /* not lint */ 53 #endif 54 #include <sys/cdefs.h> 55 __FBSDID("$FreeBSD$"); 56 57 #include <sys/param.h> 58 59 #include <err.h> 60 #include <errno.h> 61 #include <pwd.h> 62 #include <stdio.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <unistd.h> 66 #ifdef YP 67 #include <ypclnt.h> 68 #endif 69 70 #include <pw_scan.h> 71 #include <libutil.h> 72 73 #include "chpass.h" 74 75 int master_mode; 76 77 static void baduser(void); 78 static void usage(void); 79 80 int 81 main(int argc, char *argv[]) 82 { 83 enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op; 84 struct passwd lpw, *old_pw, *pw; 85 int ch, pfd, tfd; 86 const char *password; 87 char *arg = NULL, *cryptpw; 88 uid_t uid; 89 #ifdef YP 90 struct ypclnt *ypclnt; 91 const char *yp_domain = NULL, *yp_host = NULL; 92 #endif 93 94 pw = old_pw = NULL; 95 op = EDITENTRY; 96 #ifdef YP 97 while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1) 98 #else 99 while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1) 100 #endif 101 switch (ch) { 102 case 'a': 103 op = LOADENTRY; 104 arg = optarg; 105 break; 106 case 's': 107 op = NEWSH; 108 arg = optarg; 109 break; 110 case 'p': 111 op = NEWPW; 112 arg = optarg; 113 break; 114 case 'e': 115 op = NEWEXP; 116 arg = optarg; 117 break; 118 #ifdef YP 119 case 'd': 120 yp_domain = optarg; 121 break; 122 case 'h': 123 yp_host = optarg; 124 break; 125 case 'l': 126 case 'o': 127 case 'y': 128 /* compatibility */ 129 break; 130 #endif 131 case '?': 132 default: 133 usage(); 134 } 135 136 argc -= optind; 137 argv += optind; 138 139 if (argc > 1) 140 usage(); 141 142 uid = getuid(); 143 144 if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) { 145 if (argc == 0) { 146 if ((pw = getpwuid(uid)) == NULL) 147 errx(1, "unknown user: uid %lu", 148 (unsigned long)uid); 149 } else { 150 if ((pw = getpwnam(*argv)) == NULL) 151 errx(1, "unknown user: %s", *argv); 152 if (uid != 0 && uid != pw->pw_uid) 153 baduser(); 154 } 155 156 /* Make a copy for later verification */ 157 if ((pw = pw_dup(pw)) == NULL || 158 (old_pw = pw_dup(pw)) == NULL) 159 err(1, "pw_dup"); 160 } 161 162 #ifdef YP 163 if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) { 164 ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host); 165 master_mode = (ypclnt != NULL && 166 ypclnt_connect(ypclnt) != -1 && 167 ypclnt_havepasswdd(ypclnt) == 1); 168 ypclnt_free(ypclnt); 169 } else 170 #endif 171 master_mode = (uid == 0); 172 173 if (op == NEWSH) { 174 /* protect p_shell -- it thinks NULL is /bin/sh */ 175 if (!arg[0]) 176 usage(); 177 if (p_shell(arg, pw, (ENTRY *)NULL) == -1) 178 exit(1); 179 } 180 181 if (op == NEWEXP) { 182 if (uid) /* only root can change expire */ 183 baduser(); 184 if (p_expire(arg, pw, (ENTRY *)NULL) == -1) 185 exit(1); 186 } 187 188 if (op == LOADENTRY) { 189 if (uid) 190 baduser(); 191 pw = &lpw; 192 old_pw = NULL; 193 if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) 194 exit(1); 195 } 196 197 if (op == NEWPW) { 198 if (uid) 199 baduser(); 200 201 if (strchr(arg, ':')) 202 errx(1, "invalid format for password"); 203 pw->pw_passwd = arg; 204 } 205 206 if (op == EDITENTRY) { 207 /* 208 * We don't really need pw_*() here, but pw_edit() (used 209 * by edit()) is just too useful... 210 */ 211 if (pw_init(NULL, NULL)) 212 err(1, "pw_init()"); 213 if ((tfd = pw_tmp(-1)) == -1) { 214 pw_fini(); 215 err(1, "pw_tmp()"); 216 } 217 free(pw); 218 pw = edit(pw_tempname(), old_pw); 219 pw_fini(); 220 if (pw == NULL) 221 err(1, "edit()"); 222 /* 223 * pw_equal does not check for crypted passwords, so we 224 * should do it explicitly 225 */ 226 if (pw_equal(old_pw, pw) && 227 strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0) 228 errx(0, "user information unchanged"); 229 } 230 231 if (old_pw && !master_mode) { 232 password = getpass("Password: "); 233 cryptpw = crypt(password, old_pw->pw_passwd); 234 if (cryptpw == NULL || strcmp(cryptpw, old_pw->pw_passwd) != 0) 235 baduser(); 236 } else { 237 password = ""; 238 } 239 240 if (old_pw != NULL) 241 pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE); 242 switch (pw->pw_fields & _PWF_SOURCE) { 243 #ifdef YP 244 case _PWF_NIS: 245 ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host); 246 if (ypclnt == NULL) { 247 warnx("ypclnt_new failed"); 248 exit(1); 249 } 250 if (ypclnt_connect(ypclnt) == -1 || 251 ypclnt_passwd(ypclnt, pw, password) == -1) { 252 warnx("%s", ypclnt->error); 253 ypclnt_free(ypclnt); 254 exit(1); 255 } 256 ypclnt_free(ypclnt); 257 errx(0, "NIS user information updated"); 258 #endif /* YP */ 259 case 0: 260 case _PWF_FILES: 261 if (pw_init(NULL, NULL)) 262 err(1, "pw_init()"); 263 if ((pfd = pw_lock()) == -1) { 264 pw_fini(); 265 err(1, "pw_lock()"); 266 } 267 if ((tfd = pw_tmp(-1)) == -1) { 268 pw_fini(); 269 err(1, "pw_tmp()"); 270 } 271 if (pw_copy(pfd, tfd, pw, old_pw) == -1) { 272 pw_fini(); 273 err(1, "pw_copy"); 274 } 275 if (pw_mkdb(pw->pw_name) == -1) { 276 pw_fini(); 277 err(1, "pw_mkdb()"); 278 } 279 pw_fini(); 280 errx(0, "user information updated"); 281 break; 282 default: 283 errx(1, "unsupported passwd source"); 284 } 285 } 286 287 static void 288 baduser(void) 289 { 290 291 errx(1, "%s", strerror(EACCES)); 292 } 293 294 static void 295 usage(void) 296 { 297 298 (void)fprintf(stderr, 299 "usage: chpass%s %s [user]\n", 300 #ifdef YP 301 " [-d domain] [-h host]", 302 #else 303 "", 304 #endif 305 "[-a list] [-p encpass] [-s shell] [-e mmm dd yy]"); 306 exit(1); 307 } 308