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