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