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