xref: /openbsd/usr.bin/passwd/local_passwd.c (revision 78b63d65)
1 /*	$OpenBSD: local_passwd.c,v 1.24 2001/12/07 04:15:08 millert 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. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #ifndef lint
37 /*static const char sccsid[] = "from: @(#)local_passwd.c	5.5 (Berkeley) 5/6/91";*/
38 static const char rcsid[] = "$OpenBSD: local_passwd.c,v 1.24 2001/12/07 04:15:08 millert Exp $";
39 #endif /* not lint */
40 
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/uio.h>
44 
45 #include <err.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <pwd.h>
49 #include <stdio.h>
50 #include <signal.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <util.h>
54 #include <login_cap.h>
55 
56 static uid_t uid;
57 extern int pwd_gensalt __P((char *, int, struct passwd *, login_cap_t *, char));
58 extern int pwd_check __P((struct passwd *, login_cap_t *, char *));
59 extern int pwd_gettries __P((struct passwd *, login_cap_t *));
60 
61 char *getnewpasswd __P((struct passwd *, login_cap_t *, int));
62 void kbintr __P((int));
63 
64 int
65 local_passwd(uname, authenticated)
66 	char *uname;
67 	int authenticated;
68 {
69 	struct passwd *pw;
70 	login_cap_t *lc;
71 	sigset_t fullset;
72 	time_t period;
73 	int i, pfd, tfd = -1;
74 	int pwflags = _PASSWORD_OMITV7;
75 
76 	if (!(pw = getpwnam(uname))) {
77 #ifdef YP
78 		extern int use_yp;
79 		if (!use_yp)
80 #endif
81 		warnx("unknown user %s.", uname);
82 		return(1);
83 	}
84 	if ((lc = login_getclass(pw->pw_class)) == NULL) {
85 		warnx("unable to get login class for user %s.", uname);
86 		return(1);
87 	}
88 
89 	uid = authenticated ? pw->pw_uid : getuid();
90 	if (uid && uid != pw->pw_uid) {
91 		warnx("login/uid mismatch, username argument required.");
92 		return(1);
93 	}
94 
95 	/* Get the new password. */
96 	pw->pw_passwd = getnewpasswd(pw, lc, authenticated);
97 
98 	/* Reset password change time based on login.conf. */
99 	period = login_getcaptime(lc, "passwordtime", 0, 0);
100 	if (period > 0) {
101 		pw->pw_change = time(NULL) + period;
102 	} else {
103 		/*
104 		 * If the pw change time is the same we only need
105 		 * to update the spwd.db file.
106 		 */
107 		if (pw->pw_change != 0)
108 			pw->pw_change = 0;
109 		else
110 			pwflags = _PASSWORD_SECUREONLY;
111 	}
112 
113 	/* Drop user's real uid and block all signals to avoid a DoS. */
114 	setuid(0);
115 	sigfillset(&fullset);
116 	sigdelset(&fullset, SIGINT);
117 	sigprocmask(SIG_BLOCK, &fullset, NULL);
118 
119 	/* Get a lock on the passwd file and open it. */
120 	pw_init();
121 	for (i = 1; (tfd = pw_lock(0)) == -1; i++) {
122 		if (i == 4)
123 			(void)fputs("Attempting lock password file, "
124 			    "please wait or press ^C to abort", stderr);
125 		(void)signal(SIGINT, kbintr);
126 		if (i % 16 == 0)
127 			fputc('.', stderr);
128 		usleep(250000);
129 		(void)signal(SIGINT, SIG_IGN);
130 	}
131 	if (i >= 4)
132 		fputc('\n', stderr);
133 	pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0);
134 	if (pfd < 0 || fcntl(pfd, F_SETFD, 1) == -1)
135 		pw_error(_PATH_MASTERPASSWD, 1, 1);
136 
137 	/* Update master.passwd file and rebuild spwd.db. */
138 	pw_copy(pfd, tfd, pw);
139 	if (pw_mkdb(uname, pwflags) < 0)
140 		pw_error(NULL, 0, 1);
141 
142 	return(0);
143 }
144 
145 char *
146 getnewpasswd(pw, lc, authenticated)
147 	struct passwd *pw;
148 	login_cap_t *lc;
149 	int authenticated;
150 {
151 	char *p;
152 	int tries, pwd_tries;
153 	char buf[_PASSWORD_LEN+1], salt[_PASSWORD_LEN];
154 	sig_t saveint, savequit;
155 
156 	saveint = signal(SIGINT, kbintr);
157 	savequit = signal(SIGQUIT, kbintr);
158 
159 	if (!authenticated) {
160 		(void)printf("Changing local password for %s.\n", pw->pw_name);
161 		if (uid && pw->pw_passwd[0] &&
162 		    strcmp(crypt(getpass("Old password:"), pw->pw_passwd),
163 		    pw->pw_passwd)) {
164 			errno = EACCES;
165 			pw_error(NULL, 1, 1);
166 		}
167 	}
168 
169 	pwd_tries = pwd_gettries(pw, lc);
170 
171 	for (buf[0] = '\0', tries = 0;;) {
172 		p = getpass("New password:");
173 		if (!*p) {
174 			(void)printf("Password unchanged.\n");
175 			pw_error(NULL, 0, 0);
176 		}
177 		if (strcmp(p, "s/key") == 0) {
178 			printf("That password collides with a system feature. Choose another.\n");
179 			continue;
180 		}
181 
182 		if ((tries++ < pwd_tries || pwd_tries == 0)
183 		    && pwd_check(pw, lc, p) == 0)
184 			continue;
185 		strlcpy(buf, p, sizeof(buf));
186 		if (!strcmp(buf, getpass("Retype new password:")))
187 			break;
188 		(void)printf("Mismatch; try again, EOF to quit.\n");
189 	}
190 	if (!pwd_gensalt(salt, _PASSWORD_LEN, pw, lc, 'l')) {
191 		(void)printf("Couldn't generate salt.\n");
192 		pw_error(NULL, 0, 0);
193 	}
194 	(void)signal(SIGINT, saveint);
195 	(void)signal(SIGQUIT, savequit);
196 
197 	return(crypt(buf, salt));
198 }
199 
200 void
201 kbintr(signo)
202 	int signo;
203 {
204 	char msg[] = "\nPassword unchanged.\n";
205 	struct iovec iv[5];
206 	extern char *__progname;
207 
208 	iv[0].iov_base = msg;
209 	iv[0].iov_len = sizeof(msg) - 1;
210 	iv[1].iov_base = __progname;
211 	iv[1].iov_len = strlen(__progname);
212 	iv[2].iov_base = ": ";
213 	iv[2].iov_len = 2;
214 	iv[3].iov_base = _PATH_MASTERPASSWD;
215 	iv[3].iov_len = sizeof(_PATH_MASTERPASSWD) - 1;
216 	iv[4].iov_base = " unchanged\n";
217 	iv[4].iov_len = 11;
218 	writev(STDERR_FILENO, iv, 5);
219 
220 	_exit(1);
221 }
222