1/* pam_winbind module
2
3   Copyright Andrew Tridgell <tridge@samba.org> 2000
4   Copyright Tim Potter <tpot@samba.org> 2000
5   Copyright Andrew Bartlett <abartlet@samba.org> 2002
6
7   largely based on pam_userdb by Cristian Gafton <gafton@redhat.com>
8   also contains large slabs of code from pam_unix by Elliot Lee <sopwith@redhat.com>
9   (see copyright below for full details)
10*/
11
12#include "pam_winbind.h"
13
14/* data tokens */
15
16#define MAX_PASSWD_TRIES	3
17
18/* some syslogging */
19static void _pam_log(int err, const char *format, ...)
20{
21	va_list args;
22
23	va_start(args, format);
24	openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH);
25	vsyslog(err, format, args);
26	va_end(args);
27	closelog();
28}
29
30static int _pam_parse(int argc, const char **argv)
31{
32	int ctrl;
33	/* step through arguments */
34	for (ctrl = 0; argc-- > 0; ++argv) {
35
36		/* generic options */
37
38		if (!strcmp(*argv,"debug"))
39			ctrl |= WINBIND_DEBUG_ARG;
40		else if (!strcasecmp(*argv, "use_authtok"))
41			ctrl |= WINBIND_USE_AUTHTOK_ARG;
42		else if (!strcasecmp(*argv, "use_first_pass"))
43			ctrl |= WINBIND_USE_FIRST_PASS_ARG;
44		else if (!strcasecmp(*argv, "try_first_pass"))
45			ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
46		else if (!strcasecmp(*argv, "unknown_ok"))
47			ctrl |= WINBIND_UNKNOWN_OK_ARG;
48		else if (!strncasecmp(*argv, "require_membership_of", strlen("require_membership_of")))
49			ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
50		else if (!strncasecmp(*argv, "require-membership-of", strlen("require-membership-of")))
51			ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
52		else {
53			_pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv);
54		}
55	}
56
57	return ctrl;
58}
59
60/* --- authentication management functions --- */
61
62/* Attempt a conversation */
63
64static int converse(pam_handle_t *pamh, int nargs,
65		    struct pam_message **message,
66		    struct pam_response **response)
67{
68    int retval;
69    struct pam_conv *conv;
70
71    retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ;
72    if (retval == PAM_SUCCESS) {
73	retval = conv->conv(nargs, (const struct pam_message **)message,
74			    response, conv->appdata_ptr);
75    }
76
77    return retval; /* propagate error status */
78}
79
80
81static int _make_remark(pam_handle_t * pamh, int type, const char *text)
82{
83	int retval = PAM_SUCCESS;
84
85	struct pam_message *pmsg[1], msg[1];
86	struct pam_response *resp;
87
88	pmsg[0] = &msg[0];
89	msg[0].msg = text;
90	msg[0].msg_style = type;
91
92	resp = NULL;
93	retval = converse(pamh, 1, pmsg, &resp);
94
95	if (resp) {
96		_pam_drop_reply(resp, 1);
97	}
98	return retval;
99}
100
101static int pam_winbind_request(enum winbindd_cmd req_type,
102			       struct winbindd_request *request,
103			       struct winbindd_response *response)
104{
105
106	/* Fill in request and send down pipe */
107	init_request(request, req_type);
108
109	if (write_sock(request, sizeof(*request)) == -1) {
110		_pam_log(LOG_ERR, "write to socket failed!");
111		close_sock();
112		return PAM_SERVICE_ERR;
113	}
114
115	/* Wait for reply */
116	if (read_reply(response) == -1) {
117		_pam_log(LOG_ERR, "read from socket failed!");
118		close_sock();
119		return PAM_SERVICE_ERR;
120	}
121
122	/* We are done with the socket - close it and avoid mischeif */
123	close_sock();
124
125	/* Copy reply data from socket */
126	if (response->result != WINBINDD_OK) {
127		if (response->data.auth.pam_error != PAM_SUCCESS) {
128			_pam_log(LOG_ERR, "request failed: %s, PAM error was %d, NT error was %s",
129				 response->data.auth.error_string,
130				 response->data.auth.pam_error,
131				 response->data.auth.nt_status_string);
132			return response->data.auth.pam_error;
133		} else {
134			_pam_log(LOG_ERR, "request failed, but PAM error 0!");
135			return PAM_SERVICE_ERR;
136		}
137	}
138
139	return PAM_SUCCESS;
140}
141
142static int pam_winbind_request_log(enum winbindd_cmd req_type,
143			       struct winbindd_request *request,
144			       struct winbindd_response *response,
145				   int ctrl,
146				   const char *user)
147{
148	int retval;
149
150        retval = pam_winbind_request(req_type, request, response);
151
152	switch (retval) {
153	case PAM_AUTH_ERR:
154		/* incorrect password */
155		_pam_log(LOG_WARNING, "user `%s' denied access (incorrect password or invalid membership)", user);
156		return retval;
157	case PAM_ACCT_EXPIRED:
158		/* account expired */
159		_pam_log(LOG_WARNING, "user `%s' account expired", user);
160		return retval;
161	case PAM_AUTHTOK_EXPIRED:
162		/* password expired */
163		_pam_log(LOG_WARNING, "user `%s' password expired", user);
164		return retval;
165	case PAM_NEW_AUTHTOK_REQD:
166		/* password expired */
167		_pam_log(LOG_WARNING, "user `%s' new password required", user);
168		return retval;
169	case PAM_USER_UNKNOWN:
170		/* the user does not exist */
171		if (ctrl & WINBIND_DEBUG_ARG)
172			_pam_log(LOG_NOTICE, "user `%s' not found",
173				 user);
174		if (ctrl & WINBIND_UNKNOWN_OK_ARG) {
175			return PAM_IGNORE;
176		}
177		return retval;
178	case PAM_SUCCESS:
179		if (req_type == WINBINDD_PAM_AUTH) {
180			/* Otherwise, the authentication looked good */
181			_pam_log(LOG_NOTICE, "user '%s' granted access", user);
182		} else if (req_type == WINBINDD_PAM_CHAUTHTOK) {
183			/* Otherwise, the authentication looked good */
184			_pam_log(LOG_NOTICE, "user '%s' password changed", user);
185		} else {
186			/* Otherwise, the authentication looked good */
187			_pam_log(LOG_NOTICE, "user '%s' OK", user);
188		}
189		return retval;
190	default:
191		/* we don't know anything about this return value */
192		_pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s')",
193			 retval, user);
194		return retval;
195	}
196}
197
198/* talk to winbindd */
199static int winbind_auth_request(const char *user, const char *pass, const char *member, int ctrl)
200{
201	struct winbindd_request request;
202	struct winbindd_response response;
203
204	ZERO_STRUCT(request);
205
206	strncpy(request.data.auth.user, user,
207                sizeof(request.data.auth.user)-1);
208
209	strncpy(request.data.auth.pass, pass,
210                sizeof(request.data.auth.pass)-1);
211
212	if (member == NULL )
213        	return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
214
215	/* lookup name? */
216	if (!strncmp("S-", member, 2) == 0) {
217
218		struct winbindd_request sid_request;
219		struct winbindd_response sid_response;
220
221		ZERO_STRUCT(sid_request);
222		ZERO_STRUCT(sid_response);
223
224		if (ctrl & WINBIND_DEBUG_ARG)
225			_pam_log(LOG_DEBUG, "no sid given, looking up: %s\n", member);
226
227		/* fortunatly winbindd can handle non-separated names */
228		strcpy(sid_request.data.name.name, member);
229
230		if (pam_winbind_request_log(WINBINDD_LOOKUPNAME, &sid_request, &sid_response, ctrl, user)) {
231			_pam_log(LOG_INFO, "could not lookup name: %s\n", member);
232			return PAM_AUTH_ERR;
233		}
234
235		member = sid_response.data.sid.sid;
236	}
237
238	strncpy(request.data.auth.require_membership_of_sid, member,
239	        sizeof(request.data.auth.require_membership_of_sid)-1);
240
241        return pam_winbind_request_log(WINBINDD_PAM_AUTH, &request, &response, ctrl, user);
242}
243
244/* talk to winbindd */
245static int winbind_chauthtok_request(const char *user, const char *oldpass,
246                                     const char *newpass, int ctrl)
247{
248	struct winbindd_request request;
249	struct winbindd_response response;
250
251	ZERO_STRUCT(request);
252
253        if (request.data.chauthtok.user == NULL) return -2;
254
255	strncpy(request.data.chauthtok.user, user,
256                sizeof(request.data.chauthtok.user) - 1);
257
258        if (oldpass != NULL) {
259            strncpy(request.data.chauthtok.oldpass, oldpass,
260                    sizeof(request.data.chauthtok.oldpass) - 1);
261        } else {
262            request.data.chauthtok.oldpass[0] = '\0';
263        }
264
265        if (newpass != NULL) {
266            strncpy(request.data.chauthtok.newpass, newpass,
267                    sizeof(request.data.chauthtok.newpass) - 1);
268        } else {
269            request.data.chauthtok.newpass[0] = '\0';
270        }
271
272        return pam_winbind_request_log(WINBINDD_PAM_CHAUTHTOK, &request, &response, ctrl, user);
273}
274
275/*
276 * Checks if a user has an account
277 *
278 * return values:
279 *	 1  = User not found
280 *	 0  = OK
281 * 	-1  = System error
282 */
283static int valid_user(const char *user)
284{
285	if (getpwnam(user)) return 0;
286	return 1;
287}
288
289static char *_pam_delete(register char *xx)
290{
291    _pam_overwrite(xx);
292    _pam_drop(xx);
293    return NULL;
294}
295
296/*
297 * obtain a password from the user
298 */
299
300static int _winbind_read_password(pam_handle_t * pamh
301				  ,unsigned int ctrl
302				  ,const char *comment
303				  ,const char *prompt1
304				  ,const char *prompt2
305				  ,const char **pass)
306{
307	int authtok_flag;
308	int retval;
309	const char *item;
310	char *token;
311
312	/*
313	 * make sure nothing inappropriate gets returned
314	 */
315
316	*pass = token = NULL;
317
318	/*
319	 * which authentication token are we getting?
320	 */
321
322	authtok_flag = on(WINBIND__OLD_PASSWORD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
323
324	/*
325	 * should we obtain the password from a PAM item ?
326	 */
327
328	if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
329		retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
330		if (retval != PAM_SUCCESS) {
331			/* very strange. */
332			_pam_log(LOG_ALERT,
333				 "pam_get_item returned error to unix-read-password"
334			    );
335			return retval;
336		} else if (item != NULL) {	/* we have a password! */
337			*pass = item;
338			item = NULL;
339			return PAM_SUCCESS;
340		} else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
341			return PAM_AUTHTOK_RECOVER_ERR;		/* didn't work */
342		} else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
343			   && off(WINBIND__OLD_PASSWORD, ctrl)) {
344			return PAM_AUTHTOK_RECOVER_ERR;
345		}
346	}
347	/*
348	 * getting here implies we will have to get the password from the
349	 * user directly.
350	 */
351
352	{
353		struct pam_message msg[3], *pmsg[3];
354		struct pam_response *resp;
355		int i, replies;
356
357		/* prepare to converse */
358
359		if (comment != NULL) {
360			pmsg[0] = &msg[0];
361			msg[0].msg_style = PAM_TEXT_INFO;
362			msg[0].msg = comment;
363			i = 1;
364		} else {
365			i = 0;
366		}
367
368		pmsg[i] = &msg[i];
369		msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
370		msg[i++].msg = prompt1;
371		replies = 1;
372
373		if (prompt2 != NULL) {
374			pmsg[i] = &msg[i];
375			msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
376			msg[i++].msg = prompt2;
377			++replies;
378		}
379		/* so call the conversation expecting i responses */
380		resp = NULL;
381		retval = converse(pamh, i, pmsg, &resp);
382
383		if (resp != NULL) {
384
385			/* interpret the response */
386
387			if (retval == PAM_SUCCESS) {	/* a good conversation */
388
389				token = x_strdup(resp[i - replies].resp);
390				if (token != NULL) {
391					if (replies == 2) {
392
393						/* verify that password entered correctly */
394						if (!resp[i - 1].resp
395						    || strcmp(token, resp[i - 1].resp)) {
396							_pam_delete(token);	/* mistyped */
397							retval = PAM_AUTHTOK_RECOVER_ERR;
398							_make_remark(pamh								    ,PAM_ERROR_MSG, MISTYPED_PASS);
399						}
400					}
401				} else {
402					_pam_log(LOG_NOTICE
403						 ,"could not recover authentication token");
404				}
405
406			}
407			/*
408			 * tidy up the conversation (resp_retcode) is ignored
409			 * -- what is it for anyway? AGM
410			 */
411
412			_pam_drop_reply(resp, i);
413
414		} else {
415			retval = (retval == PAM_SUCCESS)
416			    ? PAM_AUTHTOK_RECOVER_ERR : retval;
417		}
418	}
419
420	if (retval != PAM_SUCCESS) {
421		if (on(WINBIND_DEBUG_ARG, ctrl))
422			_pam_log(LOG_DEBUG,
423			         "unable to obtain a password");
424		return retval;
425	}
426	/* 'token' is the entered password */
427
428	/* we store this password as an item */
429
430	retval = pam_set_item(pamh, authtok_flag, token);
431	_pam_delete(token);	/* clean it up */
432	if (retval != PAM_SUCCESS
433	    || (retval = pam_get_item(pamh, authtok_flag
434				      ,(const void **) &item))
435	    != PAM_SUCCESS) {
436
437		_pam_log(LOG_CRIT, "error manipulating password");
438		return retval;
439
440	}
441
442	*pass = item;
443	item = NULL;		/* break link to password */
444
445	return PAM_SUCCESS;
446}
447
448PAM_EXTERN
449int pam_sm_authenticate(pam_handle_t *pamh, int flags,
450			int argc, const char **argv)
451{
452     const char *username;
453     const char *password;
454     const char *member = NULL;
455     int retval = PAM_AUTH_ERR;
456     int i;
457
458     /* parse arguments */
459     int ctrl = _pam_parse(argc, argv);
460
461     /* Get the username */
462     retval = pam_get_user(pamh, &username, NULL);
463     if ((retval != PAM_SUCCESS) || (!username)) {
464        if (ctrl & WINBIND_DEBUG_ARG)
465            _pam_log(LOG_DEBUG,"can not get the username");
466        return PAM_SERVICE_ERR;
467     }
468
469     retval = _winbind_read_password(pamh, ctrl, NULL,
470				     "Password: ", NULL,
471				     &password);
472
473     if (retval != PAM_SUCCESS) {
474	 _pam_log(LOG_ERR, "Could not retrieve user's password");
475	 return PAM_AUTHTOK_ERR;
476     }
477
478     if (ctrl & WINBIND_DEBUG_ARG) {
479
480	     /* Let's not give too much away in the log file */
481
482#ifdef DEBUG_PASSWORD
483	 _pam_log(LOG_INFO, "Verify user `%s' with password `%s'",
484		  username, password);
485#else
486	 _pam_log(LOG_INFO, "Verify user `%s'", username);
487#endif
488     }
489
490     /* Retrieve membership-string here */
491     for ( i=0; i<argc; i++ ) {
492
493	 if ((strncmp(argv[i], "require_membership_of", strlen("require_membership_of")) == 0)
494	     || (strncmp(argv[i], "require-membership-of", strlen("require-membership-of")) == 0)) {
495
496	     char *p;
497	     char *parm = strdup(argv[i]);
498
499	     if ( (p = strchr( parm, '=' )) == NULL) {
500	     	_pam_log(LOG_INFO, "no \"=\" delimiter for \"require_membership_of\" found\n");
501		break;
502	     }
503
504	     member = strdup(p+1);
505	 }
506     }
507
508     /* Now use the username to look up password */
509     return winbind_auth_request(username, password, member, ctrl);
510}
511
512PAM_EXTERN
513int pam_sm_setcred(pam_handle_t *pamh, int flags,
514		   int argc, const char **argv)
515{
516    return PAM_SUCCESS;
517}
518
519/*
520 * Account management. We want to verify that the account exists
521 * before returning PAM_SUCCESS
522 */
523PAM_EXTERN
524int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
525		   int argc, const char **argv)
526{
527    const char *username;
528    int retval = PAM_USER_UNKNOWN;
529
530    /* parse arguments */
531    int ctrl = _pam_parse(argc, argv);
532
533    /* Get the username */
534    retval = pam_get_user(pamh, &username, NULL);
535    if ((retval != PAM_SUCCESS) || (!username)) {
536	if (ctrl & WINBIND_DEBUG_ARG)
537	    _pam_log(LOG_DEBUG,"can not get the username");
538	return PAM_SERVICE_ERR;
539    }
540
541    /* Verify the username */
542    retval = valid_user(username);
543    switch (retval) {
544	case -1:
545	    /* some sort of system error. The log was already printed */
546	    return PAM_SERVICE_ERR;
547	case 1:
548	    /* the user does not exist */
549	    if (ctrl & WINBIND_DEBUG_ARG)
550		_pam_log(LOG_NOTICE, "user `%s' not found",
551			 username);
552	    if (ctrl & WINBIND_UNKNOWN_OK_ARG)
553		return PAM_IGNORE;
554	    return PAM_USER_UNKNOWN;
555	case 0:
556	    /* Otherwise, the authentication looked good */
557	    _pam_log(LOG_NOTICE, "user '%s' granted access", username);
558	    return PAM_SUCCESS;
559	default:
560	    /* we don't know anything about this return value */
561	    _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'",
562		     retval, username);
563	    return PAM_SERVICE_ERR;
564    }
565
566    /* should not be reached */
567    return PAM_IGNORE;
568}
569PAM_EXTERN
570int pam_sm_open_session(pam_handle_t *pamh, int flags,
571                int argc, const char **argv)
572{
573        /* parse arguments */
574        int ctrl = _pam_parse(argc, argv);
575        if (ctrl & WINBIND_DEBUG_ARG)
576              _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_open_session handler");
577        return PAM_SUCCESS;
578}
579PAM_EXTERN
580int pam_sm_close_session(pam_handle_t *pamh, int flags,
581                int argc, const char **argv)
582{
583        /* parse arguments */
584        int ctrl = _pam_parse(argc, argv);
585        if (ctrl & WINBIND_DEBUG_ARG)
586              _pam_log(LOG_DEBUG,"libpam_winbind:pam_sm_close_session handler");
587        return PAM_SUCCESS;
588}
589
590
591
592PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
593				int argc, const char **argv)
594{
595	unsigned int lctrl;
596	int retval;
597	unsigned int ctrl = _pam_parse(argc, argv);
598
599	/* <DO NOT free() THESE> */
600	const char *user;
601	const char *member = NULL;
602	char *pass_old, *pass_new;
603	/* </DO NOT free() THESE> */
604
605	char *Announce;
606
607	int retry = 0;
608
609	/*
610	 * First get the name of a user
611	 */
612	retval = pam_get_user(pamh, &user, "Username: ");
613	if (retval == PAM_SUCCESS) {
614		if (user == NULL) {
615			_pam_log(LOG_ERR, "username was NULL!");
616			return PAM_USER_UNKNOWN;
617		}
618		if (retval == PAM_SUCCESS && on(WINBIND_DEBUG_ARG, ctrl))
619			_pam_log(LOG_DEBUG, "username [%s] obtained",
620				 user);
621	} else {
622		if (on(WINBIND_DEBUG_ARG, ctrl))
623			_pam_log(LOG_DEBUG,
624				 "password - could not identify user");
625		return retval;
626	}
627
628	/*
629	 * obtain and verify the current password (OLDAUTHTOK) for
630	 * the user.
631	 */
632
633	if (flags & PAM_PRELIM_CHECK) {
634
635		/* instruct user what is happening */
636#define greeting "Changing password for "
637		Announce = (char *) malloc(sizeof(greeting) + strlen(user));
638		if (Announce == NULL) {
639		_pam_log(LOG_CRIT,
640			 "password - out of memory");
641		return PAM_BUF_ERR;
642		}
643		(void) strcpy(Announce, greeting);
644		(void) strcpy(Announce + sizeof(greeting) - 1, user);
645#undef greeting
646
647		lctrl = ctrl | WINBIND__OLD_PASSWORD;
648		retval = _winbind_read_password(pamh, lctrl
649						,Announce
650						,"(current) NT password: "
651						,NULL
652						,(const char **) &pass_old);
653		free(Announce);
654
655		if (retval != PAM_SUCCESS) {
656			_pam_log(LOG_NOTICE
657				 ,"password - (old) token not obtained");
658			return retval;
659		}
660		/* verify that this is the password for this user */
661
662		retval = winbind_auth_request(user, pass_old, member, ctrl);
663
664		if (retval != PAM_ACCT_EXPIRED
665		    && retval != PAM_AUTHTOK_EXPIRED
666		    && retval != PAM_NEW_AUTHTOK_REQD
667		    && retval != PAM_SUCCESS) {
668			pass_old = NULL;
669			return retval;
670		}
671
672		retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
673		pass_old = NULL;
674		if (retval != PAM_SUCCESS) {
675			_pam_log(LOG_CRIT,
676				 "failed to set PAM_OLDAUTHTOK");
677		}
678	} else if (flags & PAM_UPDATE_AUTHTOK) {
679
680		/*
681		 * obtain the proposed password
682		 */
683
684		/*
685		 * get the old token back.
686		 */
687
688		retval = pam_get_item(pamh, PAM_OLDAUTHTOK
689				      ,(const void **) &pass_old);
690
691		if (retval != PAM_SUCCESS) {
692			_pam_log(LOG_NOTICE, "user not authenticated");
693			return retval;
694		}
695
696		lctrl = ctrl;
697
698		if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
699			lctrl |= WINBIND_USE_FIRST_PASS_ARG;
700		}
701		retry = 0;
702		retval = PAM_AUTHTOK_ERR;
703		while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
704			/*
705			 * use_authtok is to force the use of a previously entered
706			 * password -- needed for pluggable password strength checking
707			 */
708
709			retval = _winbind_read_password(pamh, lctrl
710							,NULL
711							,"Enter new NT password: "
712							,"Retype new NT password: "
713							,(const char **) &pass_new);
714
715			if (retval != PAM_SUCCESS) {
716				if (on(WINBIND_DEBUG_ARG, ctrl)) {
717					_pam_log(LOG_ALERT
718						 ,"password - new password not obtained");
719				}
720				pass_old = NULL;/* tidy up */
721				return retval;
722			}
723
724			/*
725			 * At this point we know who the user is and what they
726			 * propose as their new password. Verify that the new
727			 * password is acceptable.
728			 */
729
730			if (pass_new[0] == '\0') {/* "\0" password = NULL */
731				pass_new = NULL;
732			}
733		}
734
735		/*
736		 * By reaching here we have approved the passwords and must now
737		 * rebuild the password database file.
738		 */
739
740		retval = winbind_chauthtok_request(user, pass_old, pass_new, ctrl);
741		_pam_overwrite(pass_new);
742		_pam_overwrite(pass_old);
743		pass_old = pass_new = NULL;
744	} else {
745		retval = PAM_SERVICE_ERR;
746	}
747
748	return retval;
749}
750
751#ifdef PAM_STATIC
752
753/* static module data */
754
755struct pam_module _pam_winbind_modstruct = {
756     MODULE_NAME,
757     pam_sm_authenticate,
758     pam_sm_setcred,
759     pam_sm_acct_mgmt,
760     pam_sm_open_session,
761     pam_sm_close_session,
762     pam_sm_chauthtok
763};
764
765#endif
766
767/*
768 * Copyright (c) Andrew Tridgell  <tridge@samba.org>   2000
769 * Copyright (c) Tim Potter       <tpot@samba.org>     2000
770 * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
771 * Copyright (c) Jan R�korajski 1999.
772 * Copyright (c) Andrew G. Morgan 1996-8.
773 * Copyright (c) Alex O. Yuriev, 1996.
774 * Copyright (c) Cristian Gafton 1996.
775 * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
776 *
777 * Redistribution and use in source and binary forms, with or without
778 * modification, are permitted provided that the following conditions
779 * are met:
780 * 1. Redistributions of source code must retain the above copyright
781 *    notice, and the entire permission notice in its entirety,
782 *    including the disclaimer of warranties.
783 * 2. Redistributions in binary form must reproduce the above copyright
784 *    notice, this list of conditions and the following disclaimer in the
785 *    documentation and/or other materials provided with the distribution.
786 * 3. The name of the author may not be used to endorse or promote
787 *    products derived from this software without specific prior
788 *    written permission.
789 *
790 * ALTERNATIVELY, this product may be distributed under the terms of
791 * the GNU Public License, in which case the provisions of the GPL are
792 * required INSTEAD OF the above restrictions.  (This clause is
793 * necessary due to a potential bad interaction between the GPL and
794 * the restrictions contained in a BSD-style copyright.)
795 *
796 * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
797 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
798 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
799 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
800 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
801 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
802 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
803 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
804 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
805 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
806 * OF THE POSSIBILITY OF SUCH DAMAGE.
807 */
808