pam_unix.c revision 90229
1/*-
2 * Copyright 1998 Juniper Networks, Inc.
3 * All rights reserved.
4 * Copyright (c) 2002 Networks Associates Technologies, Inc.
5 * All rights reserved.
6 *
7 * Portions of this software was developed for the FreeBSD Project by
8 * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10 * ("CBOSS"), as part of the DARPA CHATS research program.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. The name of the author may not be used to endorse or promote
21 *    products derived from this software without specific prior written
22 *    permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_unix/pam_unix.c 90229 2002-02-05 06:08:26Z des $");
39
40#include <sys/param.h>
41#include <sys/socket.h>
42#include <sys/time.h>
43#include <netinet/in.h>
44#include <arpa/inet.h>
45
46#ifdef YP
47#include <rpc/rpc.h>
48#include <rpcsvc/yp_prot.h>
49#include <rpcsvc/ypclnt.h>
50#include <rpcsvc/yppasswd.h>
51#endif
52
53#include <login_cap.h>
54#include <netdb.h>
55#include <pwd.h>
56#include <stdlib.h>
57#include <string.h>
58#include <stdio.h>
59#include <unistd.h>
60
61#include <pw_copy.h>
62#include <pw_util.h>
63
64#ifdef YP
65#include <pw_yp.h>
66#include "yppasswd_private.h"
67#endif
68
69#define PAM_SM_AUTH
70#define PAM_SM_ACCOUNT
71#define	PAM_SM_SESSION
72#define	PAM_SM_PASSWORD
73
74#include <security/pam_appl.h>
75#include <security/pam_modules.h>
76#include <security/pam_mod_misc.h>
77
78#define USER_PROMPT		"Username: "
79#define PASSWORD_PROMPT		"Password:"
80#define PASSWORD_PROMPT_EXPIRED	"\nPassword expired\nOld Password:"
81#define NEW_PASSWORD_PROMPT_1	"New Password:"
82#define NEW_PASSWORD_PROMPT_2	"New Password (again):"
83#define PASSWORD_HASH		"md5"
84#define DEFAULT_WARN		(2L * 7L * 86400L)  /* Two weeks */
85#define	MAX_TRIES		3
86
87static char password_prompt_def[] = PASSWORD_PROMPT;
88static char password_hash[] = PASSWORD_HASH;
89
90enum {
91	PAM_OPT_AUTH_AS_SELF	= PAM_OPT_STD_MAX,
92	PAM_OPT_NULLOK,
93	PAM_OPT_LOCAL_PASS,
94	PAM_OPT_NIS_PASS
95};
96
97static struct opttab other_options[] = {
98	{ "auth_as_self",	PAM_OPT_AUTH_AS_SELF },
99	{ "nullok",		PAM_OPT_NULLOK },
100	{ "local_pass",		PAM_OPT_LOCAL_PASS },
101	{ "nis_pass",		PAM_OPT_NIS_PASS },
102	{ NULL, 0 }
103};
104
105#ifdef YP
106int pam_use_yp = 0;
107int yp_errno = YP_TRUE;
108#endif
109
110char *tempname = NULL;
111static int local_passwd(const char *user, const char *pass);
112#ifdef YP
113static int yp_passwd(const char *user, const char *pass);
114#endif
115
116/*
117 * authentication management
118 */
119PAM_EXTERN int
120pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc, const char **argv)
121{
122	login_cap_t *lc;
123	struct options options;
124	struct passwd *pwd;
125	int retval;
126	const char *pass, *user;
127	char *encrypted, *password_prompt;
128
129	pam_std_option(&options, other_options, argc, argv);
130
131	PAM_LOG("Options processed");
132
133	if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL))
134		pwd = getpwnam(getlogin());
135	else {
136		retval = pam_get_user(pamh, &user, NULL);
137		if (retval != PAM_SUCCESS)
138			PAM_RETURN(retval);
139		pwd = getpwnam(user);
140	}
141
142	PAM_LOG("Got user: %s", user);
143
144	lc = login_getclass(NULL);
145	password_prompt = login_getcapstr(lc, "passwd_prompt",
146	    password_prompt_def, password_prompt_def);
147	login_close(lc);
148	lc = NULL;
149
150	if (pwd != NULL) {
151
152		PAM_LOG("Doing real authentication");
153
154		if (pwd->pw_passwd[0] == '\0'
155		    && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) {
156			/*
157			 * No password case. XXX Are we giving too much away
158			 * by not prompting for a password?
159			 */
160			PAM_LOG("No password, and null password OK");
161			PAM_RETURN(PAM_SUCCESS);
162		}
163		else {
164			retval = pam_get_pass(pamh, &pass, password_prompt,
165			    &options);
166			if (retval != PAM_SUCCESS)
167				PAM_RETURN(retval);
168			PAM_LOG("Got password");
169		}
170		encrypted = crypt(pass, pwd->pw_passwd);
171		if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0')
172			encrypted = strdup(":");
173
174		PAM_LOG("Encrypted password 1 is: %s", encrypted);
175		PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd);
176
177		retval = strcmp(encrypted, pwd->pw_passwd) == 0 ?
178		    PAM_SUCCESS : PAM_AUTH_ERR;
179	}
180	else {
181
182		PAM_LOG("Doing dummy authentication");
183
184		/*
185		 * User unknown.
186		 * Encrypt a dummy password so as to not give away too much.
187		 */
188		retval = pam_get_pass(pamh, &pass, password_prompt,
189		    &options);
190		if (retval != PAM_SUCCESS)
191			PAM_RETURN(retval);
192		PAM_LOG("Got password");
193		crypt(pass, "xx");
194		retval = PAM_AUTH_ERR;
195	}
196
197	/*
198	 * The PAM infrastructure will obliterate the cleartext
199	 * password before returning to the application.
200	 */
201	if (retval != PAM_SUCCESS)
202		PAM_VERBOSE_ERROR("UNIX authentication refused");
203
204	PAM_RETURN(retval);
205}
206
207PAM_EXTERN int
208pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv)
209{
210	struct options options;
211
212	pam_std_option(&options, other_options, argc, argv);
213
214	PAM_LOG("Options processed");
215
216	PAM_RETURN(PAM_SUCCESS);
217}
218
219/*
220 * account management
221 */
222PAM_EXTERN int
223pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, int argc, const char **argv)
224{
225	struct addrinfo hints, *res;
226	struct options options;
227	struct passwd *pwd;
228	struct timeval tp;
229	login_cap_t *lc;
230	time_t warntime;
231	int retval;
232	const char *rhost, *tty, *user;
233	char rhostip[MAXHOSTNAMELEN];
234	char buf[128];
235
236	pam_std_option(&options, other_options, argc, argv);
237
238	PAM_LOG("Options processed");
239
240	retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
241	if (retval != PAM_SUCCESS)
242		PAM_RETURN(retval);
243
244	if (user == NULL || (pwd = getpwnam(user)) == NULL)
245		PAM_RETURN(PAM_SERVICE_ERR);
246
247	PAM_LOG("Got user: %s", user);
248
249	retval = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost);
250	if (retval != PAM_SUCCESS)
251		PAM_RETURN(retval);
252
253	retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty);
254	if (retval != PAM_SUCCESS)
255		PAM_RETURN(retval);
256
257	if (*pwd->pw_passwd == '\0' &&
258	    (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0)
259		return (PAM_NEW_AUTHTOK_REQD);
260
261	lc = login_getpwclass(pwd);
262	if (lc == NULL) {
263		PAM_LOG("Unable to get login class for user %s", user);
264		return (PAM_SERVICE_ERR);
265	}
266
267	PAM_LOG("Got login_cap");
268
269	if (pwd->pw_change || pwd->pw_expire)
270		gettimeofday(&tp, NULL);
271
272	/*
273	 * Check pw_expire before pw_change - no point in letting the
274	 * user change the password on an expired account.
275	 */
276
277	if (pwd->pw_expire) {
278		warntime = login_getcaptime(lc, "warnexpire",
279		    DEFAULT_WARN, DEFAULT_WARN);
280		if (tp.tv_sec >= pwd->pw_expire) {
281			login_close(lc);
282			PAM_RETURN(PAM_ACCT_EXPIRED);
283		} else if (pwd->pw_expire - tp.tv_sec < warntime &&
284		    (flags & PAM_SILENT) == 0) {
285			snprintf(buf, sizeof(buf),
286			    "Warning: your account expires on %s",
287			    ctime(&pwd->pw_expire));
288			pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL);
289		}
290	}
291
292	retval = PAM_SUCCESS;
293	if (pwd->pw_change) {
294		warntime = login_getcaptime(lc, "warnpassword",
295		    DEFAULT_WARN, DEFAULT_WARN);
296		if (tp.tv_sec >= pwd->pw_change) {
297			retval = PAM_NEW_AUTHTOK_REQD;
298		} else if (pwd->pw_change - tp.tv_sec < warntime &&
299		    (flags & PAM_SILENT) == 0) {
300			snprintf(buf, sizeof(buf),
301			    "Warning: your password expires on %s",
302			    ctime(&pwd->pw_change));
303			pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL);
304		}
305	}
306
307	/*
308	 * From here on, we must leave retval untouched (unless we
309	 * know we're going to fail), because we need to remember
310	 * whether we're supposed to return PAM_SUCCESS or
311	 * PAM_NEW_AUTHTOK_REQD.
312	 */
313
314	if (rhost) {
315		memset(&hints, 0, sizeof(hints));
316		hints.ai_family = AF_UNSPEC;
317		if (getaddrinfo(rhost, NULL, &hints, &res) == 0) {
318			getnameinfo(res->ai_addr, res->ai_addrlen,
319			    rhostip, sizeof(rhostip), NULL, 0,
320			    NI_NUMERICHOST|NI_WITHSCOPEID);
321		}
322		if (res != NULL)
323			freeaddrinfo(res);
324	}
325
326	/*
327	 * Check host / tty / time-of-day restrictions
328	 */
329
330	if (!auth_hostok(lc, rhost, rhostip) ||
331	    !auth_ttyok(lc, tty) ||
332	    !auth_timeok(lc, time(NULL)))
333		retval = PAM_AUTH_ERR;
334
335	login_close(lc);
336
337	PAM_RETURN(retval);
338}
339
340/*
341 * session management
342 *
343 * logging only
344 */
345PAM_EXTERN int
346pam_sm_open_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv)
347{
348	struct options options;
349
350	pam_std_option(&options, other_options, argc, argv);
351
352	PAM_LOG("Options processed");
353
354	PAM_RETURN(PAM_SUCCESS);
355}
356
357PAM_EXTERN int
358pam_sm_close_session(pam_handle_t *pamh __unused, int flags __unused, int argc, const char **argv)
359{
360	struct options options;
361
362	pam_std_option(&options, other_options, argc, argv);
363
364	PAM_LOG("Options processed");
365
366	PAM_RETURN(PAM_SUCCESS);
367}
368
369/*
370 * password management
371 *
372 * standard Unix and NIS password changing
373 */
374PAM_EXTERN int
375pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
376{
377	struct options options;
378	struct passwd *pwd;
379	int retval, retry, res, got;
380	const char *user, *pass;
381	char *new_pass, *new_pass_, *encrypted;
382
383	pam_std_option(&options, other_options, argc, argv);
384
385	PAM_LOG("Options processed");
386
387	if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL))
388		pwd = getpwnam(getlogin());
389	else {
390		retval = pam_get_user(pamh, &user, NULL);
391		if (retval != PAM_SUCCESS)
392			PAM_RETURN(retval);
393		pwd = getpwnam(user);
394	}
395
396	PAM_LOG("Got user: %s", user);
397
398	if (flags & PAM_PRELIM_CHECK) {
399
400		PAM_LOG("PRELIM round; checking user password");
401
402		if (pwd->pw_passwd[0] == '\0'
403		    && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) {
404			/*
405			 * No password case. XXX Are we giving too much away
406			 * by not prompting for a password?
407			 */
408			PAM_LOG("No password, and null password OK");
409			PAM_RETURN(PAM_SUCCESS);
410		}
411		else {
412			retval = pam_get_pass(pamh, &pass,
413			    PASSWORD_PROMPT_EXPIRED, &options);
414			if (retval != PAM_SUCCESS)
415				PAM_RETURN(retval);
416			PAM_LOG("Got password: %s", pass);
417		}
418		encrypted = crypt(pass, pwd->pw_passwd);
419		if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0')
420			encrypted = strdup(":");
421
422		PAM_LOG("Encrypted password 1 is: %s", encrypted);
423		PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd);
424
425		if (strcmp(encrypted, pwd->pw_passwd) != 0)
426			PAM_RETURN(PAM_AUTH_ERR);
427
428		retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *)pass);
429		pass = NULL;
430		if (retval != PAM_SUCCESS)
431			PAM_RETURN(retval);
432
433		PAM_LOG("Stashed old password");
434
435		retval = pam_set_item(pamh, PAM_AUTHTOK, (const void *)pass);
436		if (retval != PAM_SUCCESS)
437			PAM_RETURN(retval);
438
439		PAM_LOG("Voided old password");
440
441		PAM_RETURN(PAM_SUCCESS);
442	}
443	else if (flags & PAM_UPDATE_AUTHTOK) {
444		PAM_LOG("UPDATE round; checking user password");
445
446		retval = pam_get_item(pamh, PAM_OLDAUTHTOK,
447		    (const void **)&pass);
448		if (retval != PAM_SUCCESS)
449			PAM_RETURN(retval);
450
451		PAM_LOG("Got old password: %s", pass);
452
453		got = 0;
454		retry = 0;
455		while (retry++ < MAX_TRIES) {
456			new_pass = NULL;
457			retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
458			    NEW_PASSWORD_PROMPT_1, &new_pass);
459
460			if (new_pass == NULL)
461				new_pass = strdup("");
462
463			if (retval == PAM_SUCCESS) {
464				new_pass_ = NULL;
465				retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
466				    NEW_PASSWORD_PROMPT_2, &new_pass_);
467
468				if (new_pass_ == NULL)
469					new_pass_ = strdup("");
470
471				if (retval == PAM_SUCCESS) {
472					if (strcmp(new_pass, new_pass_) == 0) {
473						got = 1;
474						break;
475					}
476					else
477						PAM_VERBOSE_ERROR("Password mismatch");
478				}
479			}
480		}
481
482		if (!got) {
483			PAM_VERBOSE_ERROR("Unable to get valid password");
484			PAM_RETURN(PAM_PERM_DENIED);
485		}
486
487		PAM_LOG("Got new password: %s", new_pass);
488
489#ifdef YP
490		/* If NIS is set in the passwd database, use it */
491		res = use_yp(user, 0, 0);
492		if (res == USER_YP_ONLY) {
493			if (!pam_test_option(&options, PAM_OPT_LOCAL_PASS,
494			    NULL))
495				retval = yp_passwd(user, new_pass);
496			else {
497				/* Reject 'local' flag if NIS is on and the user
498				 * is not local
499				 */
500				retval = PAM_PERM_DENIED;
501				PAM_LOG("Unknown local user: %s", user);
502			}
503		}
504		else if (res == USER_LOCAL_ONLY) {
505			if (!pam_test_option(&options, PAM_OPT_NIS_PASS, NULL))
506				retval = local_passwd(user, new_pass);
507			else {
508				/* Reject 'nis' flag if user is only local */
509				retval = PAM_PERM_DENIED;
510				PAM_LOG("Unknown NIS user: %s", user);
511			}
512		}
513		else if (res == USER_YP_AND_LOCAL) {
514			if (pam_test_option(&options, PAM_OPT_NIS_PASS, NULL))
515				retval = yp_passwd(user, new_pass);
516			else
517				retval = local_passwd(user, new_pass);
518		}
519		else
520			retval = PAM_ABORT; /* Bad juju */
521#else
522		retval = local_passwd(user, new_pass);
523#endif
524
525		/* XXX wipe the mem as well */
526		pass = NULL;
527		new_pass = NULL;
528	}
529	else {
530		/* Very bad juju */
531		retval = PAM_ABORT;
532		PAM_LOG("Illegal 'flags'");
533	}
534
535	PAM_RETURN(retval);
536}
537
538/* Mostly stolen from passwd(1)'s local_passwd.c - markm */
539
540static unsigned char itoa64[] =		/* 0 ... 63 => ascii - 64 */
541	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
542
543static void
544to64(char *s, long v, int n)
545{
546	while (--n >= 0) {
547		*s++ = itoa64[v&0x3f];
548		v >>= 6;
549	}
550}
551
552static int
553local_passwd(const char *user, const char *pass)
554{
555	login_cap_t * lc;
556	struct passwd *pwd;
557	struct timeval tv;
558	int pfd, tfd;
559	char *crypt_type, salt[32];
560
561	pwd = getpwnam(user);
562	if (pwd == NULL)
563		return(PAM_ABORT); /* Really bad things */
564
565#ifdef YP
566	pwd = (struct passwd *)&local_password;
567#endif
568	pw_init();
569
570	pwd->pw_change = 0;
571	lc = login_getclass(NULL);
572	crypt_type = login_getcapstr(lc, "passwd_format",
573		password_hash, password_hash);
574	if (login_setcryptfmt(lc, crypt_type, NULL) == NULL)
575		syslog(LOG_ERR, "cannot set password cipher");
576	login_close(lc);
577	/* Salt suitable for anything */
578	gettimeofday(&tv, 0);
579	to64(&salt[0], (tv.tv_sec ^ random()) * tv.tv_usec, 3);
580	to64(&salt[3], (getpid() ^ random()) * tv.tv_usec, 2);
581	to64(&salt[5], (getppid() ^ random()) * tv.tv_usec, 3);
582	to64(&salt[8], (getuid() ^ random()) * tv.tv_usec, 5);
583	to64(&salt[13], (getgid() ^ random()) * tv.tv_usec, 5);
584	to64(&salt[17], random() * tv.tv_usec, 5);
585	to64(&salt[22], random() * tv.tv_usec, 5);
586	salt[27] = '\0';
587
588	pwd->pw_passwd = crypt(pass, salt);
589
590	pfd = pw_lock();
591	tfd = pw_tmp();
592	pw_copy(pfd, tfd, pwd);
593
594	if (!pw_mkdb(user))
595		pw_error((char *)NULL, 0, 1);
596
597	return PAM_SUCCESS;
598}
599
600#ifdef YP
601/* Stolen from src/usr.bin/passwd/yp_passwd.c, carrying copyrights of:
602 * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
603 * Copyright (c) 1994 Olaf Kirch <okir@monad.swb.de>
604 * Copyright (c) 1995 Bill Paul <wpaul@ctr.columbia.edu>
605 */
606int
607yp_passwd(const char *user, const char *pass)
608{
609	struct master_yppasswd master_yppasswd;
610	struct passwd *pwd;
611	struct rpc_err err;
612	struct timeval tv;
613	struct yppasswd yppasswd;
614	CLIENT *clnt;
615	login_cap_t *lc;
616	int    *status;
617	uid_t	uid;
618	char   *master, sockname[] = YP_SOCKNAME, salt[32];
619
620	_use_yp = 1;
621
622	uid = getuid();
623
624	master = get_yp_master(1);
625	if (master == NULL)
626		return PAM_ABORT; /* Major disaster */
627
628	/*
629	 * It is presumed that by the time we get here, use_yp()
630	 * has been called and that we have verified that the user
631	 * actually exists. This being the case, the yp_password
632	 * stucture has already been filled in for us.
633	 */
634
635	/* Use the correct password */
636	pwd = (struct passwd *)&yp_password;
637
638	pwd->pw_change = 0;
639
640	/* Initialize password information */
641	if (suser_override) {
642		master_yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd);
643		master_yppasswd.newpw.pw_name = strdup(pwd->pw_name);
644		master_yppasswd.newpw.pw_uid = pwd->pw_uid;
645		master_yppasswd.newpw.pw_gid = pwd->pw_gid;
646		master_yppasswd.newpw.pw_expire = pwd->pw_expire;
647		master_yppasswd.newpw.pw_change = pwd->pw_change;
648		master_yppasswd.newpw.pw_fields = pwd->pw_fields;
649		master_yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos);
650		master_yppasswd.newpw.pw_dir = strdup(pwd->pw_dir);
651		master_yppasswd.newpw.pw_shell = strdup(pwd->pw_shell);
652		master_yppasswd.newpw.pw_class = pwd->pw_class != NULL ?
653					strdup(pwd->pw_class) : strdup("");
654		master_yppasswd.oldpass = strdup("");
655		master_yppasswd.domain = yp_domain;
656	} else {
657		yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd);
658		yppasswd.newpw.pw_name = strdup(pwd->pw_name);
659		yppasswd.newpw.pw_uid = pwd->pw_uid;
660		yppasswd.newpw.pw_gid = pwd->pw_gid;
661		yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos);
662		yppasswd.newpw.pw_dir = strdup(pwd->pw_dir);
663		yppasswd.newpw.pw_shell = strdup(pwd->pw_shell);
664		yppasswd.oldpass = strdup("");
665	}
666
667	if (login_setcryptfmt(lc, "md5", NULL) == NULL)
668		syslog(LOG_ERR, "cannot set password cipher");
669	login_close(lc);
670	/* Salt suitable for anything */
671	gettimeofday(&tv, 0);
672	to64(&salt[0], (tv.tv_sec ^ random()) * tv.tv_usec, 3);
673	to64(&salt[3], (getpid() ^ random()) * tv.tv_usec, 2);
674	to64(&salt[5], (getppid() ^ random()) * tv.tv_usec, 3);
675	to64(&salt[8], (getuid() ^ random()) * tv.tv_usec, 5);
676	to64(&salt[13], (getgid() ^ random()) * tv.tv_usec, 5);
677	to64(&salt[17], random() * tv.tv_usec, 5);
678	to64(&salt[22], random() * tv.tv_usec, 5);
679	salt[27] = '\0';
680
681	if (suser_override)
682		master_yppasswd.newpw.pw_passwd = crypt(pass, salt);
683	else
684		yppasswd.newpw.pw_passwd = crypt(pass, salt);
685
686	if (suser_override) {
687		if ((clnt = clnt_create(sockname, MASTER_YPPASSWDPROG,
688		    MASTER_YPPASSWDVERS, "unix")) == NULL) {
689			syslog(LOG_ERR,
690			    "Cannot contact rpc.yppasswdd on host %s: %s",
691			    master, clnt_spcreateerror(""));
692			return PAM_ABORT;
693		}
694	}
695	else {
696		if ((clnt = clnt_create(master, YPPASSWDPROG,
697		    YPPASSWDVERS, "udp")) == NULL) {
698			syslog(LOG_ERR,
699			    "Cannot contact rpc.yppasswdd on host %s: %s",
700			    master, clnt_spcreateerror(""));
701			return PAM_ABORT;
702		}
703	}
704	/*
705	 * The yppasswd.x file said `unix authentication required',
706	 * so I added it. This is the only reason it is in here.
707	 * My yppasswdd doesn't use it, but maybe some others out there
708	 * do. 					--okir
709	 */
710	clnt->cl_auth = authunix_create_default();
711
712	if (suser_override)
713		status = yppasswdproc_update_master_1(&master_yppasswd, clnt);
714	else
715		status = yppasswdproc_update_1(&yppasswd, clnt);
716
717	clnt_geterr(clnt, &err);
718
719	auth_destroy(clnt->cl_auth);
720	clnt_destroy(clnt);
721
722	if (err.re_status != RPC_SUCCESS || status == NULL || *status)
723		return PAM_ABORT;
724
725	return (err.re_status || status == NULL || *status)
726	    ? PAM_ABORT : PAM_SUCCESS;
727}
728#endif /* YP */
729
730PAM_MODULE_ENTRY("pam_unix");
731