1242be47eSzrj /*-
2*c98db407SSascha Wildner  * SPDX-License-Identifier: BSD-3-Clause
3*c98db407SSascha Wildner  *
4242be47eSzrj  * Copyright 1998 Juniper Networks, Inc.
5242be47eSzrj  * All rights reserved.
6242be47eSzrj  * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
7242be47eSzrj  * All rights reserved.
8242be47eSzrj  *
9242be47eSzrj  * Portions of this software was developed for the FreeBSD Project by
10242be47eSzrj  * ThinkSec AS and NAI Labs, the Security Research Division of Network
11242be47eSzrj  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
12242be47eSzrj  * ("CBOSS"), as part of the DARPA CHATS research program.
13242be47eSzrj  *
14242be47eSzrj  * Redistribution and use in source and binary forms, with or without
15242be47eSzrj  * modification, are permitted provided that the following conditions
16242be47eSzrj  * are met:
17242be47eSzrj  * 1. Redistributions of source code must retain the above copyright
18242be47eSzrj  *    notice, this list of conditions and the following disclaimer.
19242be47eSzrj  * 2. Redistributions in binary form must reproduce the above copyright
20242be47eSzrj  *    notice, this list of conditions and the following disclaimer in the
21242be47eSzrj  *    documentation and/or other materials provided with the distribution.
22242be47eSzrj  * 3. The name of the author may not be used to endorse or promote
23242be47eSzrj  *    products derived from this software without specific prior written
24242be47eSzrj  *    permission.
25242be47eSzrj  *
26242be47eSzrj  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
27242be47eSzrj  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28242be47eSzrj  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29242be47eSzrj  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
30242be47eSzrj  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31242be47eSzrj  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32242be47eSzrj  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33242be47eSzrj  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34242be47eSzrj  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35242be47eSzrj  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36242be47eSzrj  * SUCH DAMAGE.
37242be47eSzrj  *
38*c98db407SSascha Wildner  * $FreeBSD: head/lib/libpam/modules/pam_unix/pam_unix.c 326219 2017-11-26 02:00:33Z pfg $
39242be47eSzrj  */
40242be47eSzrj 
41242be47eSzrj #include <sys/param.h>
42242be47eSzrj #include <sys/socket.h>
43242be47eSzrj #include <sys/time.h>
44242be47eSzrj #include <netinet/in.h>
45242be47eSzrj #include <arpa/inet.h>
46242be47eSzrj 
47242be47eSzrj #include <login_cap.h>
48242be47eSzrj #include <netdb.h>
49242be47eSzrj #include <pwd.h>
50242be47eSzrj #include <stdlib.h>
51242be47eSzrj #include <string.h>
52242be47eSzrj #include <stdio.h>
53242be47eSzrj #include <syslog.h>
54242be47eSzrj #include <time.h>
55242be47eSzrj #include <unistd.h>
56242be47eSzrj 
57242be47eSzrj #include <libutil.h>
58242be47eSzrj 
59242be47eSzrj #ifdef YP
60242be47eSzrj #include <ypclnt.h>
61242be47eSzrj #endif
62242be47eSzrj 
63242be47eSzrj #define PAM_SM_AUTH
64242be47eSzrj #define PAM_SM_ACCOUNT
65242be47eSzrj #define	PAM_SM_PASSWORD
66242be47eSzrj 
67242be47eSzrj #include <security/pam_appl.h>
68242be47eSzrj #include <security/pam_modules.h>
69242be47eSzrj #include <security/pam_mod_misc.h>
70242be47eSzrj 
71242be47eSzrj #define PASSWORD_HASH		"sha512"
72242be47eSzrj #define DEFAULT_WARN		(2L * 7L * 86400L)  /* Two weeks */
73242be47eSzrj #define	SALTSIZE		32
74242be47eSzrj 
75242be47eSzrj #define	LOCKED_PREFIX		"*LOCKED*"
76242be47eSzrj #define	LOCKED_PREFIX_LEN	(sizeof(LOCKED_PREFIX) - 1)
77242be47eSzrj 
78242be47eSzrj static void makesalt(char []);
79242be47eSzrj 
80242be47eSzrj static char password_hash[] =		PASSWORD_HASH;
81242be47eSzrj 
82242be47eSzrj #define PAM_OPT_LOCAL_PASS	"local_pass"
83242be47eSzrj #define PAM_OPT_NIS_PASS	"nis_pass"
84242be47eSzrj 
85242be47eSzrj /*
86242be47eSzrj  * authentication management
87242be47eSzrj  */
88242be47eSzrj PAM_EXTERN int
pam_sm_authenticate(pam_handle_t * pamh,int flags __unused,int argc __unused,const char * argv[]__unused)89242be47eSzrj pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
90242be47eSzrj     int argc __unused, const char *argv[] __unused)
91242be47eSzrj {
92242be47eSzrj 	login_cap_t *lc;
93242be47eSzrj 	struct passwd *pwd;
94242be47eSzrj 	int retval;
95242be47eSzrj 	const char *pass, *user, *realpw, *prompt;
96242be47eSzrj 	char *cryptpw;
97242be47eSzrj 
98242be47eSzrj 	if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) {
99*c98db407SSascha Wildner 		user = getlogin();
100242be47eSzrj 	} else {
101242be47eSzrj 		retval = pam_get_user(pamh, &user, NULL);
102242be47eSzrj 		if (retval != PAM_SUCCESS)
103242be47eSzrj 			return (retval);
104242be47eSzrj 	}
105*c98db407SSascha Wildner 	pwd = getpwnam(user);
106242be47eSzrj 
107242be47eSzrj 	PAM_LOG("Got user: %s", user);
108242be47eSzrj 
109242be47eSzrj 	if (pwd != NULL) {
110242be47eSzrj 		PAM_LOG("Doing real authentication");
111242be47eSzrj 		realpw = pwd->pw_passwd;
112242be47eSzrj 		if (realpw[0] == '\0') {
113242be47eSzrj 			if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) &&
114242be47eSzrj 			    openpam_get_option(pamh, PAM_OPT_NULLOK))
115242be47eSzrj 				return (PAM_SUCCESS);
116*c98db407SSascha Wildner 			PAM_LOG("Password is empty, using fake password");
117242be47eSzrj 			realpw = "*";
118242be47eSzrj 		}
119242be47eSzrj 		lc = login_getpwclass(pwd);
120242be47eSzrj 	} else {
121242be47eSzrj 		PAM_LOG("Doing dummy authentication");
122242be47eSzrj 		realpw = "*";
123242be47eSzrj 		lc = login_getclass(NULL);
124242be47eSzrj 	}
125242be47eSzrj 	prompt = login_getcapstr(lc, "passwd_prompt", NULL, NULL);
126242be47eSzrj 	retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, prompt);
127242be47eSzrj 	login_close(lc);
128242be47eSzrj 	if (retval != PAM_SUCCESS)
129242be47eSzrj 		return (retval);
130242be47eSzrj 	PAM_LOG("Got password");
131*c98db407SSascha Wildner 	if (strnlen(pass, _PASSWORD_LEN + 1) > _PASSWORD_LEN) {
132*c98db407SSascha Wildner 		PAM_LOG("Password is too long, using fake password");
133*c98db407SSascha Wildner 		realpw = "*";
134*c98db407SSascha Wildner 	}
135242be47eSzrj 	cryptpw = crypt(pass, realpw);
136242be47eSzrj 	if (cryptpw != NULL && strcmp(cryptpw, realpw) == 0)
137242be47eSzrj 		return (PAM_SUCCESS);
138242be47eSzrj 
139242be47eSzrj 	PAM_VERBOSE_ERROR("UNIX authentication refused");
140242be47eSzrj 	return (PAM_AUTH_ERR);
141242be47eSzrj }
142242be47eSzrj 
143242be47eSzrj PAM_EXTERN int
pam_sm_setcred(pam_handle_t * pamh __unused,int flags __unused,int argc __unused,const char * argv[]__unused)144242be47eSzrj pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused,
145242be47eSzrj     int argc __unused, const char *argv[] __unused)
146242be47eSzrj {
147242be47eSzrj 
148242be47eSzrj 	return (PAM_SUCCESS);
149242be47eSzrj }
150242be47eSzrj 
151242be47eSzrj /*
152242be47eSzrj  * account management
153242be47eSzrj  */
154242be47eSzrj PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags __unused,int argc __unused,const char * argv[]__unused)155242be47eSzrj pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused,
156242be47eSzrj     int argc __unused, const char *argv[] __unused)
157242be47eSzrj {
158242be47eSzrj 	struct addrinfo hints, *res;
159242be47eSzrj 	struct passwd *pwd;
160242be47eSzrj 	struct timeval tp;
161242be47eSzrj 	login_cap_t *lc;
162242be47eSzrj 	time_t warntime;
163242be47eSzrj 	int retval;
164242be47eSzrj 	const char *user;
165242be47eSzrj 	const void *rhost, *tty;
166242be47eSzrj 	char rhostip[MAXHOSTNAMELEN] = "";
167242be47eSzrj 
168242be47eSzrj 	retval = pam_get_user(pamh, &user, NULL);
169242be47eSzrj 	if (retval != PAM_SUCCESS)
170242be47eSzrj 		return (retval);
171242be47eSzrj 
172242be47eSzrj 	if (user == NULL || (pwd = getpwnam(user)) == NULL)
173242be47eSzrj 		return (PAM_SERVICE_ERR);
174242be47eSzrj 
175242be47eSzrj 	PAM_LOG("Got user: %s", user);
176242be47eSzrj 
177242be47eSzrj 	retval = pam_get_item(pamh, PAM_RHOST, &rhost);
178242be47eSzrj 	if (retval != PAM_SUCCESS)
179242be47eSzrj 		return (retval);
180242be47eSzrj 
181242be47eSzrj 	retval = pam_get_item(pamh, PAM_TTY, &tty);
182242be47eSzrj 	if (retval != PAM_SUCCESS)
183242be47eSzrj 		return (retval);
184242be47eSzrj 
185242be47eSzrj 	if (*pwd->pw_passwd == '\0' &&
186242be47eSzrj 	    (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0)
187242be47eSzrj 		return (PAM_NEW_AUTHTOK_REQD);
188242be47eSzrj 
189242be47eSzrj 	if (strncmp(pwd->pw_passwd, LOCKED_PREFIX, LOCKED_PREFIX_LEN) == 0)
190242be47eSzrj 		return (PAM_AUTH_ERR);
191242be47eSzrj 
192242be47eSzrj 	lc = login_getpwclass(pwd);
193242be47eSzrj 	if (lc == NULL) {
194242be47eSzrj 		PAM_LOG("Unable to get login class for user %s", user);
195242be47eSzrj 		return (PAM_SERVICE_ERR);
196242be47eSzrj 	}
197242be47eSzrj 
198242be47eSzrj 	PAM_LOG("Got login_cap");
199242be47eSzrj 
200242be47eSzrj 	if (pwd->pw_change || pwd->pw_expire)
201242be47eSzrj 		gettimeofday(&tp, NULL);
202242be47eSzrj 
203242be47eSzrj 	/*
204242be47eSzrj 	 * Check pw_expire before pw_change - no point in letting the
205242be47eSzrj 	 * user change the password on an expired account.
206242be47eSzrj 	 */
207242be47eSzrj 
208242be47eSzrj 	if (pwd->pw_expire) {
209242be47eSzrj 		warntime = login_getcaptime(lc, "warnexpire",
210242be47eSzrj 		    DEFAULT_WARN, DEFAULT_WARN);
211242be47eSzrj 		if (tp.tv_sec >= pwd->pw_expire) {
212242be47eSzrj 			login_close(lc);
213242be47eSzrj 			return (PAM_ACCT_EXPIRED);
214242be47eSzrj 		} else if (pwd->pw_expire - tp.tv_sec < warntime &&
215242be47eSzrj 		    (flags & PAM_SILENT) == 0) {
216242be47eSzrj 			pam_error(pamh, "Warning: your account expires on %s",
217242be47eSzrj 			    ctime(&pwd->pw_expire));
218242be47eSzrj 		}
219242be47eSzrj 	}
220242be47eSzrj 
221242be47eSzrj 	retval = PAM_SUCCESS;
222242be47eSzrj 	if (pwd->pw_change) {
223242be47eSzrj 		warntime = login_getcaptime(lc, "warnpassword",
224242be47eSzrj 		    DEFAULT_WARN, DEFAULT_WARN);
225242be47eSzrj 		if (tp.tv_sec >= pwd->pw_change) {
226242be47eSzrj 			retval = PAM_NEW_AUTHTOK_REQD;
227242be47eSzrj 		} else if (pwd->pw_change - tp.tv_sec < warntime &&
228242be47eSzrj 		    (flags & PAM_SILENT) == 0) {
229242be47eSzrj 			pam_error(pamh, "Warning: your password expires on %s",
230242be47eSzrj 			    ctime(&pwd->pw_change));
231242be47eSzrj 		}
232242be47eSzrj 	}
233242be47eSzrj 
234242be47eSzrj 	/*
235242be47eSzrj 	 * From here on, we must leave retval untouched (unless we
236242be47eSzrj 	 * know we're going to fail), because we need to remember
237242be47eSzrj 	 * whether we're supposed to return PAM_SUCCESS or
238242be47eSzrj 	 * PAM_NEW_AUTHTOK_REQD.
239242be47eSzrj 	 */
240242be47eSzrj 
241242be47eSzrj 	if (rhost && *(const char *)rhost != '\0') {
242242be47eSzrj 		memset(&hints, 0, sizeof(hints));
243242be47eSzrj 		hints.ai_family = AF_UNSPEC;
244242be47eSzrj 		if (getaddrinfo(rhost, NULL, &hints, &res) == 0) {
245242be47eSzrj 			getnameinfo(res->ai_addr, res->ai_addrlen,
246242be47eSzrj 			    rhostip, sizeof(rhostip), NULL, 0,
247242be47eSzrj 			    NI_NUMERICHOST);
248242be47eSzrj 		}
249242be47eSzrj 		if (res != NULL)
250242be47eSzrj 			freeaddrinfo(res);
251242be47eSzrj 	}
252242be47eSzrj 
253242be47eSzrj 	/*
254242be47eSzrj 	 * Check host / tty / time-of-day restrictions
255242be47eSzrj 	 */
256242be47eSzrj 
257242be47eSzrj 	if (!auth_hostok(lc, rhost, rhostip) ||
258242be47eSzrj 	    !auth_ttyok(lc, tty) ||
259242be47eSzrj 	    !auth_timeok(lc, time(NULL)))
260242be47eSzrj 		retval = PAM_AUTH_ERR;
261242be47eSzrj 
262242be47eSzrj 	login_close(lc);
263242be47eSzrj 
264242be47eSzrj 	return (retval);
265242be47eSzrj }
266242be47eSzrj 
267242be47eSzrj /*
268242be47eSzrj  * password management
269242be47eSzrj  *
270242be47eSzrj  * standard Unix and NIS password changing
271242be47eSzrj  */
272242be47eSzrj PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc __unused,const char * argv[]__unused)273242be47eSzrj pam_sm_chauthtok(pam_handle_t *pamh, int flags,
274242be47eSzrj     int argc __unused, const char *argv[] __unused)
275242be47eSzrj {
276242be47eSzrj #ifdef YP
277242be47eSzrj 	struct ypclnt *ypclnt;
278242be47eSzrj 	const void *yp_domain, *yp_server;
279242be47eSzrj #endif
280242be47eSzrj 	char salt[SALTSIZE + 1];
281242be47eSzrj 	login_cap_t *lc;
282242be47eSzrj 	struct passwd *pwd, *old_pwd;
283242be47eSzrj 	const char *user, *old_pass, *new_pass;
284242be47eSzrj 	char *encrypted;
285242be47eSzrj 	time_t passwordtime;
286242be47eSzrj 	int pfd, tfd, retval;
287242be47eSzrj 
288242be47eSzrj 	if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF))
289*c98db407SSascha Wildner 		user = getlogin();
290242be47eSzrj 	else {
291242be47eSzrj 		retval = pam_get_user(pamh, &user, NULL);
292242be47eSzrj 		if (retval != PAM_SUCCESS)
293242be47eSzrj 			return (retval);
294242be47eSzrj 	}
295*c98db407SSascha Wildner 	pwd = getpwnam(user);
296242be47eSzrj 
297242be47eSzrj 	if (pwd == NULL)
298242be47eSzrj 		return (PAM_AUTHTOK_RECOVERY_ERR);
299242be47eSzrj 
300242be47eSzrj 	PAM_LOG("Got user: %s", user);
301242be47eSzrj 
302242be47eSzrj 	if (flags & PAM_PRELIM_CHECK) {
303242be47eSzrj 
304242be47eSzrj 		PAM_LOG("PRELIM round");
305242be47eSzrj 
306242be47eSzrj 		if (getuid() == 0 &&
307242be47eSzrj 		    (pwd->pw_fields & _PWF_SOURCE) == _PWF_FILES)
308242be47eSzrj 			/* root doesn't need the old password */
309242be47eSzrj 			return (pam_set_item(pamh, PAM_OLDAUTHTOK, ""));
310242be47eSzrj #ifdef YP
311242be47eSzrj 		if (getuid() == 0 &&
312242be47eSzrj 		    (pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
313242be47eSzrj 
314242be47eSzrj 			yp_domain = yp_server = NULL;
315242be47eSzrj 			pam_get_data(pamh, "yp_domain", &yp_domain);
316242be47eSzrj 			pam_get_data(pamh, "yp_server", &yp_server);
317242be47eSzrj 
318242be47eSzrj 			ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_server);
319242be47eSzrj 			if (ypclnt == NULL)
320242be47eSzrj 				return (PAM_BUF_ERR);
321242be47eSzrj 
322242be47eSzrj 			if (ypclnt_connect(ypclnt) == -1) {
323242be47eSzrj 				ypclnt_free(ypclnt);
324242be47eSzrj 				return (PAM_SERVICE_ERR);
325242be47eSzrj 			}
326242be47eSzrj 
327242be47eSzrj 			retval = ypclnt_havepasswdd(ypclnt);
328242be47eSzrj 			ypclnt_free(ypclnt);
329242be47eSzrj 			if (retval == 1)
330242be47eSzrj 				return (pam_set_item(pamh, PAM_OLDAUTHTOK, ""));
331242be47eSzrj 			else if (retval == -1)
332242be47eSzrj 				return (PAM_SERVICE_ERR);
333242be47eSzrj 		}
334242be47eSzrj #endif
335242be47eSzrj 		if (pwd->pw_passwd[0] == '\0'
336242be47eSzrj 		    && openpam_get_option(pamh, PAM_OPT_NULLOK)) {
337242be47eSzrj 			/*
338242be47eSzrj 			 * No password case. XXX Are we giving too much away
339242be47eSzrj 			 * by not prompting for a password?
340242be47eSzrj 			 * XXX check PAM_DISALLOW_NULL_AUTHTOK
341242be47eSzrj 			 */
342242be47eSzrj 			old_pass = "";
343*c98db407SSascha Wildner 			retval = PAM_SUCCESS;
344242be47eSzrj 		} else {
345242be47eSzrj 			retval = pam_get_authtok(pamh,
346242be47eSzrj 			    PAM_OLDAUTHTOK, &old_pass, NULL);
347242be47eSzrj 			if (retval != PAM_SUCCESS)
348242be47eSzrj 				return (retval);
349242be47eSzrj 		}
350242be47eSzrj 		PAM_LOG("Got old password");
351242be47eSzrj 		/* always encrypt first */
352242be47eSzrj 		encrypted = crypt(old_pass, pwd->pw_passwd);
353242be47eSzrj 		if (old_pass[0] == '\0' &&
354242be47eSzrj 		    !openpam_get_option(pamh, PAM_OPT_NULLOK))
355242be47eSzrj 			return (PAM_PERM_DENIED);
356242be47eSzrj 		if (encrypted == NULL || strcmp(encrypted, pwd->pw_passwd) != 0)
357242be47eSzrj 			return (PAM_PERM_DENIED);
358242be47eSzrj 	}
359242be47eSzrj 	else if (flags & PAM_UPDATE_AUTHTOK) {
360242be47eSzrj 		PAM_LOG("UPDATE round");
361242be47eSzrj 
362242be47eSzrj 		retval = pam_get_authtok(pamh,
363242be47eSzrj 		    PAM_OLDAUTHTOK, &old_pass, NULL);
364242be47eSzrj 		if (retval != PAM_SUCCESS)
365242be47eSzrj 			return (retval);
366242be47eSzrj 		PAM_LOG("Got old password");
367242be47eSzrj 
368242be47eSzrj 		/* get new password */
369242be47eSzrj 		for (;;) {
370242be47eSzrj 			retval = pam_get_authtok(pamh,
371242be47eSzrj 			    PAM_AUTHTOK, &new_pass, NULL);
372242be47eSzrj 			if (retval != PAM_TRY_AGAIN)
373242be47eSzrj 				break;
374242be47eSzrj 			pam_error(pamh, "Mismatch; try again, EOF to quit.");
375242be47eSzrj 		}
376242be47eSzrj 		PAM_LOG("Got new password");
377242be47eSzrj 		if (retval != PAM_SUCCESS) {
378242be47eSzrj 			PAM_VERBOSE_ERROR("Unable to get new password");
379242be47eSzrj 			return (retval);
380242be47eSzrj 		}
381242be47eSzrj 
382242be47eSzrj 		if (getuid() != 0 && new_pass[0] == '\0' &&
383242be47eSzrj 		    !openpam_get_option(pamh, PAM_OPT_NULLOK))
384242be47eSzrj 			return (PAM_PERM_DENIED);
385242be47eSzrj 
386242be47eSzrj 		if ((old_pwd = pw_dup(pwd)) == NULL)
387242be47eSzrj 			return (PAM_BUF_ERR);
388242be47eSzrj 
389242be47eSzrj 		lc = login_getclass(pwd->pw_class);
390242be47eSzrj 		if (login_setcryptfmt(lc, password_hash, NULL) == NULL)
391242be47eSzrj 			openpam_log(PAM_LOG_ERROR,
392242be47eSzrj 			    "can't set password cipher, relying on default");
393242be47eSzrj 
394242be47eSzrj 		/* set password expiry date */
395242be47eSzrj 		pwd->pw_change = 0;
396242be47eSzrj 		passwordtime = login_getcaptime(lc, "passwordtime", 0, 0);
397242be47eSzrj 		if (passwordtime > 0)
398242be47eSzrj 			pwd->pw_change = time(NULL) + passwordtime;
399242be47eSzrj 
400242be47eSzrj 		login_close(lc);
401242be47eSzrj 		makesalt(salt);
402242be47eSzrj 		pwd->pw_passwd = crypt(new_pass, salt);
403242be47eSzrj #ifdef YP
404242be47eSzrj 		switch (old_pwd->pw_fields & _PWF_SOURCE) {
405242be47eSzrj 		case _PWF_FILES:
406242be47eSzrj #endif
407242be47eSzrj 			retval = PAM_SERVICE_ERR;
408242be47eSzrj 			if (pw_init(NULL, NULL))
409242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "pw_init() failed");
410242be47eSzrj 			else if ((pfd = pw_lock()) == -1)
411242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "pw_lock() failed");
412242be47eSzrj 			else if ((tfd = pw_tmp(-1)) == -1)
413242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "pw_tmp() failed");
414242be47eSzrj 			else if (pw_copy(pfd, tfd, pwd, old_pwd) == -1)
415242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "pw_copy() failed");
416242be47eSzrj 			else if (pw_mkdb(pwd->pw_name) == -1)
417242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "pw_mkdb() failed");
418242be47eSzrj 			else
419242be47eSzrj 				retval = PAM_SUCCESS;
420242be47eSzrj 			pw_fini();
421242be47eSzrj #ifdef YP
422242be47eSzrj 			break;
423242be47eSzrj 		case _PWF_NIS:
424242be47eSzrj 			yp_domain = yp_server = NULL;
425242be47eSzrj 			pam_get_data(pamh, "yp_domain", &yp_domain);
426242be47eSzrj 			pam_get_data(pamh, "yp_server", &yp_server);
427242be47eSzrj 			ypclnt = ypclnt_new(yp_domain,
428242be47eSzrj 			    "passwd.byname", yp_server);
429242be47eSzrj 			if (ypclnt == NULL) {
430242be47eSzrj 				retval = PAM_BUF_ERR;
431242be47eSzrj 			} else if (ypclnt_connect(ypclnt) == -1 ||
432242be47eSzrj 			    ypclnt_passwd(ypclnt, pwd, old_pass) == -1) {
433242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "%s", ypclnt->error);
434242be47eSzrj 				retval = PAM_SERVICE_ERR;
435242be47eSzrj 			} else {
436242be47eSzrj 				retval = PAM_SUCCESS;
437242be47eSzrj 			}
438242be47eSzrj 			ypclnt_free(ypclnt);
439242be47eSzrj 			break;
440242be47eSzrj 		default:
441242be47eSzrj 			openpam_log(PAM_LOG_ERROR, "unsupported source 0x%x",
442242be47eSzrj 			    pwd->pw_fields & _PWF_SOURCE);
443242be47eSzrj 			retval = PAM_SERVICE_ERR;
444242be47eSzrj 		}
445242be47eSzrj #endif
446242be47eSzrj 		free(old_pwd);
447242be47eSzrj 	}
448242be47eSzrj 	else {
449242be47eSzrj 		/* Very bad juju */
450242be47eSzrj 		retval = PAM_ABORT;
451242be47eSzrj 		PAM_LOG("Illegal 'flags'");
452242be47eSzrj 	}
453242be47eSzrj 
454242be47eSzrj 	return (retval);
455242be47eSzrj }
456242be47eSzrj 
457242be47eSzrj /* Mostly stolen from passwd(1)'s local_passwd.c - markm */
458242be47eSzrj 
459242be47eSzrj static unsigned char itoa64[] =		/* 0 ... 63 => ascii - 64 */
460242be47eSzrj 	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
461242be47eSzrj 
462242be47eSzrj static void
to64(char * s,long v,int n)463242be47eSzrj to64(char *s, long v, int n)
464242be47eSzrj {
465242be47eSzrj 	while (--n >= 0) {
466242be47eSzrj 		*s++ = itoa64[v&0x3f];
467242be47eSzrj 		v >>= 6;
468242be47eSzrj 	}
469242be47eSzrj }
470242be47eSzrj 
471242be47eSzrj /* Salt suitable for traditional DES and MD5 */
472242be47eSzrj static void
makesalt(char salt[SALTSIZE+1])473242be47eSzrj makesalt(char salt[SALTSIZE + 1])
474242be47eSzrj {
475242be47eSzrj 	int i;
476242be47eSzrj 
477242be47eSzrj 	/* These are not really random numbers, they are just
478242be47eSzrj 	 * numbers that change to thwart construction of a
479242be47eSzrj 	 * dictionary.
480242be47eSzrj 	 */
481242be47eSzrj 	for (i = 0; i < SALTSIZE; i += 4)
482242be47eSzrj 		to64(&salt[i], arc4random(), 4);
483242be47eSzrj 	salt[SALTSIZE] = '\0';
484242be47eSzrj }
485242be47eSzrj 
486242be47eSzrj PAM_MODULE_ENTRY("pam_unix");
487