1 /* $OpenBSD: local_passwd.c,v 1.52 2016/09/02 18:06:43 tedu Exp $ */ 2 3 /*- 4 * Copyright (c) 1990 The Regents of the University of California. 5 * 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/types.h> 33 #include <sys/stat.h> 34 #include <sys/uio.h> 35 36 #include <err.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <pwd.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <signal.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <util.h> 46 #include <login_cap.h> 47 #include <readpassphrase.h> 48 49 #define UNCHANGED_MSG "Password unchanged." 50 51 static uid_t uid; 52 extern int pwd_check(login_cap_t *, char *); 53 extern int pwd_gettries(login_cap_t *); 54 55 int local_passwd(char *, int); 56 char *getnewpasswd(struct passwd *, login_cap_t *, int); 57 void kbintr(int); 58 59 int 60 local_passwd(char *uname, int authenticated) 61 { 62 struct passwd *pw, *opw; 63 login_cap_t *lc; 64 sigset_t fullset; 65 time_t period; 66 int i, pfd, tfd = -1; 67 int pwflags = _PASSWORD_OMITV7; 68 69 if (!(pw = getpwnam_shadow(uname))) { 70 warnx("unknown user %s.", uname); 71 return(1); 72 } 73 74 if (pledge("stdio rpath wpath cpath getpw tty id proc exec", NULL) == -1) 75 err(1, "pledge"); 76 77 if ((opw = pw_dup(pw)) == NULL) { 78 warn(NULL); 79 return(1); 80 } 81 if ((lc = login_getclass(pw->pw_class)) == NULL) { 82 warnx("unable to get login class for user %s.", uname); 83 free(opw); 84 return(1); 85 } 86 87 uid = authenticated ? pw->pw_uid : getuid(); 88 if (uid && uid != pw->pw_uid) { 89 warnx("login/uid mismatch, username argument required."); 90 free(opw); 91 return(1); 92 } 93 94 /* Get the new password. */ 95 pw->pw_passwd = getnewpasswd(pw, lc, authenticated); 96 97 if (pledge("stdio rpath wpath cpath getpw id proc exec", NULL) == -1) 98 err(1, "pledge"); 99 100 /* Reset password change time based on login.conf. */ 101 period = (time_t)login_getcaptime(lc, "passwordtime", 0, 0); 102 if (period > 0) { 103 pw->pw_change = time(NULL) + period; 104 } else { 105 /* 106 * If the pw change time is the same we only need 107 * to update the spwd.db file. 108 */ 109 if (pw->pw_change != 0) 110 pw->pw_change = 0; 111 else 112 pwflags = _PASSWORD_SECUREONLY; 113 } 114 115 /* Drop user's real uid and block all signals to avoid a DoS. */ 116 setuid(0); 117 sigfillset(&fullset); 118 sigdelset(&fullset, SIGINT); 119 sigprocmask(SIG_BLOCK, &fullset, NULL); 120 121 if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1) 122 err(1, "pledge"); 123 124 /* Get a lock on the passwd file and open it. */ 125 pw_init(); 126 for (i = 1; (tfd = pw_lock(0)) == -1; i++) { 127 if (i == 4) 128 (void)fputs("Attempting to lock password file, " 129 "please wait or press ^C to abort", stderr); 130 (void)signal(SIGINT, kbintr); 131 if (i % 16 == 0) 132 fputc('.', stderr); 133 usleep(250000); 134 (void)signal(SIGINT, SIG_IGN); 135 } 136 if (i >= 4) 137 fputc('\n', stderr); 138 pfd = open(_PATH_MASTERPASSWD, O_RDONLY | O_CLOEXEC, 0); 139 if (pfd < 0) 140 pw_error(_PATH_MASTERPASSWD, 1, 1); 141 142 /* Update master.passwd file and rebuild spwd.db. */ 143 pw_copy(pfd, tfd, pw, opw); 144 free(opw); 145 if (pw_mkdb(uname, pwflags) < 0) 146 pw_error(NULL, 0, 1); 147 148 return(0); 149 } 150 151 char * 152 getnewpasswd(struct passwd *pw, login_cap_t *lc, int authenticated) 153 { 154 static char hash[_PASSWORD_LEN]; 155 char newpass[1024]; 156 char *p, *pref; 157 int tries, pwd_tries; 158 sig_t saveint, savequit; 159 160 saveint = signal(SIGINT, kbintr); 161 savequit = signal(SIGQUIT, kbintr); 162 163 if (!authenticated) { 164 (void)printf("Changing password for %s.\n", pw->pw_name); 165 if (uid != 0 && pw->pw_passwd[0] != '\0') { 166 char oldpass[1024]; 167 168 p = readpassphrase("Old password:", oldpass, 169 sizeof(oldpass), RPP_ECHO_OFF); 170 if (p == NULL || *p == '\0') { 171 (void)printf("%s\n", UNCHANGED_MSG); 172 pw_abort(); 173 exit(p == NULL ? 1 : 0); 174 } 175 if (crypt_checkpass(p, pw->pw_passwd) != 0) { 176 errno = EACCES; 177 explicit_bzero(oldpass, sizeof(oldpass)); 178 pw_error(NULL, 1, 1); 179 } 180 explicit_bzero(oldpass, sizeof(oldpass)); 181 } 182 } 183 184 pwd_tries = pwd_gettries(lc); 185 186 for (newpass[0] = '\0', tries = 0;;) { 187 char repeat[1024]; 188 189 p = readpassphrase("New password:", newpass, sizeof(newpass), 190 RPP_ECHO_OFF); 191 if (p == NULL || *p == '\0') { 192 (void)printf("%s\n", UNCHANGED_MSG); 193 pw_abort(); 194 exit(p == NULL ? 1 : 0); 195 } 196 if (strcmp(p, "s/key") == 0) { 197 printf("That password collides with a system feature. Choose another.\n"); 198 continue; 199 } 200 201 if ((tries++ < pwd_tries || pwd_tries == 0) && 202 pwd_check(lc, p) == 0) 203 continue; 204 p = readpassphrase("Retype new password:", repeat, sizeof(repeat), 205 RPP_ECHO_OFF); 206 if (p != NULL && strcmp(newpass, p) == 0) 207 break; 208 (void)printf("Mismatch; try again, EOF to quit.\n"); 209 explicit_bzero(newpass, sizeof(newpass)); 210 } 211 212 (void)signal(SIGINT, saveint); 213 (void)signal(SIGQUIT, savequit); 214 215 pref = login_getcapstr(lc, "localcipher", NULL, NULL); 216 if (crypt_newhash(newpass, pref, hash, sizeof(hash)) != 0) { 217 (void)printf("Couldn't generate hash.\n"); 218 explicit_bzero(newpass, sizeof(newpass)); 219 pw_error(NULL, 0, 0); 220 } 221 explicit_bzero(newpass, sizeof(newpass)); 222 free(pref); 223 return hash; 224 } 225 226 void 227 kbintr(int signo) 228 { 229 dprintf(STDOUT_FILENO, "\n%s\n", UNCHANGED_MSG); 230 _exit(0); 231 } 232