1/*- 2 * Copyright (c) 1990, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#ifndef lint 35static const char sccsid[] = "@(#)local_passwd.c 8.3 (Berkeley) 4/2/94"; 36#endif /* not lint */ 37 38#include <sys/cdefs.h> 39__FBSDID("$FreeBSD$"); 40 41#include <sys/types.h> 42#include <sys/time.h> 43 44#include <ctype.h> 45#include <err.h> 46#include <errno.h> 47#include <pwd.h> 48#include <stdio.h> 49#include <stdlib.h> 50#include <string.h> 51#include <time.h> 52#include <unistd.h> 53 54#include <pw_util.h> 55#ifdef YP 56#include <pw_yp.h> 57#endif 58 59#ifdef LOGGING 60#include <syslog.h> 61#endif 62 63#ifdef LOGIN_CAP 64#ifdef AUTH_NONE /* multiple defs :-( */ 65#undef AUTH_NONE 66#endif 67#include <login_cap.h> 68#endif 69 70#include "extern.h" 71 72static uid_t uid; 73int randinit; 74 75extern void 76pw_copy(int ffd, int tfd, struct passwd *pw, struct passwd *old_pw); 77 78char *tempname; 79 80static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ 81 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 82 83void 84to64(s, v, n) 85 char *s; 86 long v; 87 int n; 88{ 89 while (--n >= 0) { 90 *s++ = itoa64[v&0x3f]; 91 v >>= 6; 92 } 93} 94 95char * 96getnewpasswd(pw, nis) 97 struct passwd *pw; 98 int nis; 99{ 100 int tries, min_length = 6; 101 int force_mix_case = 1; 102 char *p, *t; 103#ifdef LOGIN_CAP 104 login_cap_t * lc; 105#endif 106 char buf[_PASSWORD_LEN+1], salt[32]; 107 struct timeval tv; 108 109 if (!nis) 110 (void)printf("Changing local password for %s.\n", pw->pw_name); 111 112 if (uid && pw->pw_passwd[0] && 113 strcmp(crypt(getpass("Old password:"), pw->pw_passwd), 114 pw->pw_passwd)) { 115 errno = EACCES; 116 pw_error(NULL, 1, 1); 117 } 118 119#ifdef LOGIN_CAP 120 /* 121 * Determine minimum password length, next password change date, 122 * and whether or not to force mixed case passwords. 123 * Note that even for NIS passwords, login_cap is still used. 124 */ 125 if ((lc = login_getpwclass(pw)) != NULL) { 126 time_t period; 127 128 /* minpasswordlen capablity */ 129 min_length = (int)login_getcapnum(lc, "minpasswordlen", 130 min_length, min_length); 131 /* passwordtime capability */ 132 period = login_getcaptime(lc, "passwordtime", 0, 0); 133 if (period > (time_t)0) { 134 pw->pw_change = time(NULL) + period; 135 } 136 /* mixpasswordcase capability */ 137 force_mix_case = login_getcapbool(lc, "mixpasswordcase", 1); 138 } 139#endif 140 141 for (buf[0] = '\0', tries = 0;;) { 142 p = getpass("New password:"); 143 if (!*p) { 144 (void)printf("Password unchanged.\n"); 145 pw_error(NULL, 0, 0); 146 } 147 if (strlen(p) < min_length && (uid != 0 || ++tries < 2)) { 148 (void)printf("Please enter a password at least %d characters in length.\n", min_length); 149 continue; 150 } 151 152 if (force_mix_case) { 153 for (t = p; *t && islower(*t); ++t); 154 if (!*t && (uid != 0 || ++tries < 2)) { 155 (void)printf("Please don't use an all-lower case password.\nUnusual capitalization, control characters or digits are suggested.\n"); 156 continue; 157 } 158 } 159 (void)strcpy(buf, p); 160 if (!strcmp(buf, getpass("Retype new password:"))) 161 break; 162 (void)printf("Mismatch; try again, EOF to quit.\n"); 163 } 164 /* grab a random printable character that isn't a colon */ 165 if (!randinit) { 166 randinit = 1; 167 srandomdev(); 168 } 169#ifdef NEWSALT 170 salt[0] = _PASSWORD_EFMT1; 171 to64(&salt[1], (long)(29 * 25), 4); 172 to64(&salt[5], random(), 4); 173 salt[9] = '\0'; 174#else 175 /* Make a good size salt for algorithms that can use it. */ 176 gettimeofday(&tv,0); 177#ifdef LOGIN_CAP 178 if (login_setcryptfmt(lc, "md5", NULL) == NULL) 179 pw_error("cannot set password cipher", 1, 1); 180 login_close(lc); 181#else 182 (void)crypt_set_format("md5"); 183#endif 184 /* Salt suitable for anything */ 185 to64(&salt[0], random(), 3); 186 to64(&salt[3], tv.tv_usec, 3); 187 to64(&salt[6], tv.tv_sec, 2); 188 to64(&salt[8], random(), 5); 189 to64(&salt[13], random(), 5); 190 to64(&salt[17], random(), 5); 191 to64(&salt[22], random(), 5); 192 salt[27] = '\0'; 193#endif 194 return (crypt(buf, salt)); 195} 196 197int 198local_passwd(uname) 199 char *uname; 200{ 201 struct passwd *pw; 202 int pfd, tfd; 203 204 if (!(pw = getpwnam(uname))) 205 errx(1, "unknown user %s", uname); 206 207#ifdef YP 208 /* Use the right password information. */ 209 pw = (struct passwd *)&local_password; 210#endif 211 uid = getuid(); 212 if (uid && uid != pw->pw_uid) 213 errx(1, "%s", strerror(EACCES)); 214 215 pw_init(); 216 217 /* 218 * Get the new password. Reset passwd change time to zero by 219 * default. If the user has a valid login class (or the default 220 * fallback exists), then the next password change date is set 221 * by getnewpasswd() according to the "passwordtime" capability 222 * if one has been specified. 223 */ 224 pw->pw_change = 0; 225 pw->pw_passwd = getnewpasswd(pw, 0); 226 227 pfd = pw_lock(); 228 tfd = pw_tmp(); 229 pw_copy(pfd, tfd, pw, NULL); 230 231 if (!pw_mkdb(uname)) 232 pw_error((char *)NULL, 0, 1); 233#ifdef LOGGING 234 syslog(LOG_DEBUG, "user %s changed their local password\n", uname); 235#endif 236 return (0); 237} 238