1*242be47eSzrj /*-
2*242be47eSzrj  * Copyright 1998 Juniper Networks, Inc.
3*242be47eSzrj  * All rights reserved.
4*242be47eSzrj  * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
5*242be47eSzrj  * All rights reserved.
6*242be47eSzrj  *
7*242be47eSzrj  * Portions of this software was developed for the FreeBSD Project by
8*242be47eSzrj  * ThinkSec AS and NAI Labs, the Security Research Division of Network
9*242be47eSzrj  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10*242be47eSzrj  * ("CBOSS"), as part of the DARPA CHATS research program.
11*242be47eSzrj  *
12*242be47eSzrj  * Redistribution and use in source and binary forms, with or without
13*242be47eSzrj  * modification, are permitted provided that the following conditions
14*242be47eSzrj  * are met:
15*242be47eSzrj  * 1. Redistributions of source code must retain the above copyright
16*242be47eSzrj  *    notice, this list of conditions and the following disclaimer.
17*242be47eSzrj  * 2. Redistributions in binary form must reproduce the above copyright
18*242be47eSzrj  *    notice, this list of conditions and the following disclaimer in the
19*242be47eSzrj  *    documentation and/or other materials provided with the distribution.
20*242be47eSzrj  * 3. The name of the author may not be used to endorse or promote
21*242be47eSzrj  *    products derived from this software without specific prior written
22*242be47eSzrj  *    permission.
23*242be47eSzrj  *
24*242be47eSzrj  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25*242be47eSzrj  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26*242be47eSzrj  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27*242be47eSzrj  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28*242be47eSzrj  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29*242be47eSzrj  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30*242be47eSzrj  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31*242be47eSzrj  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32*242be47eSzrj  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33*242be47eSzrj  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34*242be47eSzrj  * SUCH DAMAGE.
35*242be47eSzrj  *
36*242be47eSzrj  * $FreeBSD: src/lib/libpam/modules/pam_unix/pam_unix.c,v 1.56 2011/11/05 10:00:29 ed Exp $
37*242be47eSzrj  */
38*242be47eSzrj 
39*242be47eSzrj #include <sys/param.h>
40*242be47eSzrj #include <sys/socket.h>
41*242be47eSzrj #include <sys/time.h>
42*242be47eSzrj #include <netinet/in.h>
43*242be47eSzrj #include <arpa/inet.h>
44*242be47eSzrj 
45*242be47eSzrj #include <login_cap.h>
46*242be47eSzrj #include <netdb.h>
47*242be47eSzrj #include <pwd.h>
48*242be47eSzrj #include <stdlib.h>
49*242be47eSzrj #include <string.h>
50*242be47eSzrj #include <stdio.h>
51*242be47eSzrj #include <syslog.h>
52*242be47eSzrj #include <time.h>
53*242be47eSzrj #include <unistd.h>
54*242be47eSzrj 
55*242be47eSzrj #include <libutil.h>
56*242be47eSzrj 
57*242be47eSzrj #ifdef YP
58*242be47eSzrj #include <ypclnt.h>
59*242be47eSzrj #endif
60*242be47eSzrj 
61*242be47eSzrj #define PAM_SM_AUTH
62*242be47eSzrj #define PAM_SM_ACCOUNT
63*242be47eSzrj #define	PAM_SM_PASSWORD
64*242be47eSzrj 
65*242be47eSzrj #include <security/pam_appl.h>
66*242be47eSzrj #include <security/pam_modules.h>
67*242be47eSzrj #include <security/pam_mod_misc.h>
68*242be47eSzrj 
69*242be47eSzrj #define PASSWORD_HASH		"sha512"
70*242be47eSzrj #define DEFAULT_WARN		(2L * 7L * 86400L)  /* Two weeks */
71*242be47eSzrj #define	SALTSIZE		32
72*242be47eSzrj 
73*242be47eSzrj #define	LOCKED_PREFIX		"*LOCKED*"
74*242be47eSzrj #define	LOCKED_PREFIX_LEN	(sizeof(LOCKED_PREFIX) - 1)
75*242be47eSzrj 
76*242be47eSzrj static void makesalt(char []);
77*242be47eSzrj 
78*242be47eSzrj static char password_hash[] =		PASSWORD_HASH;
79*242be47eSzrj 
80*242be47eSzrj #define PAM_OPT_LOCAL_PASS	"local_pass"
81*242be47eSzrj #define PAM_OPT_NIS_PASS	"nis_pass"
82*242be47eSzrj 
83*242be47eSzrj /*
84*242be47eSzrj  * authentication management
85*242be47eSzrj  */
86*242be47eSzrj PAM_EXTERN int
87*242be47eSzrj pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
88*242be47eSzrj     int argc __unused, const char *argv[] __unused)
89*242be47eSzrj {
90*242be47eSzrj 	login_cap_t *lc;
91*242be47eSzrj 	struct passwd *pwd;
92*242be47eSzrj 	int retval;
93*242be47eSzrj 	const char *pass, *user, *realpw, *prompt;
94*242be47eSzrj 	char *cryptpw;
95*242be47eSzrj 
96*242be47eSzrj 	if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) {
97*242be47eSzrj 		pwd = getpwnam(getlogin());
98*242be47eSzrj 	} else {
99*242be47eSzrj 		retval = pam_get_user(pamh, &user, NULL);
100*242be47eSzrj 		if (retval != PAM_SUCCESS)
101*242be47eSzrj 			return (retval);
102*242be47eSzrj 		pwd = getpwnam(user);
103*242be47eSzrj 	}
104*242be47eSzrj 
105*242be47eSzrj 	PAM_LOG("Got user: %s", user);
106*242be47eSzrj 
107*242be47eSzrj 	if (pwd != NULL) {
108*242be47eSzrj 		PAM_LOG("Doing real authentication");
109*242be47eSzrj 		realpw = pwd->pw_passwd;
110*242be47eSzrj 		if (realpw[0] == '\0') {
111*242be47eSzrj 			if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) &&
112*242be47eSzrj 			    openpam_get_option(pamh, PAM_OPT_NULLOK))
113*242be47eSzrj 				return (PAM_SUCCESS);
114*242be47eSzrj 			realpw = "*";
115*242be47eSzrj 		}
116*242be47eSzrj 		lc = login_getpwclass(pwd);
117*242be47eSzrj 	} else {
118*242be47eSzrj 		PAM_LOG("Doing dummy authentication");
119*242be47eSzrj 		realpw = "*";
120*242be47eSzrj 		lc = login_getclass(NULL);
121*242be47eSzrj 	}
122*242be47eSzrj 	prompt = login_getcapstr(lc, "passwd_prompt", NULL, NULL);
123*242be47eSzrj 	retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, prompt);
124*242be47eSzrj 	login_close(lc);
125*242be47eSzrj 	if (retval != PAM_SUCCESS)
126*242be47eSzrj 		return (retval);
127*242be47eSzrj 	PAM_LOG("Got password");
128*242be47eSzrj 	cryptpw = crypt(pass, realpw);
129*242be47eSzrj 	if (cryptpw != NULL && strcmp(cryptpw, realpw) == 0)
130*242be47eSzrj 		return (PAM_SUCCESS);
131*242be47eSzrj 
132*242be47eSzrj 	PAM_VERBOSE_ERROR("UNIX authentication refused");
133*242be47eSzrj 	return (PAM_AUTH_ERR);
134*242be47eSzrj }
135*242be47eSzrj 
136*242be47eSzrj PAM_EXTERN int
137*242be47eSzrj pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused,
138*242be47eSzrj     int argc __unused, const char *argv[] __unused)
139*242be47eSzrj {
140*242be47eSzrj 
141*242be47eSzrj 	return (PAM_SUCCESS);
142*242be47eSzrj }
143*242be47eSzrj 
144*242be47eSzrj /*
145*242be47eSzrj  * account management
146*242be47eSzrj  */
147*242be47eSzrj PAM_EXTERN int
148*242be47eSzrj pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused,
149*242be47eSzrj     int argc __unused, const char *argv[] __unused)
150*242be47eSzrj {
151*242be47eSzrj 	struct addrinfo hints, *res;
152*242be47eSzrj 	struct passwd *pwd;
153*242be47eSzrj 	struct timeval tp;
154*242be47eSzrj 	login_cap_t *lc;
155*242be47eSzrj 	time_t warntime;
156*242be47eSzrj 	int retval;
157*242be47eSzrj 	const char *user;
158*242be47eSzrj 	const void *rhost, *tty;
159*242be47eSzrj 	char rhostip[MAXHOSTNAMELEN] = "";
160*242be47eSzrj 
161*242be47eSzrj 	retval = pam_get_user(pamh, &user, NULL);
162*242be47eSzrj 	if (retval != PAM_SUCCESS)
163*242be47eSzrj 		return (retval);
164*242be47eSzrj 
165*242be47eSzrj 	if (user == NULL || (pwd = getpwnam(user)) == NULL)
166*242be47eSzrj 		return (PAM_SERVICE_ERR);
167*242be47eSzrj 
168*242be47eSzrj 	PAM_LOG("Got user: %s", user);
169*242be47eSzrj 
170*242be47eSzrj 	retval = pam_get_item(pamh, PAM_RHOST, &rhost);
171*242be47eSzrj 	if (retval != PAM_SUCCESS)
172*242be47eSzrj 		return (retval);
173*242be47eSzrj 
174*242be47eSzrj 	retval = pam_get_item(pamh, PAM_TTY, &tty);
175*242be47eSzrj 	if (retval != PAM_SUCCESS)
176*242be47eSzrj 		return (retval);
177*242be47eSzrj 
178*242be47eSzrj 	if (*pwd->pw_passwd == '\0' &&
179*242be47eSzrj 	    (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0)
180*242be47eSzrj 		return (PAM_NEW_AUTHTOK_REQD);
181*242be47eSzrj 
182*242be47eSzrj 	if (strncmp(pwd->pw_passwd, LOCKED_PREFIX, LOCKED_PREFIX_LEN) == 0)
183*242be47eSzrj 		return (PAM_AUTH_ERR);
184*242be47eSzrj 
185*242be47eSzrj 	lc = login_getpwclass(pwd);
186*242be47eSzrj 	if (lc == NULL) {
187*242be47eSzrj 		PAM_LOG("Unable to get login class for user %s", user);
188*242be47eSzrj 		return (PAM_SERVICE_ERR);
189*242be47eSzrj 	}
190*242be47eSzrj 
191*242be47eSzrj 	PAM_LOG("Got login_cap");
192*242be47eSzrj 
193*242be47eSzrj 	if (pwd->pw_change || pwd->pw_expire)
194*242be47eSzrj 		gettimeofday(&tp, NULL);
195*242be47eSzrj 
196*242be47eSzrj 	/*
197*242be47eSzrj 	 * Check pw_expire before pw_change - no point in letting the
198*242be47eSzrj 	 * user change the password on an expired account.
199*242be47eSzrj 	 */
200*242be47eSzrj 
201*242be47eSzrj 	if (pwd->pw_expire) {
202*242be47eSzrj 		warntime = login_getcaptime(lc, "warnexpire",
203*242be47eSzrj 		    DEFAULT_WARN, DEFAULT_WARN);
204*242be47eSzrj 		if (tp.tv_sec >= pwd->pw_expire) {
205*242be47eSzrj 			login_close(lc);
206*242be47eSzrj 			return (PAM_ACCT_EXPIRED);
207*242be47eSzrj 		} else if (pwd->pw_expire - tp.tv_sec < warntime &&
208*242be47eSzrj 		    (flags & PAM_SILENT) == 0) {
209*242be47eSzrj 			pam_error(pamh, "Warning: your account expires on %s",
210*242be47eSzrj 			    ctime(&pwd->pw_expire));
211*242be47eSzrj 		}
212*242be47eSzrj 	}
213*242be47eSzrj 
214*242be47eSzrj 	retval = PAM_SUCCESS;
215*242be47eSzrj 	if (pwd->pw_change) {
216*242be47eSzrj 		warntime = login_getcaptime(lc, "warnpassword",
217*242be47eSzrj 		    DEFAULT_WARN, DEFAULT_WARN);
218*242be47eSzrj 		if (tp.tv_sec >= pwd->pw_change) {
219*242be47eSzrj 			retval = PAM_NEW_AUTHTOK_REQD;
220*242be47eSzrj 		} else if (pwd->pw_change - tp.tv_sec < warntime &&
221*242be47eSzrj 		    (flags & PAM_SILENT) == 0) {
222*242be47eSzrj 			pam_error(pamh, "Warning: your password expires on %s",
223*242be47eSzrj 			    ctime(&pwd->pw_change));
224*242be47eSzrj 		}
225*242be47eSzrj 	}
226*242be47eSzrj 
227*242be47eSzrj 	/*
228*242be47eSzrj 	 * From here on, we must leave retval untouched (unless we
229*242be47eSzrj 	 * know we're going to fail), because we need to remember
230*242be47eSzrj 	 * whether we're supposed to return PAM_SUCCESS or
231*242be47eSzrj 	 * PAM_NEW_AUTHTOK_REQD.
232*242be47eSzrj 	 */
233*242be47eSzrj 
234*242be47eSzrj 	if (rhost && *(const char *)rhost != '\0') {
235*242be47eSzrj 		memset(&hints, 0, sizeof(hints));
236*242be47eSzrj 		hints.ai_family = AF_UNSPEC;
237*242be47eSzrj 		if (getaddrinfo(rhost, NULL, &hints, &res) == 0) {
238*242be47eSzrj 			getnameinfo(res->ai_addr, res->ai_addrlen,
239*242be47eSzrj 			    rhostip, sizeof(rhostip), NULL, 0,
240*242be47eSzrj 			    NI_NUMERICHOST);
241*242be47eSzrj 		}
242*242be47eSzrj 		if (res != NULL)
243*242be47eSzrj 			freeaddrinfo(res);
244*242be47eSzrj 	}
245*242be47eSzrj 
246*242be47eSzrj 	/*
247*242be47eSzrj 	 * Check host / tty / time-of-day restrictions
248*242be47eSzrj 	 */
249*242be47eSzrj 
250*242be47eSzrj 	if (!auth_hostok(lc, rhost, rhostip) ||
251*242be47eSzrj 	    !auth_ttyok(lc, tty) ||
252*242be47eSzrj 	    !auth_timeok(lc, time(NULL)))
253*242be47eSzrj 		retval = PAM_AUTH_ERR;
254*242be47eSzrj 
255*242be47eSzrj 	login_close(lc);
256*242be47eSzrj 
257*242be47eSzrj 	return (retval);
258*242be47eSzrj }
259*242be47eSzrj 
260*242be47eSzrj /*
261*242be47eSzrj  * password management
262*242be47eSzrj  *
263*242be47eSzrj  * standard Unix and NIS password changing
264*242be47eSzrj  */
265*242be47eSzrj PAM_EXTERN int
266*242be47eSzrj pam_sm_chauthtok(pam_handle_t *pamh, int flags,
267*242be47eSzrj     int argc __unused, const char *argv[] __unused)
268*242be47eSzrj {
269*242be47eSzrj #ifdef YP
270*242be47eSzrj 	struct ypclnt *ypclnt;
271*242be47eSzrj 	const void *yp_domain, *yp_server;
272*242be47eSzrj #endif
273*242be47eSzrj 	char salt[SALTSIZE + 1];
274*242be47eSzrj 	login_cap_t *lc;
275*242be47eSzrj 	struct passwd *pwd, *old_pwd;
276*242be47eSzrj 	const char *user, *old_pass, *new_pass;
277*242be47eSzrj 	char *encrypted;
278*242be47eSzrj 	time_t passwordtime;
279*242be47eSzrj 	int pfd, tfd, retval;
280*242be47eSzrj 
281*242be47eSzrj 	if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF))
282*242be47eSzrj 		pwd = getpwnam(getlogin());
283*242be47eSzrj 	else {
284*242be47eSzrj 		retval = pam_get_user(pamh, &user, NULL);
285*242be47eSzrj 		if (retval != PAM_SUCCESS)
286*242be47eSzrj 			return (retval);
287*242be47eSzrj 		pwd = getpwnam(user);
288*242be47eSzrj 	}
289*242be47eSzrj 
290*242be47eSzrj 	if (pwd == NULL)
291*242be47eSzrj 		return (PAM_AUTHTOK_RECOVERY_ERR);
292*242be47eSzrj 
293*242be47eSzrj 	PAM_LOG("Got user: %s", user);
294*242be47eSzrj 
295*242be47eSzrj 	if (flags & PAM_PRELIM_CHECK) {
296*242be47eSzrj 
297*242be47eSzrj 		PAM_LOG("PRELIM round");
298*242be47eSzrj 
299*242be47eSzrj 		if (getuid() == 0 &&
300*242be47eSzrj 		    (pwd->pw_fields & _PWF_SOURCE) == _PWF_FILES)
301*242be47eSzrj 			/* root doesn't need the old password */
302*242be47eSzrj 			return (pam_set_item(pamh, PAM_OLDAUTHTOK, ""));
303*242be47eSzrj #ifdef YP
304*242be47eSzrj 		if (getuid() == 0 &&
305*242be47eSzrj 		    (pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
306*242be47eSzrj 
307*242be47eSzrj 			yp_domain = yp_server = NULL;
308*242be47eSzrj 			pam_get_data(pamh, "yp_domain", &yp_domain);
309*242be47eSzrj 			pam_get_data(pamh, "yp_server", &yp_server);
310*242be47eSzrj 
311*242be47eSzrj 			ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_server);
312*242be47eSzrj 			if (ypclnt == NULL)
313*242be47eSzrj 				return (PAM_BUF_ERR);
314*242be47eSzrj 
315*242be47eSzrj 			if (ypclnt_connect(ypclnt) == -1) {
316*242be47eSzrj 				ypclnt_free(ypclnt);
317*242be47eSzrj 				return (PAM_SERVICE_ERR);
318*242be47eSzrj 			}
319*242be47eSzrj 
320*242be47eSzrj 			retval = ypclnt_havepasswdd(ypclnt);
321*242be47eSzrj 			ypclnt_free(ypclnt);
322*242be47eSzrj 			if (retval == 1)
323*242be47eSzrj 				return (pam_set_item(pamh, PAM_OLDAUTHTOK, ""));
324*242be47eSzrj 			else if (retval == -1)
325*242be47eSzrj 				return (PAM_SERVICE_ERR);
326*242be47eSzrj 		}
327*242be47eSzrj #endif
328*242be47eSzrj 		if (pwd->pw_passwd[0] == '\0'
329*242be47eSzrj 		    && openpam_get_option(pamh, PAM_OPT_NULLOK)) {
330*242be47eSzrj 			/*
331*242be47eSzrj 			 * No password case. XXX Are we giving too much away
332*242be47eSzrj 			 * by not prompting for a password?
333*242be47eSzrj 			 * XXX check PAM_DISALLOW_NULL_AUTHTOK
334*242be47eSzrj 			 */
335*242be47eSzrj 			old_pass = "";
336*242be47eSzrj 		} else {
337*242be47eSzrj 			retval = pam_get_authtok(pamh,
338*242be47eSzrj 			    PAM_OLDAUTHTOK, &old_pass, NULL);
339*242be47eSzrj 			if (retval != PAM_SUCCESS)
340*242be47eSzrj 				return (retval);
341*242be47eSzrj 		}
342*242be47eSzrj 		PAM_LOG("Got old password");
343*242be47eSzrj 		/* always encrypt first */
344*242be47eSzrj 		encrypted = crypt(old_pass, pwd->pw_passwd);
345*242be47eSzrj 		if (old_pass[0] == '\0' &&
346*242be47eSzrj 		    !openpam_get_option(pamh, PAM_OPT_NULLOK))
347*242be47eSzrj 			return (PAM_PERM_DENIED);
348*242be47eSzrj 		if (encrypted == NULL || strcmp(encrypted, pwd->pw_passwd) != 0)
349*242be47eSzrj 			return (PAM_PERM_DENIED);
350*242be47eSzrj 	}
351*242be47eSzrj 	else if (flags & PAM_UPDATE_AUTHTOK) {
352*242be47eSzrj 		PAM_LOG("UPDATE round");
353*242be47eSzrj 
354*242be47eSzrj 		retval = pam_get_authtok(pamh,
355*242be47eSzrj 		    PAM_OLDAUTHTOK, &old_pass, NULL);
356*242be47eSzrj 		if (retval != PAM_SUCCESS)
357*242be47eSzrj 			return (retval);
358*242be47eSzrj 		PAM_LOG("Got old password");
359*242be47eSzrj 
360*242be47eSzrj 		/* get new password */
361*242be47eSzrj 		for (;;) {
362*242be47eSzrj 			retval = pam_get_authtok(pamh,
363*242be47eSzrj 			    PAM_AUTHTOK, &new_pass, NULL);
364*242be47eSzrj 			if (retval != PAM_TRY_AGAIN)
365*242be47eSzrj 				break;
366*242be47eSzrj 			pam_error(pamh, "Mismatch; try again, EOF to quit.");
367*242be47eSzrj 		}
368*242be47eSzrj 		PAM_LOG("Got new password");
369*242be47eSzrj 		if (retval != PAM_SUCCESS) {
370*242be47eSzrj 			PAM_VERBOSE_ERROR("Unable to get new password");
371*242be47eSzrj 			return (retval);
372*242be47eSzrj 		}
373*242be47eSzrj 
374*242be47eSzrj 		if (getuid() != 0 && new_pass[0] == '\0' &&
375*242be47eSzrj 		    !openpam_get_option(pamh, PAM_OPT_NULLOK))
376*242be47eSzrj 			return (PAM_PERM_DENIED);
377*242be47eSzrj 
378*242be47eSzrj 		if ((old_pwd = pw_dup(pwd)) == NULL)
379*242be47eSzrj 			return (PAM_BUF_ERR);
380*242be47eSzrj 
381*242be47eSzrj 		lc = login_getclass(pwd->pw_class);
382*242be47eSzrj 		if (login_setcryptfmt(lc, password_hash, NULL) == NULL)
383*242be47eSzrj 			openpam_log(PAM_LOG_ERROR,
384*242be47eSzrj 			    "can't set password cipher, relying on default");
385*242be47eSzrj 
386*242be47eSzrj 		/* set password expiry date */
387*242be47eSzrj 		pwd->pw_change = 0;
388*242be47eSzrj 		passwordtime = login_getcaptime(lc, "passwordtime", 0, 0);
389*242be47eSzrj 		if (passwordtime > 0)
390*242be47eSzrj 			pwd->pw_change = time(NULL) + passwordtime;
391*242be47eSzrj 
392*242be47eSzrj 		login_close(lc);
393*242be47eSzrj 		makesalt(salt);
394*242be47eSzrj 		pwd->pw_passwd = crypt(new_pass, salt);
395*242be47eSzrj #ifdef YP
396*242be47eSzrj 		switch (old_pwd->pw_fields & _PWF_SOURCE) {
397*242be47eSzrj 		case _PWF_FILES:
398*242be47eSzrj #endif
399*242be47eSzrj 			retval = PAM_SERVICE_ERR;
400*242be47eSzrj 			if (pw_init(NULL, NULL))
401*242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "pw_init() failed");
402*242be47eSzrj 			else if ((pfd = pw_lock()) == -1)
403*242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "pw_lock() failed");
404*242be47eSzrj 			else if ((tfd = pw_tmp(-1)) == -1)
405*242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "pw_tmp() failed");
406*242be47eSzrj 			else if (pw_copy(pfd, tfd, pwd, old_pwd) == -1)
407*242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "pw_copy() failed");
408*242be47eSzrj 			else if (pw_mkdb(pwd->pw_name) == -1)
409*242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "pw_mkdb() failed");
410*242be47eSzrj 			else
411*242be47eSzrj 				retval = PAM_SUCCESS;
412*242be47eSzrj 			pw_fini();
413*242be47eSzrj #ifdef YP
414*242be47eSzrj 			break;
415*242be47eSzrj 		case _PWF_NIS:
416*242be47eSzrj 			yp_domain = yp_server = NULL;
417*242be47eSzrj 			pam_get_data(pamh, "yp_domain", &yp_domain);
418*242be47eSzrj 			pam_get_data(pamh, "yp_server", &yp_server);
419*242be47eSzrj 			ypclnt = ypclnt_new(yp_domain,
420*242be47eSzrj 			    "passwd.byname", yp_server);
421*242be47eSzrj 			if (ypclnt == NULL) {
422*242be47eSzrj 				retval = PAM_BUF_ERR;
423*242be47eSzrj 			} else if (ypclnt_connect(ypclnt) == -1 ||
424*242be47eSzrj 			    ypclnt_passwd(ypclnt, pwd, old_pass) == -1) {
425*242be47eSzrj 				openpam_log(PAM_LOG_ERROR, "%s", ypclnt->error);
426*242be47eSzrj 				retval = PAM_SERVICE_ERR;
427*242be47eSzrj 			} else {
428*242be47eSzrj 				retval = PAM_SUCCESS;
429*242be47eSzrj 			}
430*242be47eSzrj 			ypclnt_free(ypclnt);
431*242be47eSzrj 			break;
432*242be47eSzrj 		default:
433*242be47eSzrj 			openpam_log(PAM_LOG_ERROR, "unsupported source 0x%x",
434*242be47eSzrj 			    pwd->pw_fields & _PWF_SOURCE);
435*242be47eSzrj 			retval = PAM_SERVICE_ERR;
436*242be47eSzrj 		}
437*242be47eSzrj #endif
438*242be47eSzrj 		free(old_pwd);
439*242be47eSzrj 	}
440*242be47eSzrj 	else {
441*242be47eSzrj 		/* Very bad juju */
442*242be47eSzrj 		retval = PAM_ABORT;
443*242be47eSzrj 		PAM_LOG("Illegal 'flags'");
444*242be47eSzrj 	}
445*242be47eSzrj 
446*242be47eSzrj 	return (retval);
447*242be47eSzrj }
448*242be47eSzrj 
449*242be47eSzrj /* Mostly stolen from passwd(1)'s local_passwd.c - markm */
450*242be47eSzrj 
451*242be47eSzrj static unsigned char itoa64[] =		/* 0 ... 63 => ascii - 64 */
452*242be47eSzrj 	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
453*242be47eSzrj 
454*242be47eSzrj static void
455*242be47eSzrj to64(char *s, long v, int n)
456*242be47eSzrj {
457*242be47eSzrj 	while (--n >= 0) {
458*242be47eSzrj 		*s++ = itoa64[v&0x3f];
459*242be47eSzrj 		v >>= 6;
460*242be47eSzrj 	}
461*242be47eSzrj }
462*242be47eSzrj 
463*242be47eSzrj /* Salt suitable for traditional DES and MD5 */
464*242be47eSzrj static void
465*242be47eSzrj makesalt(char salt[SALTSIZE + 1])
466*242be47eSzrj {
467*242be47eSzrj 	int i;
468*242be47eSzrj 
469*242be47eSzrj 	/* These are not really random numbers, they are just
470*242be47eSzrj 	 * numbers that change to thwart construction of a
471*242be47eSzrj 	 * dictionary.
472*242be47eSzrj 	 */
473*242be47eSzrj 	for (i = 0; i < SALTSIZE; i += 4)
474*242be47eSzrj 		to64(&salt[i], arc4random(), 4);
475*242be47eSzrj 	salt[SALTSIZE] = '\0';
476*242be47eSzrj }
477*242be47eSzrj 
478*242be47eSzrj PAM_MODULE_ENTRY("pam_unix");
479