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