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