1 /* $NetBSD: local_passwd.c,v 1.34 2010/03/02 16:19:13 gdt Exp $ */ 2 3 /*- 4 * Copyright (c) 1990, 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "from: @(#)local_passwd.c 8.3 (Berkeley) 4/2/94"; 36 #else 37 __RCSID("$NetBSD: local_passwd.c,v 1.34 2010/03/02 16:19:13 gdt Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <ctype.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <pwd.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <limits.h> 52 #include <time.h> 53 #include <unistd.h> 54 #include <util.h> 55 #include <login_cap.h> 56 #include <syslog.h> 57 58 #include "extern.h" 59 60 static uid_t uid; 61 62 static char * 63 getnewpasswd(struct passwd *pw, int min_pw_len) 64 { 65 int tries; 66 char *p, *t; 67 char buf[_PASSWORD_LEN+1], salt[_PASSWORD_LEN+1]; 68 char option[LINE_MAX], *key, *opt; 69 70 (void)printf("Changing local password for %s.\n", pw->pw_name); 71 72 if (uid && pw->pw_passwd[0] && 73 strcmp(crypt(getpass("Old password:"), pw->pw_passwd), 74 pw->pw_passwd)) { 75 errno = EACCES; 76 syslog(LOG_AUTH | LOG_NOTICE, 77 "user %s (UID %lu) failed to change the " 78 "local password of user %s: %m", 79 pw->pw_name, (unsigned long)uid, pw->pw_name); 80 pw_error(NULL, 1, 1); 81 } 82 83 for (buf[0] = '\0', tries = 0;;) { 84 p = getpass("New password:"); 85 if (!*p) { 86 (void)printf("Password unchanged.\n"); 87 pw_error(NULL, 0, 0); 88 } 89 if (min_pw_len > 0 && (int)strlen(p) < min_pw_len) { 90 (void) printf("Password is too short.\n"); 91 continue; 92 } 93 if (strlen(p) <= 5 && ++tries < 2) { 94 (void)printf("Please enter a longer password.\n"); 95 continue; 96 } 97 for (t = p; *t && islower((unsigned char)*t); ++t); 98 if (!*t && ++tries < 2) { 99 (void)printf("Please don't use an all-lower case " 100 "password.\nUnusual capitalization, " 101 "control characters or digits are " 102 "suggested.\n"); 103 continue; 104 } 105 (void)strlcpy(buf, p, sizeof(buf)); 106 if (!strcmp(buf, getpass("Retype new password:"))) 107 break; 108 (void)printf("Mismatch; try again, EOF to quit.\n"); 109 } 110 111 pw_getpwconf(option, sizeof(option), pw, "localcipher"); 112 opt = option; 113 key = strsep(&opt, ","); 114 if(pw_gensalt(salt, _PASSWORD_LEN, key, opt) == -1) { 115 warn("Couldn't generate salt"); 116 pw_error(NULL, 0, 0); 117 } 118 return(crypt(buf, salt)); 119 } 120 121 #ifdef USE_PAM 122 123 void 124 pwlocal_usage(const char *prefix) 125 { 126 127 (void) fprintf(stderr, "%s %s [-d files | -l] [user]\n", 128 prefix, getprogname()); 129 } 130 131 void 132 pwlocal_process(const char *username, int argc, char **argv) 133 { 134 struct passwd *pw; 135 struct passwd old_pw; 136 time_t old_change; 137 int pfd, tfd; 138 int min_pw_len = 0; 139 int pw_expiry = 0; 140 int ch; 141 #ifdef LOGIN_CAP 142 login_cap_t *lc; 143 #endif 144 145 while ((ch = getopt(argc, argv, "l")) != -1) { 146 switch (ch) { 147 case 'l': 148 /* 149 * Aborb the -l that may have gotten us here. 150 */ 151 break; 152 153 default: 154 usage(); 155 /* NOTREACHED */ 156 } 157 } 158 159 argc -= optind; 160 argv += optind; 161 162 switch (argc) { 163 case 0: 164 /* username already provided */ 165 break; 166 case 1: 167 username = argv[0]; 168 break; 169 default: 170 usage(); 171 /* NOTREACHED */ 172 } 173 174 if (!(pw = getpwnam(username))) 175 errx(1, "unknown user %s", username); 176 177 uid = getuid(); 178 if (uid && uid != pw->pw_uid) 179 errx(1, "%s", strerror(EACCES)); 180 181 /* Save the old pw information for comparing on pw_copy(). */ 182 old_pw = *pw; 183 184 /* 185 * Get class restrictions for this user, then get the new password. 186 */ 187 #ifdef LOGIN_CAP 188 if((lc = login_getclass(pw->pw_class)) != NULL) { 189 min_pw_len = (int) login_getcapnum(lc, "minpasswordlen", 0, 0); 190 pw_expiry = (int) login_getcaptime(lc, "passwordtime", 0, 0); 191 login_close(lc); 192 } 193 #endif 194 195 pw->pw_passwd = getnewpasswd(pw, min_pw_len); 196 old_change = pw->pw_change; 197 pw->pw_change = pw_expiry ? pw_expiry + time(NULL) : 0; 198 199 /* 200 * Now that the user has given us a new password, let us 201 * change the database. 202 */ 203 pw_init(); 204 tfd = pw_lock(0); 205 if (tfd < 0) { 206 warnx ("The passwd file is busy, waiting..."); 207 tfd = pw_lock(10); 208 if (tfd < 0) 209 errx(1, "The passwd file is still busy, " 210 "try again later."); 211 } 212 213 pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); 214 if (pfd < 0) 215 pw_error(_PATH_MASTERPASSWD, 1, 1); 216 217 pw_copy(pfd, tfd, pw, &old_pw); 218 219 if (pw_mkdb(username, old_change == pw->pw_change) < 0) 220 pw_error((char *)NULL, 0, 1); 221 222 syslog(LOG_AUTH | LOG_INFO, 223 "user %s (UID %lu) successfully changed " 224 "the local password of user %s", 225 uid ? username : "root", (unsigned long)uid, username); 226 } 227 228 #else /* ! USE_PAM */ 229 230 static int force_local; 231 232 int 233 local_init(progname) 234 const char *progname; 235 { 236 force_local = 0; 237 return (0); 238 } 239 240 int 241 local_arg(char ch, const char *arg) 242 { 243 switch (ch) { 244 case 'l': 245 force_local = 1; 246 break; 247 default: 248 return(0); 249 } 250 return(1); 251 } 252 253 int 254 local_arg_end() 255 { 256 if (force_local) 257 return(PW_USE_FORCE); 258 return(PW_USE); 259 } 260 261 void 262 local_end() 263 { 264 /* NOOP */ 265 } 266 267 int 268 local_chpw(uname) 269 const char *uname; 270 { 271 struct passwd *pw; 272 struct passwd old_pw; 273 time_t old_change; 274 int pfd, tfd; 275 int min_pw_len = 0; 276 int pw_expiry = 0; 277 #ifdef LOGIN_CAP 278 login_cap_t *lc; 279 #endif 280 281 if (!(pw = getpwnam(uname))) { 282 warnx("unknown user %s", uname); 283 return (1); 284 } 285 286 uid = getuid(); 287 if (uid && uid != pw->pw_uid) { 288 warnx("%s", strerror(EACCES)); 289 return (1); 290 } 291 292 /* Save the old pw information for comparing on pw_copy(). */ 293 old_pw = *pw; 294 295 /* 296 * Get class restrictions for this user, then get the new password. 297 */ 298 #ifdef LOGIN_CAP 299 if((lc = login_getclass(pw->pw_class))) { 300 min_pw_len = (int) login_getcapnum(lc, "minpasswordlen", 0, 0); 301 pw_expiry = (int) login_getcaptime(lc, "passwordtime", 0, 0); 302 login_close(lc); 303 } 304 #endif 305 306 pw->pw_passwd = getnewpasswd(pw, min_pw_len); 307 old_change = pw->pw_change; 308 pw->pw_change = pw_expiry ? pw_expiry + time(NULL) : 0; 309 310 /* 311 * Now that the user has given us a new password, let us 312 * change the database. 313 */ 314 pw_init(); 315 tfd = pw_lock(0); 316 if (tfd < 0) { 317 warnx ("The passwd file is busy, waiting..."); 318 tfd = pw_lock(10); 319 if (tfd < 0) 320 errx(1, "The passwd file is still busy, " 321 "try again later."); 322 } 323 324 pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0); 325 if (pfd < 0) 326 pw_error(_PATH_MASTERPASSWD, 1, 1); 327 328 pw_copy(pfd, tfd, pw, &old_pw); 329 330 if (pw_mkdb(uname, old_change == pw->pw_change) < 0) 331 pw_error((char *)NULL, 0, 1); 332 333 syslog(LOG_AUTH | LOG_INFO, 334 "user %s (UID %lu) successfully changed " 335 "the local password of user %s", 336 uid ? uname : "root", (unsigned long)uid, uname); 337 338 return (0); 339 } 340 341 #endif /* USE_PAM */ 342