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    Copyright Guenther Deschner <gd@samba.org> 2005-2008
7 
8    largely based on pam_userdb by Cristian Gafton <gafton@redhat.com> also
9    contains large slabs of code from pam_unix by Elliot Lee
10    <sopwith@redhat.com> (see copyright below for full details)
11 */
12 
13 #include "pam_winbind.h"
14 
15 enum pam_winbind_request_type
16 {
17 	PAM_WINBIND_AUTHENTICATE,
18 	PAM_WINBIND_SETCRED,
19 	PAM_WINBIND_ACCT_MGMT,
20 	PAM_WINBIND_OPEN_SESSION,
21 	PAM_WINBIND_CLOSE_SESSION,
22 	PAM_WINBIND_CHAUTHTOK,
23 	PAM_WINBIND_CLEANUP
24 };
25 
26 static int wbc_error_to_pam_error(wbcErr status)
27 {
28 	switch (status) {
29 		case WBC_ERR_SUCCESS:
30 			return PAM_SUCCESS;
31 		case WBC_ERR_NOT_IMPLEMENTED:
32 			return PAM_SERVICE_ERR;
33 		case WBC_ERR_UNKNOWN_FAILURE:
34 			break;
35 		case WBC_ERR_NO_MEMORY:
36 			return PAM_BUF_ERR;
37 		case WBC_ERR_INVALID_SID:
38 		case WBC_ERR_INVALID_PARAM:
39 			break;
40 		case WBC_ERR_WINBIND_NOT_AVAILABLE:
41 			return PAM_AUTHINFO_UNAVAIL;
42 		case WBC_ERR_DOMAIN_NOT_FOUND:
43 			return PAM_AUTHINFO_UNAVAIL;
44 		case WBC_ERR_INVALID_RESPONSE:
45 			return PAM_BUF_ERR;
46 		case WBC_ERR_NSS_ERROR:
47 			return PAM_USER_UNKNOWN;
48 		case WBC_ERR_AUTH_ERROR:
49 			return PAM_AUTH_ERR;
50 		case WBC_ERR_UNKNOWN_USER:
51 			return PAM_USER_UNKNOWN;
52 		case WBC_ERR_UNKNOWN_GROUP:
53 			return PAM_USER_UNKNOWN;
54 		case WBC_ERR_PWD_CHANGE_FAILED:
55 			break;
56 	}
57 
58 	/* be paranoid */
59 	return PAM_AUTH_ERR;
60 }
61 
62 static const char *_pam_error_code_str(int err)
63 {
64 	switch (err) {
65 		case PAM_SUCCESS:
66 			return "PAM_SUCCESS";
67 		case PAM_OPEN_ERR:
68 			return "PAM_OPEN_ERR";
69 		case PAM_SYMBOL_ERR:
70 			return "PAM_SYMBOL_ERR";
71 		case PAM_SERVICE_ERR:
72 			return "PAM_SERVICE_ERR";
73 		case PAM_SYSTEM_ERR:
74 			return "PAM_SYSTEM_ERR";
75 		case PAM_BUF_ERR:
76 			return "PAM_BUF_ERR";
77 		case PAM_PERM_DENIED:
78 			return "PAM_PERM_DENIED";
79 		case PAM_AUTH_ERR:
80 			return "PAM_AUTH_ERR";
81 		case PAM_CRED_INSUFFICIENT:
82 			return "PAM_CRED_INSUFFICIENT";
83 		case PAM_AUTHINFO_UNAVAIL:
84 			return "PAM_AUTHINFO_UNAVAIL";
85 		case PAM_USER_UNKNOWN:
86 			return "PAM_USER_UNKNOWN";
87 		case PAM_MAXTRIES:
88 			return "PAM_MAXTRIES";
89 		case PAM_NEW_AUTHTOK_REQD:
90 			return "PAM_NEW_AUTHTOK_REQD";
91 		case PAM_ACCT_EXPIRED:
92 			return "PAM_ACCT_EXPIRED";
93 		case PAM_SESSION_ERR:
94 			return "PAM_SESSION_ERR";
95 		case PAM_CRED_UNAVAIL:
96 			return "PAM_CRED_UNAVAIL";
97 		case PAM_CRED_EXPIRED:
98 			return "PAM_CRED_EXPIRED";
99 		case PAM_CRED_ERR:
100 			return "PAM_CRED_ERR";
101 		case PAM_NO_MODULE_DATA:
102 			return "PAM_NO_MODULE_DATA";
103 		case PAM_CONV_ERR:
104 			return "PAM_CONV_ERR";
105 		case PAM_AUTHTOK_ERR:
106 			return "PAM_AUTHTOK_ERR";
107 		case PAM_AUTHTOK_RECOVER_ERR:
108 			return "PAM_AUTHTOK_RECOVER_ERR";
109 		case PAM_AUTHTOK_LOCK_BUSY:
110 			return "PAM_AUTHTOK_LOCK_BUSY";
111 		case PAM_AUTHTOK_DISABLE_AGING:
112 			return "PAM_AUTHTOK_DISABLE_AGING";
113 		case PAM_TRY_AGAIN:
114 			return "PAM_TRY_AGAIN";
115 		case PAM_IGNORE:
116 			return "PAM_IGNORE";
117 		case PAM_ABORT:
118 			return "PAM_ABORT";
119 		case PAM_AUTHTOK_EXPIRED:
120 			return "PAM_AUTHTOK_EXPIRED";
121 #ifdef PAM_MODULE_UNKNOWN
122 		case PAM_MODULE_UNKNOWN:
123 			return "PAM_MODULE_UNKNOWN";
124 #endif
125 #ifdef PAM_BAD_ITEM
126 		case PAM_BAD_ITEM:
127 			return "PAM_BAD_ITEM";
128 #endif
129 #ifdef PAM_CONV_AGAIN
130 		case PAM_CONV_AGAIN:
131 			return "PAM_CONV_AGAIN";
132 #endif
133 #ifdef PAM_INCOMPLETE
134 		case PAM_INCOMPLETE:
135 			return "PAM_INCOMPLETE";
136 #endif
137 		default:
138 			return NULL;
139 	}
140 }
141 
142 #define _PAM_LOG_FUNCTION_ENTER(function, ctx) \
143 	do { \
144 		_pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] ENTER: " \
145 			       function " (flags: 0x%04x)", ctx->pamh, ctx->flags); \
146 		_pam_log_state(ctx); \
147 	} while (0)
148 
149 #define _PAM_LOG_FUNCTION_LEAVE(function, ctx, retval) \
150 	do { \
151 		_pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] LEAVE: " \
152 			       function " returning %d (%s)", ctx ? ctx->pamh : NULL, retval, \
153 			       _pam_error_code_str(retval)); \
154 		_pam_log_state(ctx); \
155 	} while (0)
156 
157 /* data tokens */
158 
159 #define MAX_PASSWD_TRIES	3
160 
161 #ifdef HAVE_GETTEXT
162 static char initialized = 0;
163 
164 static inline void textdomain_init(void);
165 static inline void textdomain_init(void)
166 {
167 	if (!initialized) {
168 		bindtextdomain(MODULE_NAME, LOCALEDIR);
169 		initialized = 1;
170 	}
171 	return;
172 }
173 #endif
174 
175 
176 /* some syslogging */
177 static void _pam_log_int(const pam_handle_t *pamh,
178 			 int err,
179 			 const char *format,
180 			 va_list args) PRINTF_ATTRIBUTE(3, 0);
181 
182 #ifdef HAVE_PAM_VSYSLOG
183 static void _pam_log_int(const pam_handle_t *pamh,
184 			 int err,
185 			 const char *format,
186 			 va_list args)
187 {
188 	pam_vsyslog(pamh, err, format, args);
189 }
190 #else
191 static void _pam_log_int(const pam_handle_t *pamh,
192 			 int err,
193 			 const char *format,
194 			 va_list args)
195 {
196 	char *base = NULL;
197 	va_list args2;
198 	const char *service;
199 	int ret;
200 
201 	va_copy(args2, args);
202 
203 	pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
204 
205 	ret = vasprintf(&base, format, args);
206 	if (ret == -1) {
207 		/* what else todo ? */
208 		vsyslog(err, format, args2);
209 		va_end(args2);
210 		return;
211 	}
212 
213 	syslog(err, "%s(%s): %s", MODULE_NAME, service, base);
214 	SAFE_FREE(base);
215 	va_end(args2);
216 }
217 #endif /* HAVE_PAM_VSYSLOG */
218 
219 static bool _pam_log_is_silent(int ctrl)
220 {
221 	return on(ctrl, WINBIND_SILENT);
222 }
223 
224 static void _pam_log(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
225 static void _pam_log(struct pwb_context *r, int err, const char *format, ...)
226 {
227 	va_list args;
228 
229 	if (_pam_log_is_silent(r->ctrl)) {
230 		return;
231 	}
232 
233 	va_start(args, format);
234 	_pam_log_int(r->pamh, err, format, args);
235 	va_end(args);
236 }
237 static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
238 static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
239 {
240 	va_list args;
241 
242 	if (_pam_log_is_silent(ctrl)) {
243 		return;
244 	}
245 
246 	va_start(args, format);
247 	_pam_log_int(pamh, err, format, args);
248 	va_end(args);
249 }
250 
251 static bool _pam_log_is_debug_enabled(int ctrl)
252 {
253 	if (ctrl == -1) {
254 		return false;
255 	}
256 
257 	if (_pam_log_is_silent(ctrl)) {
258 		return false;
259 	}
260 
261 	if (!(ctrl & WINBIND_DEBUG_ARG)) {
262 		return false;
263 	}
264 
265 	return true;
266 }
267 
268 static bool _pam_log_is_debug_state_enabled(int ctrl)
269 {
270 	if (!(ctrl & WINBIND_DEBUG_STATE)) {
271 		return false;
272 	}
273 
274 	return _pam_log_is_debug_enabled(ctrl);
275 }
276 
277 static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
278 static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...)
279 {
280 	va_list args;
281 
282 	if (!r || !_pam_log_is_debug_enabled(r->ctrl)) {
283 		return;
284 	}
285 
286 	va_start(args, format);
287 	_pam_log_int(r->pamh, err, format, args);
288 	va_end(args);
289 }
290 static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
291 static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
292 {
293 	va_list args;
294 
295 	if (!_pam_log_is_debug_enabled(ctrl)) {
296 		return;
297 	}
298 
299 	va_start(args, format);
300 	_pam_log_int(pamh, err, format, args);
301 	va_end(args);
302 }
303 
304 static void _pam_log_state_datum(struct pwb_context *ctx,
305 				 int item_type,
306 				 const char *key,
307 				 int is_string)
308 {
309 	const void *data = NULL;
310 	if (item_type != 0) {
311 		pam_get_item(ctx->pamh, item_type, &data);
312 	} else {
313 		pam_get_data(ctx->pamh, key, &data);
314 	}
315 	if (data != NULL) {
316 		const char *type = (item_type != 0) ? "ITEM" : "DATA";
317 		if (is_string != 0) {
318 			_pam_log_debug(ctx, LOG_DEBUG,
319 				       "[pamh: %p] STATE: %s(%s) = \"%s\" (%p)",
320 				       ctx->pamh, type, key, (const char *)data,
321 				       data);
322 		} else {
323 			_pam_log_debug(ctx, LOG_DEBUG,
324 				       "[pamh: %p] STATE: %s(%s) = %p",
325 				       ctx->pamh, type, key, data);
326 		}
327 	}
328 }
329 
330 #define _PAM_LOG_STATE_DATA_POINTER(ctx, module_data_name) \
331 	_pam_log_state_datum(ctx, 0, module_data_name, 0)
332 
333 #define _PAM_LOG_STATE_DATA_STRING(ctx, module_data_name) \
334 	_pam_log_state_datum(ctx, 0, module_data_name, 1)
335 
336 #define _PAM_LOG_STATE_ITEM_POINTER(ctx, item_type) \
337 	_pam_log_state_datum(ctx, item_type, #item_type, 0)
338 
339 #define _PAM_LOG_STATE_ITEM_STRING(ctx, item_type) \
340 	_pam_log_state_datum(ctx, item_type, #item_type, 1)
341 
342 #ifdef DEBUG_PASSWORD
343 #define _LOG_PASSWORD_AS_STRING 1
344 #else
345 #define _LOG_PASSWORD_AS_STRING 0
346 #endif
347 
348 #define _PAM_LOG_STATE_ITEM_PASSWORD(ctx, item_type) \
349 	_pam_log_state_datum(ctx, item_type, #item_type, \
350 			     _LOG_PASSWORD_AS_STRING)
351 /*
352  * wrapper to preserve old behaviour of iniparser which ignored
353  * key values that had no value assigned like
354  *    key =
355  * for a key like above newer iniparser will return a zero-length
356  * string, previously iniparser would return NULL
357  *
358  * JRA: For compatibility, tiniparser behaves like iniparser.
359  */
360 static const char *tiniparser_getstring_nonempty(struct tiniparser_dictionary *d,
361 			const char *key,
362 			const char *def)
363 {
364 	const char *ret = tiniparser_getstring(d, key, def);
365 	if (ret && strlen(ret) == 0) {
366 		ret = NULL;
367 	}
368 	return ret;
369 }
370 
371 static void _pam_log_state(struct pwb_context *ctx)
372 {
373 	if (!ctx || !_pam_log_is_debug_state_enabled(ctx->ctrl)) {
374 		return;
375 	}
376 
377 	_PAM_LOG_STATE_ITEM_STRING(ctx, PAM_SERVICE);
378 	_PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER);
379 	_PAM_LOG_STATE_ITEM_STRING(ctx, PAM_TTY);
380 	_PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RHOST);
381 	_PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RUSER);
382 	_PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_OLDAUTHTOK);
383 	_PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_AUTHTOK);
384 	_PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER_PROMPT);
385 	_PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_CONV);
386 #ifdef PAM_FAIL_DELAY
387 	_PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_FAIL_DELAY);
388 #endif
389 #ifdef PAM_REPOSITORY
390 	_PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_REPOSITORY);
391 #endif
392 
393 	_PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_HOMEDIR);
394 	_PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSCRIPT);
395 	_PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSERVER);
396 	_PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_PROFILEPATH);
397 	_PAM_LOG_STATE_DATA_STRING(ctx,
398 				   PAM_WINBIND_NEW_AUTHTOK_REQD);
399 				   /* Use atoi to get PAM result code */
400 	_PAM_LOG_STATE_DATA_STRING(ctx,
401 				   PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH);
402 	_PAM_LOG_STATE_DATA_POINTER(ctx, PAM_WINBIND_PWD_LAST_SET);
403 }
404 
405 static int _pam_parse(const pam_handle_t *pamh,
406 		      int flags,
407 		      int argc,
408 		      const char **argv,
409 		      enum pam_winbind_request_type type,
410 		      struct tiniparser_dictionary **result_d)
411 {
412 	int ctrl = 0;
413 	const char *config_file = NULL;
414 	int i;
415 	const char **v;
416 	struct tiniparser_dictionary *d = NULL;
417 
418 	if (flags & PAM_SILENT) {
419 		ctrl |= WINBIND_SILENT;
420 	}
421 
422 	for (i=argc,v=argv; i-- > 0; ++v) {
423 		if (!strncasecmp(*v, "config", strlen("config"))) {
424 			ctrl |= WINBIND_CONFIG_FILE;
425 			config_file = v[i];
426 			break;
427 		}
428 	}
429 
430 	if (config_file == NULL) {
431 		config_file = PAM_WINBIND_CONFIG_FILE;
432 	}
433 
434 	d = tiniparser_load(config_file);
435 	if (d == NULL) {
436 		goto config_from_pam;
437 	}
438 
439 	if (tiniparser_getboolean(d, "global:debug", false)) {
440 		ctrl |= WINBIND_DEBUG_ARG;
441 	}
442 
443 	if (tiniparser_getboolean(d, "global:debug_state", false)) {
444 		ctrl |= WINBIND_DEBUG_STATE;
445 	}
446 
447 	if (tiniparser_getboolean(d, "global:cached_login", false)) {
448 		ctrl |= WINBIND_CACHED_LOGIN;
449 	}
450 
451 	if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
452 		ctrl |= WINBIND_KRB5_AUTH;
453 	}
454 
455 	if (tiniparser_getboolean(d, "global:silent", false)) {
456 		ctrl |= WINBIND_SILENT;
457 	}
458 
459 	if (tiniparser_getstring_nonempty(d, "global:krb5_ccache_type", NULL) != NULL) {
460 		ctrl |= WINBIND_KRB5_CCACHE_TYPE;
461 	}
462 
463 	if ((tiniparser_getstring_nonempty(d, "global:require-membership-of", NULL)
464 	     != NULL) ||
465 	    (tiniparser_getstring_nonempty(d, "global:require_membership_of", NULL)
466 	     != NULL)) {
467 		ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
468 	}
469 
470 	if (tiniparser_getboolean(d, "global:try_first_pass", false)) {
471 		ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
472 	}
473 
474 	if (tiniparser_getint(d, "global:warn_pwd_expire", 0)) {
475 		ctrl |= WINBIND_WARN_PWD_EXPIRE;
476 	}
477 
478 	if (tiniparser_getboolean(d, "global:mkhomedir", false)) {
479 		ctrl |= WINBIND_MKHOMEDIR;
480 	}
481 
482 config_from_pam:
483 	/* step through arguments */
484 	for (i=argc,v=argv; i-- > 0; ++v) {
485 
486 		/* generic options */
487 		if (!strcmp(*v,"debug"))
488 			ctrl |= WINBIND_DEBUG_ARG;
489 		else if (!strcasecmp(*v, "debug_state"))
490 			ctrl |= WINBIND_DEBUG_STATE;
491 		else if (!strcasecmp(*v, "silent"))
492 			ctrl |= WINBIND_SILENT;
493 		else if (!strcasecmp(*v, "use_authtok"))
494 			ctrl |= WINBIND_USE_AUTHTOK_ARG;
495 		else if (!strcasecmp(*v, "try_authtok"))
496 			ctrl |= WINBIND_TRY_AUTHTOK_ARG;
497 		else if (!strcasecmp(*v, "use_first_pass"))
498 			ctrl |= WINBIND_USE_FIRST_PASS_ARG;
499 		else if (!strcasecmp(*v, "try_first_pass"))
500 			ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
501 		else if (!strcasecmp(*v, "unknown_ok"))
502 			ctrl |= WINBIND_UNKNOWN_OK_ARG;
503 		else if ((type == PAM_WINBIND_AUTHENTICATE
504 			  || type == PAM_WINBIND_SETCRED)
505 			 && !strncasecmp(*v, "require_membership_of",
506 					 strlen("require_membership_of")))
507 			ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
508 		else if ((type == PAM_WINBIND_AUTHENTICATE
509 			  || type == PAM_WINBIND_SETCRED)
510 			 && !strncasecmp(*v, "require-membership-of",
511 					 strlen("require-membership-of")))
512 			ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
513 		else if (!strcasecmp(*v, "krb5_auth"))
514 			ctrl |= WINBIND_KRB5_AUTH;
515 		else if (!strncasecmp(*v, "krb5_ccache_type",
516 				      strlen("krb5_ccache_type")))
517 			ctrl |= WINBIND_KRB5_CCACHE_TYPE;
518 		else if (!strcasecmp(*v, "cached_login"))
519 			ctrl |= WINBIND_CACHED_LOGIN;
520 		else if (!strcasecmp(*v, "mkhomedir"))
521 			ctrl |= WINBIND_MKHOMEDIR;
522 		else if (!strncasecmp(*v, "warn_pwd_expire",
523 			strlen("warn_pwd_expire")))
524 			ctrl |= WINBIND_WARN_PWD_EXPIRE;
525 		else if (type != PAM_WINBIND_CLEANUP) {
526 			__pam_log(pamh, ctrl, LOG_ERR,
527 				 "pam_parse: unknown option: %s", *v);
528 			return -1;
529 		}
530 
531 	}
532 
533 	if (result_d) {
534 		*result_d = d;
535 	} else {
536 		if (d) {
537 			tiniparser_freedict(d);
538 		}
539 	}
540 
541 	return ctrl;
542 };
543 
544 static int _pam_winbind_free_context(struct pwb_context *ctx)
545 {
546 	if (!ctx) {
547 		return 0;
548 	}
549 
550 	if (ctx->dict) {
551 		tiniparser_freedict(ctx->dict);
552 	}
553 
554 	wbcCtxFree(ctx->wbc_ctx);
555 
556 	return 0;
557 }
558 
559 static int _pam_winbind_init_context(pam_handle_t *pamh,
560 				     int flags,
561 				     int argc,
562 				     const char **argv,
563 				     enum pam_winbind_request_type type,
564 				     struct pwb_context **ctx_p)
565 {
566 	struct pwb_context *r = NULL;
567 	const char *service = NULL;
568 	char service_name[32] = {0};
569 	int ctrl_code;
570 
571 #ifdef HAVE_GETTEXT
572 	textdomain_init();
573 #endif
574 
575 	r = talloc_zero(NULL, struct pwb_context);
576 	if (!r) {
577 		return PAM_BUF_ERR;
578 	}
579 
580 	talloc_set_destructor(r, _pam_winbind_free_context);
581 
582 	r->pamh = pamh;
583 	r->flags = flags;
584 	r->argc = argc;
585 	r->argv = argv;
586 	ctrl_code = _pam_parse(pamh, flags, argc, argv, type, &r->dict);
587 	if (ctrl_code == -1) {
588 		TALLOC_FREE(r);
589 		return PAM_SYSTEM_ERR;
590 	}
591 	r->ctrl = ctrl_code;
592 
593 	r->wbc_ctx = wbcCtxCreate();
594 	if (r->wbc_ctx == NULL) {
595 		TALLOC_FREE(r);
596 		return PAM_SYSTEM_ERR;
597 	}
598 
599 	pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
600 
601 	snprintf(service_name, sizeof(service_name), "PAM_WINBIND[%s]", service);
602 
603 	wbcSetClientProcessName(service_name);
604 
605 	*ctx_p = r;
606 
607 	return PAM_SUCCESS;
608 }
609 
610 static void _pam_winbind_cleanup_func(pam_handle_t *pamh,
611 				      void *data,
612 				      int error_status)
613 {
614 	int ctrl = _pam_parse(pamh, 0, 0, NULL, PAM_WINBIND_CLEANUP, NULL);
615 	if (_pam_log_is_debug_state_enabled(ctrl)) {
616 		__pam_log_debug(pamh, ctrl, LOG_DEBUG,
617 			       "[pamh: %p] CLEAN: cleaning up PAM data %p "
618 			       "(error_status = %d)", pamh, data,
619 			       error_status);
620 	}
621 	TALLOC_FREE(data);
622 }
623 
624 
625 static const struct ntstatus_errors {
626 	const char *ntstatus_string;
627 	const char *error_string;
628 } ntstatus_errors[] = {
629 	{"NT_STATUS_OK",
630 		N_("Success")},
631 	{"NT_STATUS_BACKUP_CONTROLLER",
632 		N_("No primary Domain Controller available")},
633 	{"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
634 		N_("No domain controllers found")},
635 	{"NT_STATUS_NO_LOGON_SERVERS",
636 		N_("No logon servers")},
637 	{"NT_STATUS_PWD_TOO_SHORT",
638 		N_("Password too short")},
639 	{"NT_STATUS_PWD_TOO_RECENT",
640 		N_("The password of this user is too recent to change")},
641 	{"NT_STATUS_PWD_HISTORY_CONFLICT",
642 		N_("Password is already in password history")},
643 	{"NT_STATUS_PASSWORD_EXPIRED",
644 		N_("Your password has expired")},
645 	{"NT_STATUS_PASSWORD_MUST_CHANGE",
646 		N_("You need to change your password now")},
647 	{"NT_STATUS_INVALID_WORKSTATION",
648 		N_("You are not allowed to logon from this workstation")},
649 	{"NT_STATUS_INVALID_LOGON_HOURS",
650 		N_("You are not allowed to logon at this time")},
651 	{"NT_STATUS_ACCOUNT_EXPIRED",
652 		N_("Your account has expired. "
653 		   "Please contact your System administrator")}, /* SCNR */
654 	{"NT_STATUS_ACCOUNT_DISABLED",
655 		N_("Your account is disabled. "
656 		   "Please contact your System administrator")}, /* SCNR */
657 	{"NT_STATUS_ACCOUNT_LOCKED_OUT",
658 		N_("Your account has been locked. "
659 		   "Please contact your System administrator")}, /* SCNR */
660 	{"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
661 		N_("Invalid Trust Account")},
662 	{"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
663 		N_("Invalid Trust Account")},
664 	{"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
665 		N_("Invalid Trust Account")},
666 	{"NT_STATUS_ACCESS_DENIED",
667 		N_("Access is denied")},
668 	{NULL, NULL}
669 };
670 
671 static const char *_get_ntstatus_error_string(const char *nt_status_string)
672 {
673 	int i;
674 	for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
675 		if (!strcasecmp(ntstatus_errors[i].ntstatus_string,
676 				nt_status_string)) {
677 			return _(ntstatus_errors[i].error_string);
678 		}
679 	}
680 	return NULL;
681 }
682 
683 /* --- authentication management functions --- */
684 
685 /* Attempt a conversation */
686 
687 static int converse(const pam_handle_t *pamh,
688 		    int nargs,
689 		    const struct pam_message **message,
690 		    struct pam_response **response)
691 {
692 	int retval;
693 	const struct pam_conv *conv;
694 
695 	retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
696 	if (retval == PAM_SUCCESS) {
697 		retval = conv->conv(nargs,
698 				    discard_const_p(const struct pam_message *, message),
699 				    response, conv->appdata_ptr);
700 	}
701 
702 	return retval; /* propagate error status */
703 }
704 
705 
706 static int _make_remark(struct pwb_context *ctx,
707 			int type,
708 			const char *text)
709 {
710 	int retval = PAM_SUCCESS;
711 
712 	const struct pam_message *pmsg[1];
713 	struct pam_message msg[1];
714 	struct pam_response *resp;
715 
716 	if (ctx->flags & WINBIND_SILENT) {
717 		return PAM_SUCCESS;
718 	}
719 
720 	pmsg[0] = &msg[0];
721 	msg[0].msg = discard_const_p(char, text);
722 	msg[0].msg_style = type;
723 
724 	resp = NULL;
725 	retval = converse(ctx->pamh, 1, pmsg, &resp);
726 
727 	if (resp) {
728 		_pam_drop_reply(resp, 1);
729 	}
730 	return retval;
731 }
732 
733 static int _make_remark_v(struct pwb_context *ctx,
734 			  int type,
735 			  const char *format,
736 			  va_list args) PRINTF_ATTRIBUTE(3, 0);
737 
738 static int _make_remark_v(struct pwb_context *ctx,
739 			  int type,
740 			  const char *format,
741 			  va_list args)
742 {
743 	char *var;
744 	int ret;
745 
746 	ret = vasprintf(&var, format, args);
747 	if (ret < 0) {
748 		_pam_log(ctx, LOG_ERR, "memory allocation failure");
749 		return ret;
750 	}
751 
752 	ret = _make_remark(ctx, type, var);
753 	SAFE_FREE(var);
754 	return ret;
755 }
756 
757 static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
758 static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...)
759 {
760 	int ret;
761 	va_list args;
762 
763 	va_start(args, format);
764 	ret = _make_remark_v(ctx, type, format, args);
765 	va_end(args);
766 	return ret;
767 }
768 
769 static int pam_winbind_request_log(struct pwb_context *ctx,
770 				   int retval,
771 				   const char *user,
772 				   const char *fn)
773 {
774 	switch (retval) {
775 	case PAM_AUTH_ERR:
776 		/* incorrect password */
777 		_pam_log(ctx, LOG_WARNING, "user '%s' denied access "
778 			 "(incorrect password or invalid membership)", user);
779 		return retval;
780 	case PAM_ACCT_EXPIRED:
781 		/* account expired */
782 		_pam_log(ctx, LOG_WARNING, "user '%s' account expired",
783 			 user);
784 		return retval;
785 	case PAM_AUTHTOK_EXPIRED:
786 		/* password expired */
787 		_pam_log(ctx, LOG_WARNING, "user '%s' password expired",
788 			 user);
789 		return retval;
790 	case PAM_NEW_AUTHTOK_REQD:
791 		/* new password required */
792 		_pam_log(ctx, LOG_WARNING, "user '%s' new password "
793 			 "required", user);
794 		return retval;
795 	case PAM_USER_UNKNOWN:
796 		/* the user does not exist */
797 		_pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
798 			       user);
799 		if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
800 			return PAM_IGNORE;
801 		}
802 		return retval;
803 	case PAM_AUTHTOK_ERR:
804 		/* Authentication token manipulation error */
805 		_pam_log(ctx, LOG_WARNING, "user `%s' authentication token change failed "
806 			"(pwd complexity/history/min_age not met?)", user);
807 		return retval;
808 	case PAM_SUCCESS:
809 		/* Otherwise, the authentication looked good */
810 		if (strcmp(fn, "wbcLogonUser") == 0) {
811 			_pam_log(ctx, LOG_NOTICE,
812 				 "user '%s' granted access", user);
813 		} else {
814 			_pam_log(ctx, LOG_NOTICE,
815 				 "user '%s' OK", user);
816 		}
817 		return retval;
818 	default:
819 		/* we don't know anything about this return value */
820 		_pam_log(ctx, LOG_ERR,
821 			 "internal module error (retval = %s(%d), user = '%s')",
822 			_pam_error_code_str(retval), retval, user);
823 		return retval;
824 	}
825 }
826 
827 static int wbc_auth_error_to_pam_error(struct pwb_context *ctx,
828 				       struct wbcAuthErrorInfo *e,
829 				       wbcErr status,
830 				       const char *username,
831 				       const char *fn)
832 {
833 	int ret = PAM_AUTH_ERR;
834 
835 	if (WBC_ERROR_IS_OK(status)) {
836 		_pam_log_debug(ctx, LOG_DEBUG, "request %s succeeded",
837 			fn);
838 		ret = PAM_SUCCESS;
839 		return pam_winbind_request_log(ctx, ret, username, fn);
840 	}
841 
842 	if (e) {
843 		if (e->pam_error != PAM_SUCCESS) {
844 			_pam_log(ctx, LOG_ERR,
845 				 "request %s failed: %s, "
846 				 "PAM error: %s (%d), NTSTATUS: %s, "
847 				 "Error message was: %s",
848 				 fn,
849 				 wbcErrorString(status),
850 				 _pam_error_code_str(e->pam_error),
851 				 e->pam_error,
852 				 e->nt_string,
853 				 e->display_string);
854 			ret = e->pam_error;
855 			return pam_winbind_request_log(ctx, ret, username, fn);
856 		}
857 
858 		_pam_log(ctx, LOG_ERR, "request %s failed, but PAM error 0!", fn);
859 
860 		ret = PAM_SERVICE_ERR;
861 		return pam_winbind_request_log(ctx, ret, username, fn);
862 	}
863 
864 	ret = wbc_error_to_pam_error(status);
865 	_pam_log(ctx, LOG_ERR,
866 		 "request %s failed: %s, PAM error: %s (%d)!",
867 		 fn, wbcErrorString(status),
868 		 _pam_error_code_str(ret), ret);
869 	return pam_winbind_request_log(ctx, ret, username, fn);
870 }
871 
872 #if defined(HAVE_PAM_RADIO_TYPE)
873 static bool _pam_winbind_change_pwd(struct pwb_context *ctx)
874 {
875 	struct pam_message msg;
876 	const struct pam_message *pmsg;
877 	struct pam_response *resp = NULL;
878 	int ret;
879 	bool retval = false;
880 	pmsg = &msg;
881 	msg.msg_style = PAM_RADIO_TYPE;
882 	msg.msg = _("Do you want to change your password now?");
883 	ret = converse(ctx->pamh, 1, &pmsg, &resp);
884 	if (resp == NULL) {
885 		if (ret == PAM_SUCCESS) {
886 			_pam_log(ctx, LOG_CRIT, "pam_winbind: system error!\n");
887 			return false;
888 		}
889 	}
890 	if (ret != PAM_SUCCESS) {
891 		return false;
892 	}
893 	_pam_log(ctx, LOG_CRIT, "Received [%s] reply from application.\n", resp->resp);
894 
895 	if ((resp->resp != NULL) && (strcasecmp(resp->resp, "yes") == 0)) {
896 		retval = true;
897 	}
898 
899 	_pam_drop_reply(resp, 1);
900 	return retval;
901 }
902 #else
903 static bool _pam_winbind_change_pwd(struct pwb_context *ctx)
904 {
905 	return false;
906 }
907 #endif
908 
909 /**
910  * send a password expiry message if required
911  *
912  * @param ctx PAM winbind context.
913  * @param next_change expected (calculated) next expiry date.
914  * @param already_expired pointer to a boolean to indicate if the password is
915  *        already expired.
916  *
917  * @return boolean Returns true if message has been sent, false if not.
918  */
919 
920 static bool _pam_send_password_expiry_message(struct pwb_context *ctx,
921 					      time_t next_change,
922 					      time_t now,
923 					      int warn_pwd_expire,
924 					      bool *already_expired,
925 					      bool *change_pwd)
926 {
927 	int days = 0;
928 	struct tm tm_now, tm_next_change;
929 	bool retval = false;
930 	int ret;
931 
932 	if (already_expired) {
933 		*already_expired = false;
934 	}
935 
936 	if (change_pwd) {
937 		*change_pwd = false;
938 	}
939 
940 	if (next_change <= now) {
941 		PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PASSWORD_EXPIRED");
942 		if (already_expired) {
943 			*already_expired = true;
944 		}
945 		return true;
946 	}
947 
948 	if ((next_change < 0) ||
949 	    (next_change > now + warn_pwd_expire * SECONDS_PER_DAY)) {
950 		return false;
951 	}
952 
953 	if ((localtime_r(&now, &tm_now) == NULL) ||
954 	    (localtime_r(&next_change, &tm_next_change) == NULL)) {
955 		return false;
956 	}
957 
958 	days = (tm_next_change.tm_yday+tm_next_change.tm_year*365) -
959 	       (tm_now.tm_yday+tm_now.tm_year*365);
960 
961 	if (days == 0) {
962 		ret = _make_remark(ctx, PAM_TEXT_INFO,
963 				_("Your password expires today.\n"));
964 
965 		/*
966 		 * If change_pwd and already_expired is null.
967 		 * We are just sending a notification message.
968 		 * We don't expect any response in this case.
969 		 */
970 
971 		if (!change_pwd && !already_expired) {
972 			return true;
973 		}
974 
975 		/*
976 		 * successfully sent the warning message.
977 		 * Give the user a chance to change pwd.
978 		 */
979 		if (ret == PAM_SUCCESS) {
980 			if (change_pwd) {
981 				retval = _pam_winbind_change_pwd(ctx);
982 				if (retval) {
983 					*change_pwd = true;
984 				}
985 			}
986 		}
987 		return true;
988 	}
989 
990 	if (days > 0 && days < warn_pwd_expire) {
991 
992 		ret = _make_remark_format(ctx, PAM_TEXT_INFO,
993 					_("Your password will expire in %d %s.\n"),
994 					days, (days > 1) ? _("days"):_("day"));
995 		/*
996 		 * If change_pwd and already_expired is null.
997 		 * We are just sending a notification message.
998 		 * We don't expect any response in this case.
999 		 */
1000 
1001 		if (!change_pwd && !already_expired) {
1002 			return true;
1003 		}
1004 
1005 		/*
1006 		 * successfully sent the warning message.
1007 		 * Give the user a chance to change pwd.
1008 		 */
1009 		if (ret == PAM_SUCCESS) {
1010 			if (change_pwd) {
1011 				retval = _pam_winbind_change_pwd(ctx);
1012 				if (retval) {
1013 					*change_pwd = true;
1014 				}
1015 			}
1016 		}
1017 		return true;
1018 	}
1019 
1020 	return false;
1021 }
1022 
1023 /**
1024  * Send a warning if the password expires in the near future
1025  *
1026  * @param ctx PAM winbind context.
1027  * @param response The full authentication response structure.
1028  * @param already_expired boolean, is the pwd already expired?
1029  *
1030  * @return void.
1031  */
1032 
1033 static void _pam_warn_password_expiry(struct pwb_context *ctx,
1034 				      const struct wbcAuthUserInfo *info,
1035 				      int warn_pwd_expire,
1036 				      bool *already_expired,
1037 				      bool *change_pwd)
1038 {
1039 	time_t now = time(NULL);
1040 	time_t next_change = 0;
1041 
1042 	if (info == NULL) {
1043 		return;
1044 	}
1045 
1046 	if (already_expired) {
1047 		*already_expired = false;
1048 	}
1049 
1050 	if (change_pwd) {
1051 		*change_pwd = false;
1052 	}
1053 
1054 	/* accounts with WBC_ACB_PWNOEXP set never receive a warning */
1055 	if (info->acct_flags & WBC_ACB_PWNOEXP) {
1056 		return;
1057 	}
1058 
1059 	/* no point in sending a warning if this is a grace logon */
1060 	if (PAM_WB_GRACE_LOGON(info->user_flags)) {
1061 		return;
1062 	}
1063 
1064 	/* check if the info3 must change timestamp has been set */
1065 	next_change = info->pass_must_change_time;
1066 
1067 	if (_pam_send_password_expiry_message(ctx, next_change, now,
1068 					      warn_pwd_expire,
1069 					      already_expired,
1070 					      change_pwd)) {
1071 		return;
1072 	}
1073 
1074 	/* no warning sent */
1075 }
1076 
1077 #define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0)
1078 
1079 /**
1080  * Append a string, making sure not to overflow and to always return a
1081  * NULL-terminated string.
1082  *
1083  * @param dest Destination string buffer (must already be NULL-terminated).
1084  * @param src Source string buffer.
1085  * @param dest_buffer_size Size of dest buffer in bytes.
1086  *
1087  * @return false if dest buffer is not big enough (no bytes copied), true on
1088  * success.
1089  */
1090 
1091 static bool safe_append_string(char *dest,
1092 			       const char *src,
1093 			       int dest_buffer_size)
1094 {
1095 	size_t len;
1096 	len = strlcat(dest, src, dest_buffer_size);
1097 	return (len < dest_buffer_size);
1098 }
1099 
1100 /**
1101  * Convert a names into a SID string, appending it to a buffer.
1102  *
1103  * @param ctx PAM winbind context.
1104  * @param user User in PAM request.
1105  * @param name Name to convert.
1106  * @param sid_list_buffer Where to append the string sid.
1107  * @param sid_list_buffer Size of sid_list_buffer (in bytes).
1108  *
1109  * @return false on failure, true on success.
1110  */
1111 static bool winbind_name_to_sid_string(struct pwb_context *ctx,
1112 				       const char *user,
1113 				       const char *name,
1114 				       char *sid_list_buffer,
1115 				       int sid_list_buffer_size)
1116 {
1117 	char sid_string[WBC_SID_STRING_BUFLEN];
1118 
1119 	/* lookup name? */
1120 	if (IS_SID_STRING(name)) {
1121 		strlcpy(sid_string, name, sizeof(sid_string));
1122 	} else {
1123 		wbcErr wbc_status;
1124 		struct wbcDomainSid sid;
1125 		enum wbcSidType type;
1126 
1127 		_pam_log_debug(ctx, LOG_DEBUG,
1128 			       "no sid given, looking up: %s\n", name);
1129 
1130 		wbc_status = wbcCtxLookupName(ctx->wbc_ctx,
1131 					      "",
1132 					      name,
1133 					      &sid,
1134 					      &type);
1135 		if (!WBC_ERROR_IS_OK(wbc_status)) {
1136 			_pam_log(ctx, LOG_INFO,
1137 				 "could not lookup name: %s\n", name);
1138 			return false;
1139 		}
1140 
1141 		wbcSidToStringBuf(&sid, sid_string, sizeof(sid_string));
1142 	}
1143 
1144 	if (!safe_append_string(sid_list_buffer, sid_string,
1145 				sid_list_buffer_size)) {
1146 		return false;
1147 	}
1148 	return true;
1149 }
1150 
1151 /**
1152  * Convert a list of names into a list of sids.
1153  *
1154  * @param ctx PAM winbind context.
1155  * @param user User in PAM request.
1156  * @param name_list List of names or string sids, separated by commas.
1157  * @param sid_list_buffer Where to put the list of string sids.
1158  * @param sid_list_buffer Size of sid_list_buffer (in bytes).
1159  *
1160  * @return false on failure, true on success.
1161  */
1162 static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx,
1163 						 const char *user,
1164 						 const char *name_list,
1165 						 char *sid_list_buffer,
1166 						 int sid_list_buffer_size)
1167 {
1168 	bool result = false;
1169 	char *current_name = NULL;
1170 	const char *search_location;
1171 	const char *comma;
1172 	int len;
1173 
1174 	if (sid_list_buffer_size > 0) {
1175 		sid_list_buffer[0] = 0;
1176 	}
1177 
1178 	search_location = name_list;
1179 	while ((comma = strchr(search_location, ',')) != NULL) {
1180 		current_name = strndup(search_location,
1181 				       comma - search_location);
1182 		if (NULL == current_name) {
1183 			goto out;
1184 		}
1185 
1186 		if (!winbind_name_to_sid_string(ctx, user,
1187 						current_name,
1188 						sid_list_buffer,
1189 						sid_list_buffer_size)) {
1190 			/*
1191 			 * If one group name failed, we must not fail
1192 			 * the authentication totally, continue with
1193 			 * the following group names. If user belongs to
1194 			 * one of the valid groups, we must allow it
1195 			 * login. -- BoYang
1196 			 */
1197 
1198 			_pam_log(ctx, LOG_INFO, "cannot convert group %s to sid, "
1199 				 "check if group %s is valid group.", current_name,
1200 				 current_name);
1201 			_make_remark_format(ctx, PAM_TEXT_INFO, _("Cannot convert group %s "
1202 					"to sid, please contact your administrator to see "
1203 					"if group %s is valid."), current_name, current_name);
1204 			SAFE_FREE(current_name);
1205 			search_location = comma + 1;
1206 			continue;
1207 		}
1208 
1209 		SAFE_FREE(current_name);
1210 
1211 		if (!safe_append_string(sid_list_buffer, ",",
1212 					sid_list_buffer_size)) {
1213 			goto out;
1214 		}
1215 
1216 		search_location = comma + 1;
1217 	}
1218 
1219 	if (!winbind_name_to_sid_string(ctx, user, search_location,
1220 					sid_list_buffer,
1221 					sid_list_buffer_size)) {
1222 		_pam_log(ctx, LOG_INFO, "cannot convert group %s to sid, "
1223 			 "check if group %s is valid group.", search_location,
1224 			 search_location);
1225 		_make_remark_format(ctx, PAM_TEXT_INFO, _("Cannot convert group %s "
1226 				"to sid, please contact your administrator to see "
1227 				"if group %s is valid."), search_location, search_location);
1228 
1229 		/* If no valid groups were converted we should fail outright */
1230 		if (name_list != NULL && strlen(sid_list_buffer) == 0) {
1231 			result = false;
1232 			goto out;
1233 		}
1234 		/*
1235 		 * The lookup of the last name failed..
1236 		 * It results in require_member_of_sid ends with ','
1237 		 * It is malformatted parameter here, overwrite the last ','.
1238 		 */
1239 		len = strlen(sid_list_buffer);
1240 		if ((len != 0) && (sid_list_buffer[len - 1] == ',')) {
1241 			sid_list_buffer[len - 1] = '\0';
1242 		}
1243 	}
1244 
1245 	result = true;
1246 
1247 out:
1248 	SAFE_FREE(current_name);
1249 	return result;
1250 }
1251 
1252 /**
1253  * put krb5ccname variable into environment
1254  *
1255  * @param ctx PAM winbind context.
1256  * @param krb5ccname env variable retrieved from winbindd.
1257  *
1258  * @return void.
1259  */
1260 
1261 static void _pam_setup_krb5_env(struct pwb_context *ctx,
1262 				struct wbcLogonUserInfo *info)
1263 {
1264 	char *var = NULL;
1265 	int ret;
1266 	uint32_t i;
1267 	const char *krb5ccname = NULL;
1268 
1269 	if (off(ctx->ctrl, WINBIND_KRB5_AUTH)) {
1270 		return;
1271 	}
1272 
1273 	if (!info) {
1274 		return;
1275 	}
1276 
1277 	for (i=0; i < info->num_blobs; i++) {
1278 		if (strcasecmp(info->blobs[i].name, "krb5ccname") == 0) {
1279 			krb5ccname = (const char *)info->blobs[i].blob.data;
1280 			break;
1281 		}
1282 	}
1283 
1284 	if (!krb5ccname || (strlen(krb5ccname) == 0)) {
1285 		return;
1286 	}
1287 
1288 	_pam_log_debug(ctx, LOG_DEBUG,
1289 		       "request returned KRB5CCNAME: %s", krb5ccname);
1290 
1291 	if (asprintf(&var, "KRB5CCNAME=%s", krb5ccname) == -1) {
1292 		return;
1293 	}
1294 
1295 	ret = pam_putenv(ctx->pamh, var);
1296 	if (ret != PAM_SUCCESS) {
1297 		_pam_log(ctx, LOG_ERR,
1298 			 "failed to set KRB5CCNAME to %s: %s",
1299 			 var, pam_strerror(ctx->pamh, ret));
1300 	}
1301 	free(var);
1302 }
1303 
1304 /**
1305  * Copy unix username if available (further processed in PAM).
1306  *
1307  * @param ctx PAM winbind context
1308  * @param user_ret A pointer that holds a pointer to a string
1309  * @param unix_username A username
1310  *
1311  * @return void.
1312  */
1313 
1314 static void _pam_setup_unix_username(struct pwb_context *ctx,
1315 				     char **user_ret,
1316 				     struct wbcLogonUserInfo *info)
1317 {
1318 	const char *unix_username = NULL;
1319 	uint32_t i;
1320 
1321 	if (!user_ret || !info) {
1322 		return;
1323 	}
1324 
1325 	for (i=0; i < info->num_blobs; i++) {
1326 		if (strcasecmp(info->blobs[i].name, "unix_username") == 0) {
1327 			unix_username = (const char *)info->blobs[i].blob.data;
1328 			break;
1329 		}
1330 	}
1331 
1332 	if (!unix_username || !unix_username[0]) {
1333 		return;
1334 	}
1335 
1336 	*user_ret = strdup(unix_username);
1337 }
1338 
1339 /**
1340  * Set string into the PAM stack.
1341  *
1342  * @param ctx PAM winbind context.
1343  * @param data_name Key name for pam_set_data.
1344  * @param value String value.
1345  *
1346  * @return void.
1347  */
1348 
1349 static void _pam_set_data_string(struct pwb_context *ctx,
1350 				 const char *data_name,
1351 				 const char *value)
1352 {
1353 	int ret;
1354 
1355 	if (!data_name || !value || (strlen(data_name) == 0) ||
1356 	     (strlen(value) == 0)) {
1357 		return;
1358 	}
1359 
1360 	ret = pam_set_data(ctx->pamh, data_name, talloc_strdup(NULL, value),
1361 			   _pam_winbind_cleanup_func);
1362 	if (ret != PAM_SUCCESS) {
1363 		_pam_log_debug(ctx, LOG_DEBUG,
1364 			       "Could not set data %s: %s\n",
1365 			       data_name, pam_strerror(ctx->pamh, ret));
1366 	}
1367 }
1368 
1369 /**
1370  * Set info3 strings into the PAM stack.
1371  *
1372  * @param ctx PAM winbind context.
1373  * @param data_name Key name for pam_set_data.
1374  * @param value String value.
1375  *
1376  * @return void.
1377  */
1378 
1379 static void _pam_set_data_info3(struct pwb_context *ctx,
1380 				const struct wbcAuthUserInfo *info)
1381 {
1382 	if (info != NULL) {
1383 		_pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR,
1384 			     info->home_directory);
1385 		_pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT,
1386 			     info->logon_script);
1387 		_pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER,
1388 			     info->logon_server);
1389 		_pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH,
1390 			     info->profile_path);
1391 	}
1392 }
1393 
1394 /**
1395  * Free info3 strings in the PAM stack.
1396  *
1397  * @param pamh PAM handle
1398  *
1399  * @return void.
1400  */
1401 
1402 static void _pam_free_data_info3(pam_handle_t *pamh)
1403 {
1404 	pam_set_data(pamh, PAM_WINBIND_HOMEDIR, NULL, NULL);
1405 	pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT, NULL, NULL);
1406 	pam_set_data(pamh, PAM_WINBIND_LOGONSERVER, NULL, NULL);
1407 	pam_set_data(pamh, PAM_WINBIND_PROFILEPATH, NULL, NULL);
1408 }
1409 
1410 /**
1411  * Send PAM_ERROR_MSG for cached or grace logons.
1412  *
1413  * @param ctx PAM winbind context.
1414  * @param username User in PAM request.
1415  * @param info3_user_flgs Info3 flags containing logon type bits.
1416  *
1417  * @return void.
1418  */
1419 
1420 static void _pam_warn_logon_type(struct pwb_context *ctx,
1421 				 const char *username,
1422 				 uint32_t info3_user_flgs)
1423 {
1424 	/* inform about logon type */
1425 	if (PAM_WB_GRACE_LOGON(info3_user_flgs)) {
1426 
1427 		_make_remark(ctx, PAM_ERROR_MSG,
1428 			     _("Grace login. "
1429 			       "Please change your password as soon you're "
1430 			       "online again"));
1431 		_pam_log_debug(ctx, LOG_DEBUG,
1432 			       "User %s logged on using grace logon\n",
1433 			       username);
1434 
1435 	} else if (PAM_WB_CACHED_LOGON(info3_user_flgs)) {
1436 
1437 		_make_remark(ctx, PAM_ERROR_MSG,
1438 			     _("Domain Controller unreachable, "
1439 			       "using cached credentials instead. "
1440 			       "Network resources may be unavailable"));
1441 		_pam_log_debug(ctx, LOG_DEBUG,
1442 			       "User %s logged on using cached credentials\n",
1443 			       username);
1444 	}
1445 }
1446 
1447 /**
1448  * Send PAM_ERROR_MSG for krb5 errors.
1449  *
1450  * @param ctx PAM winbind context.
1451  * @param username User in PAM request.
1452  * @param info3_user_flgs Info3 flags containing logon type bits.
1453  *
1454  * @return void.
1455  */
1456 
1457 static void _pam_warn_krb5_failure(struct pwb_context *ctx,
1458 				   const char *username,
1459 				   uint32_t info3_user_flgs)
1460 {
1461 	if (PAM_WB_KRB5_CLOCK_SKEW(info3_user_flgs)) {
1462 		_make_remark(ctx, PAM_ERROR_MSG,
1463 			     _("Failed to establish your Kerberos Ticket cache "
1464 			       "due time differences\n"
1465 			       "with the domain controller.  "
1466 			       "Please verify the system time.\n"));
1467 		_pam_log_debug(ctx, LOG_DEBUG,
1468 			       "User %s: Clock skew when getting Krb5 TGT\n",
1469 			       username);
1470 	}
1471 }
1472 
1473 static bool _pam_check_remark_auth_err(struct pwb_context *ctx,
1474 				       const struct wbcAuthErrorInfo *e,
1475 				       const char *nt_status_string,
1476 				       int *pam_err)
1477 {
1478 	const char *ntstatus = NULL;
1479 	const char *error_string = NULL;
1480 
1481 	if (!e || !pam_err) {
1482 		return false;
1483 	}
1484 
1485 	ntstatus = e->nt_string;
1486 	if (!ntstatus) {
1487 		return false;
1488 	}
1489 
1490 	if (strcasecmp(ntstatus, nt_status_string) == 0) {
1491 
1492 		error_string = _get_ntstatus_error_string(nt_status_string);
1493 		if (error_string) {
1494 			_make_remark(ctx, PAM_ERROR_MSG, error_string);
1495 			*pam_err = e->pam_error;
1496 			return true;
1497 		}
1498 
1499 		if (e->display_string) {
1500 			_make_remark(ctx, PAM_ERROR_MSG, _(e->display_string));
1501 			*pam_err = e->pam_error;
1502 			return true;
1503 		}
1504 
1505 		_make_remark(ctx, PAM_ERROR_MSG, nt_status_string);
1506 		*pam_err = e->pam_error;
1507 
1508 		return true;
1509 	}
1510 
1511 	return false;
1512 };
1513 
1514 /**
1515  * Compose Password Restriction String for a PAM_ERROR_MSG conversation.
1516  *
1517  * @param i The wbcUserPasswordPolicyInfo struct.
1518  *
1519  * @return string (caller needs to talloc_free).
1520  */
1521 
1522 static char *_pam_compose_pwd_restriction_string(struct pwb_context *ctx,
1523 						 struct wbcUserPasswordPolicyInfo *i)
1524 {
1525 	char *str = NULL;
1526 
1527 	if (!i) {
1528 		goto failed;
1529 	}
1530 
1531 	str = talloc_asprintf(ctx, _("Your password "));
1532 	if (!str) {
1533 		goto failed;
1534 	}
1535 
1536 	if (i->min_length_password > 0) {
1537 		str = talloc_asprintf_append(str,
1538 			       _("must be at least %d characters; "),
1539 			       i->min_length_password);
1540 		if (!str) {
1541 			goto failed;
1542 		}
1543 	}
1544 
1545 	if (i->password_history > 0) {
1546 		str = talloc_asprintf_append(str,
1547 			       _("cannot repeat any of your previous %d "
1548 			        "passwords; "),
1549 			       i->password_history);
1550 		if (!str) {
1551 			goto failed;
1552 		}
1553 	}
1554 
1555 	if (i->password_properties & WBC_DOMAIN_PASSWORD_COMPLEX) {
1556 		str = talloc_asprintf_append(str,
1557 			       _("must contain capitals, numerals "
1558 			         "or punctuation; "
1559 			         "and cannot contain your account "
1560 			         "or full name; "));
1561 		if (!str) {
1562 			goto failed;
1563 		}
1564 	}
1565 
1566 	str = talloc_asprintf_append(str,
1567 		       _("Please type a different password. "
1568 		         "Type a password which meets these requirements in "
1569 		         "both text boxes."));
1570 	if (!str) {
1571 		goto failed;
1572 	}
1573 
1574 	return str;
1575 
1576  failed:
1577 	TALLOC_FREE(str);
1578 	return NULL;
1579 }
1580 
1581 static int _pam_create_homedir(struct pwb_context *ctx,
1582 			       const char *dirname,
1583 			       mode_t mode)
1584 {
1585 	struct stat sbuf;
1586 
1587 	if (stat(dirname, &sbuf) == 0) {
1588 		return PAM_SUCCESS;
1589 	}
1590 
1591 	if (mkdir(dirname, mode) != 0) {
1592 
1593 		_make_remark_format(ctx, PAM_TEXT_INFO,
1594 				    _("Creating directory: %s failed: %s"),
1595 				    dirname, strerror(errno));
1596 		_pam_log(ctx, LOG_ERR, "could not create dir: %s (%s)",
1597 		 dirname, strerror(errno));
1598 		 return PAM_PERM_DENIED;
1599 	}
1600 
1601 	return PAM_SUCCESS;
1602 }
1603 
1604 static int _pam_chown_homedir(struct pwb_context *ctx,
1605 			      const char *dirname,
1606 			      uid_t uid,
1607 			      gid_t gid)
1608 {
1609 	if (chown(dirname, uid, gid) != 0) {
1610 		_pam_log(ctx, LOG_ERR, "failed to chown user homedir: %s (%s)",
1611 			 dirname, strerror(errno));
1612 		return PAM_PERM_DENIED;
1613 	}
1614 
1615 	return PAM_SUCCESS;
1616 }
1617 
1618 static int _pam_mkhomedir(struct pwb_context *ctx)
1619 {
1620 	struct passwd *pwd = NULL;
1621 	char *token = NULL;
1622 	char *create_dir = NULL;
1623 	char *user_dir = NULL;
1624 	int ret;
1625 	const char *username;
1626 	mode_t mode = 0700;
1627 	char *safe_ptr = NULL;
1628 	char *p = NULL;
1629 
1630 	/* Get the username */
1631 	ret = pam_get_user(ctx->pamh, &username, NULL);
1632 	if ((ret != PAM_SUCCESS) || (!username)) {
1633 		_pam_log_debug(ctx, LOG_DEBUG, "can not get the username");
1634 		return PAM_SERVICE_ERR;
1635 	}
1636 
1637 	pwd = getpwnam(username);
1638 	if (pwd == NULL) {
1639 		_pam_log_debug(ctx, LOG_DEBUG, "can not get the username");
1640 		return PAM_USER_UNKNOWN;
1641 	}
1642 	_pam_log_debug(ctx, LOG_DEBUG, "homedir is: %s", pwd->pw_dir);
1643 
1644 	ret = _pam_create_homedir(ctx, pwd->pw_dir, 0700);
1645 	if (ret == PAM_SUCCESS) {
1646 		ret = _pam_chown_homedir(ctx, pwd->pw_dir,
1647 					 pwd->pw_uid,
1648 					 pwd->pw_gid);
1649 	}
1650 
1651 	if (ret == PAM_SUCCESS) {
1652 		return ret;
1653 	}
1654 
1655 	/* maybe we need to create parent dirs */
1656 	create_dir = talloc_strdup(ctx, "/");
1657 	if (!create_dir) {
1658 		return PAM_BUF_ERR;
1659 	}
1660 
1661 	/* find final directory */
1662 	user_dir = strrchr(pwd->pw_dir, '/');
1663 	if (!user_dir) {
1664 		return PAM_BUF_ERR;
1665 	}
1666 	user_dir++;
1667 
1668 	_pam_log(ctx, LOG_DEBUG, "final directory: %s", user_dir);
1669 
1670 	p = pwd->pw_dir;
1671 
1672 	while ((token = strtok_r(p, "/", &safe_ptr)) != NULL) {
1673 
1674 		mode = 0755;
1675 
1676 		p = NULL;
1677 
1678 		_pam_log_debug(ctx, LOG_DEBUG, "token is %s", token);
1679 
1680 		create_dir = talloc_asprintf_append(create_dir, "%s/", token);
1681 		if (!create_dir) {
1682 			return PAM_BUF_ERR;
1683 		}
1684 		_pam_log_debug(ctx, LOG_DEBUG, "current_dir is %s", create_dir);
1685 
1686 		if (strcmp(token, user_dir) == 0) {
1687 			_pam_log_debug(ctx, LOG_DEBUG, "assuming last directory: %s", token);
1688 			mode = 0700;
1689 		}
1690 
1691 		ret = _pam_create_homedir(ctx, create_dir, mode);
1692 		if (ret != PAM_SUCCESS) {
1693 			return ret;
1694 		}
1695 	}
1696 
1697 	return _pam_chown_homedir(ctx, create_dir,
1698 				  pwd->pw_uid,
1699 				  pwd->pw_gid);
1700 }
1701 
1702 /* talk to winbindd */
1703 static int winbind_auth_request(struct pwb_context *ctx,
1704 				const char *user,
1705 				const char *pass,
1706 				const char *member,
1707 				const char *cctype,
1708 				const int warn_pwd_expire,
1709 				struct wbcAuthErrorInfo **p_error,
1710 				struct wbcLogonUserInfo **p_info,
1711 				time_t *pwd_last_set,
1712 				char **user_ret)
1713 {
1714 	wbcErr wbc_status;
1715 	struct wbcLogonUserParams logon;
1716 	char membership_of[1024];
1717 	uid_t user_uid = -1;
1718 	uint32_t flags = WBFLAG_PAM_INFO3_TEXT;
1719 	struct wbcLogonUserInfo *info = NULL;
1720 	struct wbcAuthUserInfo *user_info = NULL;
1721 	struct wbcAuthErrorInfo *error = NULL;
1722 	int ret = PAM_AUTH_ERR;
1723 	int i;
1724 	const char *codes[] = {
1725 		"NT_STATUS_PASSWORD_EXPIRED",
1726 		"NT_STATUS_PASSWORD_MUST_CHANGE",
1727 		"NT_STATUS_INVALID_WORKSTATION",
1728 		"NT_STATUS_INVALID_LOGON_HOURS",
1729 		"NT_STATUS_ACCOUNT_EXPIRED",
1730 		"NT_STATUS_ACCOUNT_DISABLED",
1731 		"NT_STATUS_ACCOUNT_LOCKED_OUT",
1732 		"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
1733 		"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
1734 		"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
1735 		"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
1736 		"NT_STATUS_NO_LOGON_SERVERS",
1737 		"NT_STATUS_WRONG_PASSWORD",
1738 		"NT_STATUS_ACCESS_DENIED"
1739 	};
1740 
1741 	if (pwd_last_set) {
1742 		*pwd_last_set = 0;
1743 	}
1744 
1745 	/* Krb5 auth always has to go against the KDC of the user's realm */
1746 
1747 	if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1748 		flags		|= WBFLAG_PAM_CONTACT_TRUSTDOM;
1749 	}
1750 
1751 	if (ctx->ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) {
1752 		struct passwd *pwd = NULL;
1753 
1754 		pwd = getpwnam(user);
1755 		if (pwd == NULL) {
1756 			return PAM_USER_UNKNOWN;
1757 		}
1758 		user_uid	= pwd->pw_uid;
1759 	}
1760 
1761 	if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1762 
1763 		_pam_log_debug(ctx, LOG_DEBUG,
1764 			       "enabling krb5 login flag\n");
1765 
1766 		flags		|= WBFLAG_PAM_KRB5 |
1767 				   WBFLAG_PAM_FALLBACK_AFTER_KRB5;
1768 	}
1769 
1770 	if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
1771 		_pam_log_debug(ctx, LOG_DEBUG,
1772 			       "enabling cached login flag\n");
1773 		flags		|= WBFLAG_PAM_CACHED_LOGIN;
1774 	}
1775 
1776 	if (user_ret) {
1777 		*user_ret = NULL;
1778 		flags		|= WBFLAG_PAM_UNIX_NAME;
1779 	}
1780 
1781 	if (cctype != NULL) {
1782 		_pam_log_debug(ctx, LOG_DEBUG,
1783 			       "enabling request for a %s krb5 ccache\n",
1784 			       cctype);
1785 	}
1786 
1787 	if (member != NULL) {
1788 
1789 		ZERO_STRUCT(membership_of);
1790 
1791 		if (!winbind_name_list_to_sid_string_list(ctx, user, member,
1792 							  membership_of,
1793 							  sizeof(membership_of))) {
1794 			_pam_log_debug(ctx, LOG_ERR,
1795 				       "failed to serialize membership of sid "
1796 				       "\"%s\"\n", member);
1797 			return PAM_AUTH_ERR;
1798 		}
1799 	}
1800 
1801 	ZERO_STRUCT(logon);
1802 
1803 	logon.username			= user;
1804 	logon.password			= pass;
1805 
1806 	if (cctype) {
1807 		wbc_status = wbcAddNamedBlob(&logon.num_blobs,
1808 					     &logon.blobs,
1809 					     "krb5_cc_type",
1810 					     0,
1811 					     discard_const_p(uint8_t, cctype),
1812 					     strlen(cctype)+1);
1813 		if (!WBC_ERROR_IS_OK(wbc_status)) {
1814 			goto done;
1815 		}
1816 	}
1817 
1818 	wbc_status = wbcAddNamedBlob(&logon.num_blobs,
1819 				     &logon.blobs,
1820 				     "flags",
1821 				     0,
1822 				     (uint8_t *)&flags,
1823 				     sizeof(flags));
1824 	if (!WBC_ERROR_IS_OK(wbc_status)) {
1825 		goto done;
1826 	}
1827 
1828 	wbc_status = wbcAddNamedBlob(&logon.num_blobs,
1829 				     &logon.blobs,
1830 				     "user_uid",
1831 				     0,
1832 				     (uint8_t *)&user_uid,
1833 				     sizeof(user_uid));
1834 	if (!WBC_ERROR_IS_OK(wbc_status)) {
1835 		goto done;
1836 	}
1837 
1838 	if (member) {
1839 		wbc_status = wbcAddNamedBlob(&logon.num_blobs,
1840 					     &logon.blobs,
1841 					     "membership_of",
1842 					     0,
1843 					     (uint8_t *)membership_of,
1844 					     sizeof(membership_of));
1845 		if (!WBC_ERROR_IS_OK(wbc_status)) {
1846 			goto done;
1847 		}
1848 	}
1849 
1850 	wbc_status = wbcCtxLogonUser(ctx->wbc_ctx,
1851 				     &logon,
1852 				     &info,
1853 				     &error,
1854 				     NULL);
1855 	ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
1856 					  user, "wbcLogonUser");
1857 	wbcFreeMemory(logon.blobs);
1858 	logon.blobs = NULL;
1859 
1860 	if (info && info->info) {
1861 		user_info = info->info;
1862 	}
1863 
1864 	if (pwd_last_set && user_info) {
1865 		*pwd_last_set = user_info->pass_last_set_time;
1866 	}
1867 
1868 	if (p_info && info) {
1869 		*p_info = info;
1870 	}
1871 
1872 	if (p_error && error) {
1873 		/* We want to process the error in the caller. */
1874 		*p_error = error;
1875 		return ret;
1876 	}
1877 
1878 	for (i=0; i<ARRAY_SIZE(codes); i++) {
1879 		int _ret = ret;
1880 		if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
1881 			ret = _ret;
1882 			goto done;
1883 		}
1884 	}
1885 
1886 	if ((ret == PAM_SUCCESS) && user_info && info) {
1887 
1888 		bool already_expired = false;
1889 		bool change_pwd = false;
1890 
1891 		/* warn a user if the password is about to expire soon */
1892 		_pam_warn_password_expiry(ctx, user_info,
1893 					  warn_pwd_expire,
1894 					  &already_expired,
1895 					  &change_pwd);
1896 
1897 		if (already_expired == true) {
1898 
1899 			SMB_TIME_T last_set = user_info->pass_last_set_time;
1900 			SMB_TIME_T must_set = user_info->pass_must_change_time;
1901 
1902 			_pam_log_debug(ctx, LOG_DEBUG,
1903 				       "Password has expired "
1904 				       "(Password was last set: %lld, "
1905 				       "it must be changed here "
1906 				       "%lld (now it's: %ld))\n",
1907 				       (long long int)last_set,
1908 				       (long long int)must_set,
1909 				       (long)time(NULL));
1910 
1911 			return PAM_AUTHTOK_EXPIRED;
1912 		}
1913 
1914 		if (change_pwd) {
1915 			ret = PAM_NEW_AUTHTOK_REQD;
1916 			goto done;
1917 		}
1918 
1919 		/* inform about logon type */
1920 		_pam_warn_logon_type(ctx, user, user_info->user_flags);
1921 
1922 		/* inform about krb5 failures */
1923 		_pam_warn_krb5_failure(ctx, user, user_info->user_flags);
1924 
1925 		/* set some info3 info for other modules in the stack */
1926 		_pam_set_data_info3(ctx, user_info);
1927 
1928 		/* put krb5ccname into env */
1929 		_pam_setup_krb5_env(ctx, info);
1930 
1931 		/* If winbindd returned a username, return the pointer to it
1932 		 * here. */
1933 		_pam_setup_unix_username(ctx, user_ret, info);
1934 	}
1935 
1936  done:
1937 	wbcFreeMemory(logon.blobs);
1938 	if (info && info->blobs && !p_info) {
1939 		wbcFreeMemory(info->blobs);
1940 		/*
1941 		 * We set blobs to NULL to prevent a use after free in the
1942 		 * in the wbcLogonUserInfoDestructor
1943 		 */
1944 		info->blobs = NULL;
1945 	}
1946 	if (error && !p_error) {
1947 		wbcFreeMemory(error);
1948 	}
1949 	if (info && !p_info) {
1950 		wbcFreeMemory(info);
1951 	}
1952 
1953 	return ret;
1954 }
1955 
1956 /* talk to winbindd */
1957 static int winbind_chauthtok_request(struct pwb_context *ctx,
1958 				     const char *user,
1959 				     const char *oldpass,
1960 				     const char *newpass,
1961 				     time_t pwd_last_set)
1962 {
1963 	wbcErr wbc_status;
1964 	struct wbcChangePasswordParams params;
1965 	struct wbcAuthErrorInfo *error = NULL;
1966 	struct wbcUserPasswordPolicyInfo *policy = NULL;
1967 	enum wbcPasswordChangeRejectReason reject_reason = -1;
1968 	uint32_t flags = 0;
1969 
1970 	int i;
1971 	const char *codes[] = {
1972 		"NT_STATUS_BACKUP_CONTROLLER",
1973 		"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
1974 		"NT_STATUS_NO_LOGON_SERVERS",
1975 		"NT_STATUS_ACCESS_DENIED",
1976 		"NT_STATUS_PWD_TOO_SHORT", /* TODO: tell the min pwd length ? */
1977 		"NT_STATUS_PWD_TOO_RECENT", /* TODO: tell the minage ? */
1978 		"NT_STATUS_PWD_HISTORY_CONFLICT" /* TODO: tell the history length ? */
1979 	};
1980 	int ret = PAM_AUTH_ERR;
1981 
1982 	ZERO_STRUCT(params);
1983 
1984 	if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1985 		flags |= WBFLAG_PAM_KRB5 |
1986 			 WBFLAG_PAM_CONTACT_TRUSTDOM;
1987 	}
1988 
1989 	if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
1990 		flags |= WBFLAG_PAM_CACHED_LOGIN;
1991 	}
1992 
1993 	params.account_name		= user;
1994 	params.level			= WBC_CHANGE_PASSWORD_LEVEL_PLAIN;
1995 	params.old_password.plaintext	= oldpass;
1996 	params.new_password.plaintext	= newpass;
1997 	params.flags			= flags;
1998 
1999 	wbc_status = wbcCtxChangeUserPasswordEx(ctx->wbc_ctx,
2000 						&params,
2001 						&error,
2002 						&reject_reason,
2003 						&policy);
2004 	ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
2005 					  user, "wbcChangeUserPasswordEx");
2006 
2007 	if (WBC_ERROR_IS_OK(wbc_status)) {
2008 		_pam_log(ctx, LOG_NOTICE,
2009 			 "user '%s' password changed", user);
2010 		return PAM_SUCCESS;
2011 	}
2012 
2013 	if (!error) {
2014 		wbcFreeMemory(policy);
2015 		return ret;
2016 	}
2017 
2018 	for (i=0; i<ARRAY_SIZE(codes); i++) {
2019 		int _ret = ret;
2020 		if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
2021 			ret = _ret;
2022 			goto done;
2023 		}
2024 	}
2025 
2026 	if (!strcasecmp(error->nt_string,
2027 			"NT_STATUS_PASSWORD_RESTRICTION")) {
2028 
2029 		char *pwd_restriction_string = NULL;
2030 		SMB_TIME_T min_pwd_age = 0;
2031 
2032 		if (policy) {
2033 			min_pwd_age	= policy->min_passwordage;
2034 		}
2035 
2036 		/* FIXME: avoid to send multiple PAM messages after another */
2037 		switch ((int)reject_reason) {
2038 			case -1:
2039 				break;
2040 			case WBC_PWD_CHANGE_NO_ERROR:
2041 				if ((min_pwd_age > 0) &&
2042 				    (pwd_last_set + min_pwd_age > time(NULL))) {
2043 					PAM_WB_REMARK_DIRECT(ctx,
2044 					     "NT_STATUS_PWD_TOO_RECENT");
2045 				}
2046 				break;
2047 			case WBC_PWD_CHANGE_PASSWORD_TOO_SHORT:
2048 				PAM_WB_REMARK_DIRECT(ctx,
2049 					"NT_STATUS_PWD_TOO_SHORT");
2050 				break;
2051 			case WBC_PWD_CHANGE_PWD_IN_HISTORY:
2052 				PAM_WB_REMARK_DIRECT(ctx,
2053 					"NT_STATUS_PWD_HISTORY_CONFLICT");
2054 				break;
2055 			case WBC_PWD_CHANGE_NOT_COMPLEX:
2056 				_make_remark(ctx, PAM_ERROR_MSG,
2057 					     _("Password does not meet "
2058 					       "complexity requirements"));
2059 				break;
2060 			default:
2061 				_pam_log_debug(ctx, LOG_DEBUG,
2062 					       "unknown password change "
2063 					       "reject reason: %d",
2064 					       reject_reason);
2065 				break;
2066 		}
2067 
2068 		pwd_restriction_string =
2069 			_pam_compose_pwd_restriction_string(ctx, policy);
2070 		if (pwd_restriction_string) {
2071 			_make_remark(ctx, PAM_ERROR_MSG,
2072 				     pwd_restriction_string);
2073 			TALLOC_FREE(pwd_restriction_string);
2074 		}
2075 	}
2076  done:
2077 	wbcFreeMemory(error);
2078 	wbcFreeMemory(policy);
2079 
2080 	return ret;
2081 }
2082 
2083 /*
2084  * Checks if a user has an account
2085  *
2086  * return values:
2087  *	 1  = User not found
2088  *	 0  = OK
2089  * 	-1  = System error
2090  */
2091 static int valid_user(struct pwb_context *ctx,
2092 		      const char *user)
2093 {
2094 	/* check not only if the user is available over NSS calls, also make
2095 	 * sure it's really a winbind user, this is important when stacking PAM
2096 	 * modules in the 'account' or 'password' facility. */
2097 
2098 	wbcErr wbc_status;
2099 	struct passwd *pwd = NULL;
2100 	struct passwd *wb_pwd = NULL;
2101 
2102 	pwd = getpwnam(user);
2103 	if (pwd == NULL) {
2104 		return 1;
2105 	}
2106 
2107 	wbc_status = wbcCtxGetpwnam(ctx->wbc_ctx, user, &wb_pwd);
2108 	wbcFreeMemory(wb_pwd);
2109 	if (!WBC_ERROR_IS_OK(wbc_status)) {
2110 		_pam_log(ctx, LOG_DEBUG, "valid_user: wbcGetpwnam gave %s\n",
2111 			wbcErrorString(wbc_status));
2112 	}
2113 
2114 	switch (wbc_status) {
2115 		case WBC_ERR_UNKNOWN_USER:
2116 		/* match other insane libwbclient return codes */
2117 		case WBC_ERR_WINBIND_NOT_AVAILABLE:
2118 		case WBC_ERR_DOMAIN_NOT_FOUND:
2119 			return 1;
2120 		case WBC_ERR_SUCCESS:
2121 			return 0;
2122 		default:
2123 			break;
2124 	}
2125 	return -1;
2126 }
2127 
2128 static char *_pam_delete(register char *xx)
2129 {
2130 	_pam_overwrite(xx);
2131 	_pam_drop(xx);
2132 	return NULL;
2133 }
2134 
2135 /*
2136  * obtain a password from the user
2137  */
2138 
2139 static int _winbind_read_password(struct pwb_context *ctx,
2140 				  unsigned int ctrl,
2141 				  const char *comment,
2142 				  const char *prompt1,
2143 				  const char *prompt2,
2144 				  const char **pass)
2145 {
2146 	int authtok_flag;
2147 	int retval;
2148 	const char *item;
2149 	char *token;
2150 
2151 	_pam_log(ctx, LOG_DEBUG, "getting password (0x%08x)", ctrl);
2152 
2153 	/*
2154 	 * make sure nothing inappropriate gets returned
2155 	 */
2156 
2157 	*pass = token = NULL;
2158 
2159 	/*
2160 	 * which authentication token are we getting?
2161 	 */
2162 
2163 	if (on(WINBIND__OLD_PASSWORD, ctrl)) {
2164 		authtok_flag = PAM_OLDAUTHTOK;
2165 	} else {
2166 		authtok_flag = PAM_AUTHTOK;
2167 	}
2168 
2169 	/*
2170 	 * should we obtain the password from a PAM item ?
2171 	 */
2172 
2173 	if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) ||
2174 	    on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
2175 		retval = pam_get_item(ctx->pamh,
2176 				      authtok_flag,
2177 				      (const void **) &item);
2178 		if (retval != PAM_SUCCESS) {
2179 			/* very strange. */
2180 			_pam_log(ctx, LOG_ALERT,
2181 				 "pam_get_item returned error "
2182 				 "to unix-read-password");
2183 			return retval;
2184 		} else if (item != NULL) {	/* we have a password! */
2185 			*pass = item;
2186 			item = NULL;
2187 			_pam_log(ctx, LOG_DEBUG,
2188 				 "pam_get_item returned a password");
2189 			return PAM_SUCCESS;
2190 		} else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
2191 			return PAM_AUTHTOK_RECOVER_ERR;	/* didn't work */
2192 		} else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
2193 			   && off(WINBIND__OLD_PASSWORD, ctrl)) {
2194 			return PAM_AUTHTOK_RECOVER_ERR;
2195 		}
2196 	}
2197 	/*
2198 	 * getting here implies we will have to get the password from the
2199 	 * user directly.
2200 	 */
2201 
2202 	{
2203 		struct pam_message msg[3];
2204 		const struct pam_message *pmsg[3];
2205 		struct pam_response *resp;
2206 		int i, replies;
2207 
2208 		/* prepare to converse */
2209 
2210 		if (comment != NULL && off(ctrl, WINBIND_SILENT)) {
2211 			pmsg[0] = &msg[0];
2212 			msg[0].msg_style = PAM_TEXT_INFO;
2213 			msg[0].msg = discard_const_p(char, comment);
2214 			i = 1;
2215 		} else {
2216 			i = 0;
2217 		}
2218 
2219 		pmsg[i] = &msg[i];
2220 		msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
2221 		msg[i++].msg = discard_const_p(char, prompt1);
2222 		replies = 1;
2223 
2224 		if (prompt2 != NULL) {
2225 			pmsg[i] = &msg[i];
2226 			msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
2227 			msg[i++].msg = discard_const_p(char, prompt2);
2228 			++replies;
2229 		}
2230 		/* so call the conversation expecting i responses */
2231 		resp = NULL;
2232 		retval = converse(ctx->pamh, i, pmsg, &resp);
2233 		if (resp == NULL) {
2234 			if (retval == PAM_SUCCESS) {
2235 				retval = PAM_AUTHTOK_RECOVER_ERR;
2236 			}
2237 			goto done;
2238 		}
2239 		if (retval != PAM_SUCCESS) {
2240 			_pam_drop_reply(resp, i);
2241 			goto done;
2242 		}
2243 
2244 		/* interpret the response */
2245 
2246 		token = x_strdup(resp[i - replies].resp);
2247 		if (!token) {
2248 			_pam_log(ctx, LOG_NOTICE,
2249 				 "could not recover "
2250 				 "authentication token");
2251 			retval = PAM_AUTHTOK_RECOVER_ERR;
2252 			goto done;
2253 		}
2254 
2255 		if (replies == 2) {
2256 			/* verify that password entered correctly */
2257 			if (!resp[i - 1].resp ||
2258 			    strcmp(token, resp[i - 1].resp)) {
2259 				_pam_delete(token);	/* mistyped */
2260 				retval = PAM_AUTHTOK_RECOVER_ERR;
2261 				_make_remark(ctx, PAM_ERROR_MSG,
2262 					     MISTYPED_PASS);
2263 			}
2264 		}
2265 
2266 		/*
2267 		 * tidy up the conversation (resp_retcode) is ignored
2268 		 * -- what is it for anyway? AGM
2269 		 */
2270 		_pam_drop_reply(resp, i);
2271 	}
2272 
2273  done:
2274 	if (retval != PAM_SUCCESS) {
2275 		_pam_log_debug(ctx, LOG_DEBUG,
2276 			       "unable to obtain a password");
2277 		return retval;
2278 	}
2279 	/* 'token' is the entered password */
2280 
2281 	/* we store this password as an item */
2282 
2283 	retval = pam_set_item(ctx->pamh, authtok_flag, token);
2284 	_pam_delete(token);	/* clean it up */
2285 	if (retval != PAM_SUCCESS ||
2286 	    (retval = pam_get_item(ctx->pamh, authtok_flag, (const void **) &item)) != PAM_SUCCESS) {
2287 
2288 		_pam_log(ctx, LOG_CRIT, "error manipulating password");
2289 		return retval;
2290 
2291 	}
2292 
2293 	*pass = item;
2294 	item = NULL;		/* break link to password */
2295 
2296 	return PAM_SUCCESS;
2297 }
2298 
2299 static const char *get_conf_item_string(struct pwb_context *ctx,
2300 					const char *item,
2301 					int config_flag)
2302 {
2303 	int i = 0;
2304 	const char *parm_opt = NULL;
2305 
2306 	if (!(ctx->ctrl & config_flag)) {
2307 		goto out;
2308 	}
2309 
2310 	/* let the pam opt take precedence over the pam_winbind.conf option */
2311 	for (i=0; i<ctx->argc; i++) {
2312 
2313 		if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
2314 			char *p;
2315 
2316 			if ((p = strchr(ctx->argv[i], '=')) == NULL) {
2317 				_pam_log(ctx, LOG_INFO,
2318 					 "no \"=\" delimiter for \"%s\" found\n",
2319 					 item);
2320 				goto out;
2321 			}
2322 			_pam_log_debug(ctx, LOG_INFO,
2323 				       "PAM config: %s '%s'\n", item, p+1);
2324 			return p + 1;
2325 		}
2326 	}
2327 
2328 	if (ctx->dict) {
2329 		char *key = NULL;
2330 
2331 		key = talloc_asprintf(ctx, "global:%s", item);
2332 		if (!key) {
2333 			goto out;
2334 		}
2335 
2336 		parm_opt = tiniparser_getstring_nonempty(ctx->dict, key, NULL);
2337 		TALLOC_FREE(key);
2338 
2339 		_pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n",
2340 			       item, parm_opt);
2341 	}
2342 out:
2343 	return parm_opt;
2344 }
2345 
2346 static int get_config_item_int(struct pwb_context *ctx,
2347 			       const char *item,
2348 			       int config_flag)
2349 {
2350 	int i, parm_opt = -1;
2351 
2352 	if (!(ctx->ctrl & config_flag)) {
2353 		goto out;
2354 	}
2355 
2356 	/* let the pam opt take precedence over the pam_winbind.conf option */
2357 	for (i = 0; i < ctx->argc; i++) {
2358 
2359 		if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
2360 			char *p;
2361 
2362 			if ((p = strchr(ctx->argv[i], '=')) == NULL) {
2363 				_pam_log(ctx, LOG_INFO,
2364 					 "no \"=\" delimiter for \"%s\" found\n",
2365 					 item);
2366 				goto out;
2367 			}
2368 			parm_opt = atoi(p + 1);
2369 			_pam_log_debug(ctx, LOG_INFO,
2370 				       "PAM config: %s '%d'\n",
2371 				       item, parm_opt);
2372 			return parm_opt;
2373 		}
2374 	}
2375 
2376 	if (ctx->dict) {
2377 		char *key = NULL;
2378 
2379 		key = talloc_asprintf(ctx, "global:%s", item);
2380 		if (!key) {
2381 			goto out;
2382 		}
2383 
2384 		parm_opt = tiniparser_getint(ctx->dict, key, -1);
2385 		TALLOC_FREE(key);
2386 
2387 		_pam_log_debug(ctx, LOG_INFO,
2388 			       "CONFIG file: %s '%d'\n",
2389 			       item, parm_opt);
2390 	}
2391 out:
2392 	return parm_opt;
2393 }
2394 
2395 static const char *get_krb5_cc_type_from_config(struct pwb_context *ctx)
2396 {
2397 	return get_conf_item_string(ctx, "krb5_ccache_type",
2398 				    WINBIND_KRB5_CCACHE_TYPE);
2399 }
2400 
2401 static const char *get_member_from_config(struct pwb_context *ctx)
2402 {
2403 	const char *ret = NULL;
2404 	ret = get_conf_item_string(ctx, "require_membership_of",
2405 				   WINBIND_REQUIRED_MEMBERSHIP);
2406 	if (ret != NULL) {
2407 		return ret;
2408 	}
2409 	return get_conf_item_string(ctx, "require-membership-of",
2410 				    WINBIND_REQUIRED_MEMBERSHIP);
2411 }
2412 
2413 static int get_warn_pwd_expire_from_config(struct pwb_context *ctx)
2414 {
2415 	int ret;
2416 	ret = get_config_item_int(ctx, "warn_pwd_expire",
2417 				  WINBIND_WARN_PWD_EXPIRE);
2418 	/* no or broken setting */
2419 	if (ret < 0) {
2420 		return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES;
2421 	}
2422 	return ret;
2423 }
2424 
2425 /**
2426  * Retrieve the winbind separator.
2427  *
2428  * @param ctx PAM winbind context.
2429  *
2430  * @return string separator character. NULL on failure.
2431  */
2432 
2433 static char winbind_get_separator(struct pwb_context *ctx)
2434 {
2435 	wbcErr wbc_status;
2436 	static struct wbcInterfaceDetails *details = NULL;
2437 
2438 	wbc_status = wbcCtxInterfaceDetails(ctx->wbc_ctx, &details);
2439 	if (!WBC_ERROR_IS_OK(wbc_status)) {
2440 		_pam_log(ctx, LOG_ERR,
2441 			 "Could not retrieve winbind interface details: %s",
2442 			 wbcErrorString(wbc_status));
2443 		return '\0';
2444 	}
2445 
2446 	if (!details) {
2447 		return '\0';
2448 	}
2449 
2450 	return details->winbind_separator;
2451 }
2452 
2453 
2454 /**
2455  * Convert a upn to a name.
2456  *
2457  * @param ctx PAM winbind context.
2458  * @param upn  User UPN to be translated.
2459  *
2460  * @return converted name. NULL pointer on failure. Caller needs to free.
2461  */
2462 
2463 static char* winbind_upn_to_username(struct pwb_context *ctx,
2464 				     const char *upn)
2465 {
2466 	char sep;
2467 	wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
2468 	struct wbcDomainSid sid;
2469 	enum wbcSidType type;
2470 	char *domain = NULL;
2471 	char *name;
2472 	char *p;
2473 	char *result;
2474 
2475 	/* This cannot work when the winbind separator = @ */
2476 
2477 	sep = winbind_get_separator(ctx);
2478 	if (!sep || sep == '@') {
2479 		return NULL;
2480 	}
2481 
2482 	name = talloc_strdup(ctx, upn);
2483 	if (!name) {
2484 		return NULL;
2485 	}
2486 
2487 	p = strchr(name, '@');
2488 	if (p == NULL) {
2489 		TALLOC_FREE(name);
2490 		return NULL;
2491 	}
2492 	*p = '\0';
2493 	domain = p + 1;
2494 
2495 	/* Convert the UPN to a SID */
2496 
2497 	wbc_status = wbcCtxLookupName(ctx->wbc_ctx, domain, name, &sid, &type);
2498 	if (!WBC_ERROR_IS_OK(wbc_status)) {
2499 		return NULL;
2500 	}
2501 
2502 	/* Convert the the SID back to the sAMAccountName */
2503 
2504 	wbc_status = wbcCtxLookupSid(ctx->wbc_ctx, &sid, &domain, &name, &type);
2505 	if (!WBC_ERROR_IS_OK(wbc_status)) {
2506 		return NULL;
2507 	}
2508 
2509 	result = talloc_asprintf(ctx, "%s%c%s", domain, sep, name);
2510 	wbcFreeMemory(domain);
2511 	wbcFreeMemory(name);
2512 	return result;
2513 }
2514 
2515 static int _pam_delete_cred(pam_handle_t *pamh, int flags,
2516 			    int argc, enum pam_winbind_request_type type,
2517 			    const char **argv)
2518 {
2519 	int retval = PAM_SUCCESS;
2520 	struct pwb_context *ctx = NULL;
2521 	struct wbcLogoffUserParams logoff;
2522 	struct wbcAuthErrorInfo *error = NULL;
2523 	const char *user;
2524 	wbcErr wbc_status = WBC_ERR_SUCCESS;
2525 
2526 	ZERO_STRUCT(logoff);
2527 
2528 	retval = _pam_winbind_init_context(pamh, flags, argc, argv, type, &ctx);
2529 	if (retval != PAM_SUCCESS) {
2530 		return retval;
2531 	}
2532 
2533 	_PAM_LOG_FUNCTION_ENTER("_pam_delete_cred", ctx);
2534 
2535 	if (ctx->ctrl & WINBIND_KRB5_AUTH) {
2536 
2537 		/* destroy the ccache here */
2538 
2539 		uint32_t wbc_flags = 0;
2540 		const char *ccname = NULL;
2541 		struct passwd *pwd = NULL;
2542 
2543 		retval = pam_get_user(pamh, &user, _("Username: "));
2544 		if (retval != PAM_SUCCESS) {
2545 			_pam_log(ctx, LOG_ERR,
2546 				 "could not identify user");
2547 			goto out;
2548 		}
2549 
2550 		if (user == NULL) {
2551 			_pam_log(ctx, LOG_ERR,
2552 				 "username was NULL!");
2553 			retval = PAM_USER_UNKNOWN;
2554 			goto out;
2555 		}
2556 
2557 		_pam_log_debug(ctx, LOG_DEBUG,
2558 			       "username [%s] obtained", user);
2559 
2560 		ccname = pam_getenv(pamh, "KRB5CCNAME");
2561 		if (ccname == NULL) {
2562 			_pam_log_debug(ctx, LOG_DEBUG,
2563 				       "user has no KRB5CCNAME environment");
2564 		}
2565 
2566 		pwd = getpwnam(user);
2567 		if (pwd == NULL) {
2568 			retval = PAM_USER_UNKNOWN;
2569 			goto out;
2570 		}
2571 
2572 		wbc_flags = WBFLAG_PAM_KRB5 |
2573 			WBFLAG_PAM_CONTACT_TRUSTDOM;
2574 
2575 		logoff.username		= user;
2576 
2577 		if (ccname) {
2578 			wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
2579 						     &logoff.blobs,
2580 						     "ccfilename",
2581 						     0,
2582 						     discard_const_p(uint8_t, ccname),
2583 						     strlen(ccname)+1);
2584 			if (!WBC_ERROR_IS_OK(wbc_status)) {
2585 				goto out;
2586 			}
2587 		}
2588 
2589 		wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
2590 					     &logoff.blobs,
2591 					     "flags",
2592 					     0,
2593 					     (uint8_t *)&wbc_flags,
2594 					     sizeof(wbc_flags));
2595 		if (!WBC_ERROR_IS_OK(wbc_status)) {
2596 			goto out;
2597 		}
2598 
2599 		wbc_status = wbcAddNamedBlob(&logoff.num_blobs,
2600 					     &logoff.blobs,
2601 					     "user_uid",
2602 					     0,
2603 					     (uint8_t *)&pwd->pw_uid,
2604 					     sizeof(pwd->pw_uid));
2605 		if (!WBC_ERROR_IS_OK(wbc_status)) {
2606 			goto out;
2607 		}
2608 
2609 		wbc_status = wbcCtxLogoffUserEx(ctx->wbc_ctx, &logoff, &error);
2610 		retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
2611 						     user, "wbcLogoffUser");
2612 		wbcFreeMemory(logoff.blobs);
2613 		logoff.blobs = NULL;
2614 
2615 		if (!WBC_ERROR_IS_OK(wbc_status)) {
2616 			_pam_log(ctx, LOG_INFO,
2617 				 "failed to logoff user %s: %s\n",
2618 					 user, wbcErrorString(wbc_status));
2619 		}
2620 	}
2621 
2622 out:
2623 	if (logoff.blobs) {
2624 		wbcFreeMemory(logoff.blobs);
2625 	}
2626 
2627 	if (!WBC_ERROR_IS_OK(wbc_status)) {
2628 		retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
2629 		     user, "wbcLogoffUser");
2630 	}
2631 	wbcFreeMemory(error);
2632 
2633 	/*
2634 	 * Delete the krb5 ccname variable from the PAM environment
2635 	 * if it was set by winbind.
2636 	 */
2637 	if ((ctx->ctrl & WINBIND_KRB5_AUTH) && pam_getenv(pamh, "KRB5CCNAME")) {
2638 		pam_putenv(pamh, "KRB5CCNAME");
2639 	}
2640 
2641 	_PAM_LOG_FUNCTION_LEAVE("_pam_delete_cred", ctx, retval);
2642 
2643 	TALLOC_FREE(ctx);
2644 
2645 	return retval;
2646 }
2647 
2648 PAM_EXTERN
2649 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
2650 			int argc, const char **argv)
2651 {
2652 	const char *username;
2653 	const char *password;
2654 	const char *member = NULL;
2655 	const char *cctype = NULL;
2656 	int warn_pwd_expire;
2657 	int retval = PAM_AUTH_ERR;
2658 	char *username_ret = NULL;
2659 	char *new_authtok_required = NULL;
2660 	char *real_username = NULL;
2661 	struct pwb_context *ctx = NULL;
2662 
2663 	retval = _pam_winbind_init_context(pamh, flags, argc, argv,
2664 					   PAM_WINBIND_AUTHENTICATE, &ctx);
2665 	if (retval != PAM_SUCCESS) {
2666 		return retval;
2667 	}
2668 
2669 	_PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", ctx);
2670 
2671 	/* Get the username */
2672 	retval = pam_get_user(pamh, &username, NULL);
2673 	if ((retval != PAM_SUCCESS) || (!username)) {
2674 		_pam_log_debug(ctx, LOG_DEBUG,
2675 			       "can not get the username");
2676 		retval = PAM_SERVICE_ERR;
2677 		goto out;
2678 	}
2679 
2680 
2681 #if defined(AIX)
2682 	/* Decode the user name since AIX does not support logn user
2683 	   names by default.  The name is encoded as _#uid.  */
2684 
2685 	if (username[0] == '_') {
2686 		uid_t id = atoi(&username[1]);
2687 		struct passwd *pw = NULL;
2688 
2689 		if ((id!=0) && ((pw = getpwuid(id)) != NULL)) {
2690 			real_username = strdup(pw->pw_name);
2691 		}
2692 	}
2693 #endif
2694 
2695 	if (!real_username) {
2696 		/* Just making a copy of the username we got from PAM */
2697 		if ((real_username = strdup(username)) == NULL) {
2698 			_pam_log_debug(ctx, LOG_DEBUG,
2699 				       "memory allocation failure when copying "
2700 				       "username");
2701 			retval = PAM_SERVICE_ERR;
2702 			goto out;
2703 		}
2704 	}
2705 
2706 	/* Maybe this was a UPN */
2707 
2708 	if (strchr(real_username, '@') != NULL) {
2709 		char *samaccountname = NULL;
2710 
2711 		samaccountname = winbind_upn_to_username(ctx,
2712 							 real_username);
2713 		if (samaccountname) {
2714 			free(real_username);
2715 			real_username = strdup(samaccountname);
2716 		}
2717 	}
2718 
2719 	retval = _winbind_read_password(ctx, ctx->ctrl, NULL,
2720 					_("Password: "), NULL,
2721 					&password);
2722 
2723 	if (retval != PAM_SUCCESS) {
2724 		_pam_log(ctx, LOG_ERR,
2725 			 "Could not retrieve user's password");
2726 		retval = PAM_AUTHTOK_ERR;
2727 		goto out;
2728 	}
2729 
2730 	/* Let's not give too much away in the log file */
2731 
2732 #ifdef DEBUG_PASSWORD
2733 	_pam_log_debug(ctx, LOG_INFO,
2734 		       "Verify user '%s' with password '%s'",
2735 		       real_username, password);
2736 #else
2737 	_pam_log_debug(ctx, LOG_INFO,
2738 		       "Verify user '%s'", real_username);
2739 #endif
2740 
2741 	member = get_member_from_config(ctx);
2742 	cctype = get_krb5_cc_type_from_config(ctx);
2743 	warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
2744 
2745 	/* Now use the username to look up password */
2746 	retval = winbind_auth_request(ctx, real_username, password,
2747 				      member, cctype, warn_pwd_expire,
2748 				      NULL, NULL, NULL, &username_ret);
2749 
2750 	if (retval == PAM_NEW_AUTHTOK_REQD ||
2751 	    retval == PAM_AUTHTOK_EXPIRED) {
2752 
2753 		char *new_authtok_required_during_auth = NULL;
2754 
2755 		new_authtok_required = talloc_asprintf(NULL, "%d", retval);
2756 		if (!new_authtok_required) {
2757 			retval = PAM_BUF_ERR;
2758 			goto out;
2759 		}
2760 
2761 		pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
2762 			     new_authtok_required,
2763 			     _pam_winbind_cleanup_func);
2764 
2765 		retval = PAM_SUCCESS;
2766 
2767 		new_authtok_required_during_auth = talloc_asprintf(NULL, "%d", true);
2768 		if (!new_authtok_required_during_auth) {
2769 			retval = PAM_BUF_ERR;
2770 			goto out;
2771 		}
2772 
2773 		pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
2774 			     new_authtok_required_during_auth,
2775 			     _pam_winbind_cleanup_func);
2776 
2777 		goto out;
2778 	}
2779 
2780 out:
2781 	if (username_ret) {
2782 		pam_set_item (pamh, PAM_USER, username_ret);
2783 		_pam_log_debug(ctx, LOG_INFO,
2784 			       "Returned user was '%s'", username_ret);
2785 		free(username_ret);
2786 	}
2787 
2788 	if (real_username) {
2789 		free(real_username);
2790 	}
2791 
2792 	if (!new_authtok_required) {
2793 		pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, NULL, NULL);
2794 	}
2795 
2796 	if (retval != PAM_SUCCESS) {
2797 		_pam_free_data_info3(pamh);
2798 	}
2799 
2800 	_PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval);
2801 
2802 	TALLOC_FREE(ctx);
2803 
2804 	return retval;
2805 }
2806 
2807 PAM_EXTERN
2808 int pam_sm_setcred(pam_handle_t *pamh, int flags,
2809 		   int argc, const char **argv)
2810 {
2811 	int ret = PAM_SYSTEM_ERR;
2812 	struct pwb_context *ctx = NULL;
2813 
2814 	ret = _pam_winbind_init_context(pamh, flags, argc, argv,
2815 					PAM_WINBIND_SETCRED, &ctx);
2816 	if (ret != PAM_SUCCESS) {
2817 		return ret;
2818 	}
2819 
2820 	_PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", ctx);
2821 
2822 	switch (flags & ~PAM_SILENT) {
2823 
2824 		case PAM_DELETE_CRED:
2825 			ret = _pam_delete_cred(pamh, flags, argc,
2826 					       PAM_WINBIND_SETCRED, argv);
2827 			break;
2828 		case PAM_REFRESH_CRED:
2829 			_pam_log_debug(ctx, LOG_WARNING,
2830 				       "PAM_REFRESH_CRED not implemented");
2831 			ret = PAM_SUCCESS;
2832 			break;
2833 		case PAM_REINITIALIZE_CRED:
2834 			_pam_log_debug(ctx, LOG_WARNING,
2835 				       "PAM_REINITIALIZE_CRED not implemented");
2836 			ret = PAM_SUCCESS;
2837 			break;
2838 		case PAM_ESTABLISH_CRED:
2839 			_pam_log_debug(ctx, LOG_WARNING,
2840 				       "PAM_ESTABLISH_CRED not implemented");
2841 			ret = PAM_SUCCESS;
2842 			break;
2843 		default:
2844 			ret = PAM_SYSTEM_ERR;
2845 			break;
2846 	}
2847 
2848 	_PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret);
2849 
2850 	TALLOC_FREE(ctx);
2851 
2852 	return ret;
2853 }
2854 
2855 /*
2856  * Account management. We want to verify that the account exists
2857  * before returning PAM_SUCCESS
2858  */
2859 PAM_EXTERN
2860 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
2861 		   int argc, const char **argv)
2862 {
2863 	const char *username;
2864 	int ret = PAM_USER_UNKNOWN;
2865 	const char *tmp = NULL;
2866 	struct pwb_context *ctx = NULL;
2867 
2868 	ret = _pam_winbind_init_context(pamh, flags, argc, argv,
2869 					PAM_WINBIND_ACCT_MGMT, &ctx);
2870 	if (ret != PAM_SUCCESS) {
2871 		return ret;
2872 	}
2873 
2874 	_PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", ctx);
2875 
2876 
2877 	/* Get the username */
2878 	ret = pam_get_user(pamh, &username, NULL);
2879 	if ((ret != PAM_SUCCESS) || (!username)) {
2880 		_pam_log_debug(ctx, LOG_DEBUG,
2881 			       "can not get the username");
2882 		ret = PAM_SERVICE_ERR;
2883 		goto out;
2884 	}
2885 
2886 	/* Verify the username */
2887 	ret = valid_user(ctx, username);
2888 	switch (ret) {
2889 	case -1:
2890 		/* some sort of system error. The log was already printed */
2891 		ret = PAM_SERVICE_ERR;
2892 		goto out;
2893 	case 1:
2894 		/* the user does not exist */
2895 		_pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
2896 			       username);
2897 		if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
2898 			ret = PAM_IGNORE;
2899 			goto out;
2900 		}
2901 		ret = PAM_USER_UNKNOWN;
2902 		goto out;
2903 	case 0:
2904 		pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
2905 			     (const void **)&tmp);
2906 		if (tmp != NULL) {
2907 			ret = atoi(tmp);
2908 			switch (ret) {
2909 			case PAM_AUTHTOK_EXPIRED:
2910 				/* Since new token is required in this case */
2911 				FALL_THROUGH;
2912 			case PAM_NEW_AUTHTOK_REQD:
2913 				_pam_log(ctx, LOG_WARNING,
2914 					 "pam_sm_acct_mgmt success but %s is set",
2915 					 PAM_WINBIND_NEW_AUTHTOK_REQD);
2916 				_pam_log(ctx, LOG_NOTICE,
2917 					 "user '%s' needs new password",
2918 					 username);
2919 				/* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
2920 				ret = PAM_NEW_AUTHTOK_REQD;
2921 				goto out;
2922 			default:
2923 				_pam_log(ctx, LOG_WARNING,
2924 					 "pam_sm_acct_mgmt success");
2925 				_pam_log(ctx, LOG_NOTICE,
2926 					 "user '%s' granted access", username);
2927 				ret = PAM_SUCCESS;
2928 				goto out;
2929 			}
2930 		}
2931 
2932 		/* Otherwise, the authentication looked good */
2933 		_pam_log(ctx, LOG_NOTICE,
2934 			 "user '%s' granted access", username);
2935 		ret = PAM_SUCCESS;
2936 		goto out;
2937 	default:
2938 		/* we don't know anything about this return value */
2939 		_pam_log(ctx, LOG_ERR,
2940 			 "internal module error (ret = %d, user = '%s')",
2941 			 ret, username);
2942 		ret = PAM_SERVICE_ERR;
2943 		goto out;
2944 	}
2945 
2946 	/* should not be reached */
2947 	ret = PAM_IGNORE;
2948 
2949  out:
2950 
2951 	_PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx, ret);
2952 
2953 	TALLOC_FREE(ctx);
2954 
2955 	return ret;
2956 }
2957 
2958 PAM_EXTERN
2959 int pam_sm_open_session(pam_handle_t *pamh, int flags,
2960 			int argc, const char **argv)
2961 {
2962 	int ret = PAM_SUCCESS;
2963 	struct pwb_context *ctx = NULL;
2964 
2965 	ret = _pam_winbind_init_context(pamh, flags, argc, argv,
2966 					PAM_WINBIND_OPEN_SESSION, &ctx);
2967 	if (ret != PAM_SUCCESS) {
2968 		return ret;
2969 	}
2970 
2971 	_PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx);
2972 
2973 	if (ctx->ctrl & WINBIND_MKHOMEDIR) {
2974 		/* check and create homedir */
2975 		ret = _pam_mkhomedir(ctx);
2976 	}
2977 
2978 	_PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret);
2979 
2980 	TALLOC_FREE(ctx);
2981 
2982 	return ret;
2983 }
2984 
2985 PAM_EXTERN
2986 int pam_sm_close_session(pam_handle_t *pamh, int flags,
2987 			 int argc, const char **argv)
2988 {
2989 	int ret = PAM_SUCCESS;
2990 	struct pwb_context *ctx = NULL;
2991 
2992 	ret = _pam_winbind_init_context(pamh, flags, argc, argv,
2993 					PAM_WINBIND_CLOSE_SESSION, &ctx);
2994 	if (ret != PAM_SUCCESS) {
2995 		return ret;
2996 	}
2997 
2998 	_PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", ctx);
2999 
3000 	_PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, ret);
3001 
3002 	TALLOC_FREE(ctx);
3003 
3004 	return ret;
3005 }
3006 
3007 /**
3008  * evaluate whether we need to re-authenticate with kerberos after a
3009  * password change
3010  *
3011  * @param ctx PAM winbind context.
3012  * @param user The username
3013  *
3014  * @return boolean Returns true if required, false if not.
3015  */
3016 
3017 static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx,
3018 						   const char *user)
3019 {
3020 
3021 	/* Make sure that we only do this if a) the chauthtok got initiated
3022 	 * during a logon attempt (authenticate->acct_mgmt->chauthtok) b) any
3023 	 * later password change via the "passwd" command if done by the user
3024 	 * itself
3025 	 * NB. If we login from gdm or xdm and the password expires,
3026 	 * we change the password, but there is no memory cache.
3027 	 * Thus, even for passthrough login, we should do the
3028 	 * authentication again to update memory cache.
3029 	 * --- BoYang
3030 	 * */
3031 
3032 	const char *new_authtok_reqd_during_auth = NULL;
3033 	struct passwd *pwd = NULL;
3034 
3035 	pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
3036 		      (const void **) &new_authtok_reqd_during_auth);
3037 	pam_set_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
3038 		     NULL, NULL);
3039 
3040 	if (new_authtok_reqd_during_auth) {
3041 		return true;
3042 	}
3043 
3044 	pwd = getpwnam(user);
3045 	if (!pwd) {
3046 		return false;
3047 	}
3048 
3049 	if (getuid() == pwd->pw_uid) {
3050 		return true;
3051 	}
3052 
3053 	return false;
3054 }
3055 
3056 
3057 PAM_EXTERN
3058 int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
3059 		     int argc, const char **argv)
3060 {
3061 	unsigned int lctrl;
3062 	int ret;
3063 	bool cached_login = false;
3064 
3065 	/* <DO NOT free() THESE> */
3066 	const char *user;
3067 	const char *pass_old;
3068 	const char *pass_new;
3069 	/* </DO NOT free() THESE> */
3070 
3071 	char *Announce;
3072 
3073 	int retry = 0;
3074 	char *username_ret = NULL;
3075 	struct wbcAuthErrorInfo *error = NULL;
3076 	struct pwb_context *ctx = NULL;
3077 
3078 	ret = _pam_winbind_init_context(pamh, flags, argc, argv,
3079 					PAM_WINBIND_CHAUTHTOK, &ctx);
3080 	if (ret != PAM_SUCCESS) {
3081 		return ret;
3082 	}
3083 
3084 	_PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", ctx);
3085 
3086 	cached_login = (ctx->ctrl & WINBIND_CACHED_LOGIN);
3087 
3088 	/* clearing offline bit for auth */
3089 	ctx->ctrl &= ~WINBIND_CACHED_LOGIN;
3090 
3091 	/*
3092 	 * First get the name of a user
3093 	 */
3094 	ret = pam_get_user(pamh, &user, _("Username: "));
3095 	if (ret != PAM_SUCCESS) {
3096 		_pam_log(ctx, LOG_ERR,
3097 			 "password - could not identify user");
3098 		goto out;
3099 	}
3100 
3101 	if (user == NULL) {
3102 		_pam_log(ctx, LOG_ERR, "username was NULL!");
3103 		ret = PAM_USER_UNKNOWN;
3104 		goto out;
3105 	}
3106 
3107 	_pam_log_debug(ctx, LOG_DEBUG, "username [%s] obtained", user);
3108 
3109 	/* check if this is really a user in winbindd, not only in NSS */
3110 	ret = valid_user(ctx, user);
3111 	switch (ret) {
3112 		case 1:
3113 			ret = PAM_USER_UNKNOWN;
3114 			goto out;
3115 		case -1:
3116 			ret = PAM_SYSTEM_ERR;
3117 			goto out;
3118 		default:
3119 			break;
3120 	}
3121 
3122 	/*
3123 	 * obtain and verify the current password (OLDAUTHTOK) for
3124 	 * the user.
3125 	 */
3126 
3127 	if (flags & PAM_PRELIM_CHECK) {
3128 		time_t pwdlastset_prelim = 0;
3129 
3130 		/* instruct user what is happening */
3131 
3132 #define greeting _("Changing password for")
3133 		Announce = talloc_asprintf(ctx, "%s %s", greeting, user);
3134 		if (!Announce) {
3135 			_pam_log(ctx, LOG_CRIT,
3136 				 "password - out of memory");
3137 			ret = PAM_BUF_ERR;
3138 			goto out;
3139 		}
3140 #undef greeting
3141 
3142 		lctrl = ctx->ctrl | WINBIND__OLD_PASSWORD;
3143 		ret = _winbind_read_password(ctx, lctrl,
3144 						Announce,
3145 						_("(current) NT password: "),
3146 						NULL,
3147 						(const char **) &pass_old);
3148 		TALLOC_FREE(Announce);
3149 		if (ret != PAM_SUCCESS) {
3150 			_pam_log(ctx, LOG_NOTICE,
3151 				 "password - (old) token not obtained");
3152 			goto out;
3153 		}
3154 
3155 		/* verify that this is the password for this user */
3156 
3157 		ret = winbind_auth_request(ctx, user, pass_old,
3158 					   NULL, NULL, 0,
3159 					   &error, NULL,
3160 					   &pwdlastset_prelim, NULL);
3161 
3162 		if (ret != PAM_ACCT_EXPIRED &&
3163 		    ret != PAM_AUTHTOK_EXPIRED &&
3164 		    ret != PAM_NEW_AUTHTOK_REQD &&
3165 		    ret != PAM_SUCCESS) {
3166 			pass_old = NULL;
3167 			goto out;
3168 		}
3169 
3170 		pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET,
3171 			     (void *)pwdlastset_prelim, NULL);
3172 
3173 		ret = pam_set_item(pamh, PAM_OLDAUTHTOK,
3174 				   (const void *) pass_old);
3175 		pass_old = NULL;
3176 		if (ret != PAM_SUCCESS) {
3177 			_pam_log(ctx, LOG_CRIT,
3178 				 "failed to set PAM_OLDAUTHTOK");
3179 		}
3180 	} else if (flags & PAM_UPDATE_AUTHTOK) {
3181 
3182 		time_t pwdlastset_update = 0;
3183 
3184 		/*
3185 		 * obtain the proposed password
3186 		 */
3187 
3188 		/*
3189 		 * get the old token back.
3190 		 */
3191 
3192 		ret = pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **) &pass_old);
3193 
3194 		if (ret != PAM_SUCCESS) {
3195 			_pam_log(ctx, LOG_NOTICE,
3196 				 "user not authenticated");
3197 			goto out;
3198 		}
3199 
3200 		lctrl = ctx->ctrl & ~WINBIND_TRY_FIRST_PASS_ARG;
3201 
3202 		if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
3203 			lctrl |= WINBIND_USE_FIRST_PASS_ARG;
3204 		}
3205 		if (on(WINBIND_TRY_AUTHTOK_ARG, lctrl)) {
3206 			lctrl |= WINBIND_TRY_FIRST_PASS_ARG;
3207 		}
3208 		retry = 0;
3209 		ret = PAM_AUTHTOK_ERR;
3210 		while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
3211 			/*
3212 			 * use_authtok is to force the use of a previously entered
3213 			 * password -- needed for pluggable password strength checking
3214 			 */
3215 
3216 			ret = _winbind_read_password(ctx, lctrl,
3217 						     NULL,
3218 						     _("Enter new NT password: "),
3219 						     _("Retype new NT password: "),
3220 						     (const char **)&pass_new);
3221 
3222 			if (ret != PAM_SUCCESS) {
3223 				_pam_log_debug(ctx, LOG_ALERT,
3224 					       "password - "
3225 					       "new password not obtained");
3226 				pass_old = NULL;/* tidy up */
3227 				goto out;
3228 			}
3229 
3230 			/*
3231 			 * At this point we know who the user is and what they
3232 			 * propose as their new password. Verify that the new
3233 			 * password is acceptable.
3234 			 */
3235 
3236 			if (pass_new[0] == '\0') {/* "\0" password = NULL */
3237 				pass_new = NULL;
3238 			}
3239 		}
3240 
3241 		/*
3242 		 * By reaching here we have approved the passwords and must now
3243 		 * rebuild the password database file.
3244 		 */
3245 		pam_get_data(pamh, PAM_WINBIND_PWD_LAST_SET,
3246 			     (const void **) &pwdlastset_update);
3247 
3248 		/*
3249 		 * if cached creds were enabled, make sure to set the
3250 		 * WINBIND_CACHED_LOGIN bit here in order to have winbindd
3251 		 * update the cached creds storage - gd
3252 		 */
3253 		if (cached_login) {
3254 			ctx->ctrl |= WINBIND_CACHED_LOGIN;
3255 		}
3256 
3257 		ret = winbind_chauthtok_request(ctx, user, pass_old,
3258 						pass_new, pwdlastset_update);
3259 		if (ret != PAM_SUCCESS) {
3260 			pass_old = pass_new = NULL;
3261 			goto out;
3262 		}
3263 
3264 		if (_pam_require_krb5_auth_after_chauthtok(ctx, user)) {
3265 
3266 			const char *member = NULL;
3267 			const char *cctype = NULL;
3268 			int warn_pwd_expire;
3269 			struct wbcLogonUserInfo *info = NULL;
3270 
3271 			member = get_member_from_config(ctx);
3272 			cctype = get_krb5_cc_type_from_config(ctx);
3273 			warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
3274 
3275 			/* Keep WINBIND_CACHED_LOGIN bit for
3276 			 * authentication after changing the password.
3277 			 * This will update the cached credentials in case
3278 			 * that winbindd_dual_pam_chauthtok() fails
3279 			 * to update them.
3280 			 * --- BoYang
3281 			 * */
3282 
3283 			ret = winbind_auth_request(ctx, user, pass_new,
3284 						   member, cctype, 0,
3285 						   &error, &info,
3286 						   NULL, &username_ret);
3287 			pass_old = pass_new = NULL;
3288 
3289 			if (ret == PAM_SUCCESS) {
3290 
3291 				struct wbcAuthUserInfo *user_info = NULL;
3292 
3293 				if (info && info->info) {
3294 					user_info = info->info;
3295 				}
3296 
3297 				/* warn a user if the password is about to
3298 				 * expire soon */
3299 				_pam_warn_password_expiry(ctx, user_info,
3300 							  warn_pwd_expire,
3301 							  NULL, NULL);
3302 
3303 				/* set some info3 info for other modules in the
3304 				 * stack */
3305 				_pam_set_data_info3(ctx, user_info);
3306 
3307 				/* put krb5ccname into env */
3308 				_pam_setup_krb5_env(ctx, info);
3309 
3310 				if (username_ret) {
3311 					pam_set_item(pamh, PAM_USER,
3312 						     username_ret);
3313 					_pam_log_debug(ctx, LOG_INFO,
3314 						       "Returned user was '%s'",
3315 						       username_ret);
3316 					free(username_ret);
3317 				}
3318 
3319 			}
3320 
3321 			if (info && info->blobs) {
3322 				wbcFreeMemory(info->blobs);
3323 			}
3324 			wbcFreeMemory(info);
3325 
3326 			goto out;
3327 		}
3328 	} else {
3329 		ret = PAM_SERVICE_ERR;
3330 	}
3331 
3332 out:
3333 	{
3334 		/* Deal with offline errors. */
3335 		int i;
3336 		const char *codes[] = {
3337 			"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
3338 			"NT_STATUS_NO_LOGON_SERVERS",
3339 			"NT_STATUS_ACCESS_DENIED"
3340 		};
3341 
3342 		for (i=0; i<ARRAY_SIZE(codes); i++) {
3343 			int _ret;
3344 			if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) {
3345 				break;
3346 			}
3347 		}
3348 	}
3349 
3350 	wbcFreeMemory(error);
3351 
3352 	_PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx, ret);
3353 
3354 	TALLOC_FREE(ctx);
3355 
3356 	return ret;
3357 }
3358 
3359 #ifdef PAM_STATIC
3360 
3361 /* static module data */
3362 
3363 struct pam_module _pam_winbind_modstruct = {
3364 	MODULE_NAME,
3365 	pam_sm_authenticate,
3366 	pam_sm_setcred,
3367 	pam_sm_acct_mgmt,
3368 	pam_sm_open_session,
3369 	pam_sm_close_session,
3370 	pam_sm_chauthtok
3371 };
3372 
3373 #endif
3374 
3375 /*
3376  * Copyright (c) Andrew Tridgell  <tridge@samba.org>   2000
3377  * Copyright (c) Tim Potter       <tpot@samba.org>     2000
3378  * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
3379  * Copyright (c) Guenther Deschner <gd@samba.org>      2005-2008
3380  * Copyright (c) Jan Rêkorajski 1999.
3381  * Copyright (c) Andrew G. Morgan 1996-8.
3382  * Copyright (c) Alex O. Yuriev, 1996.
3383  * Copyright (c) Cristian Gafton 1996.
3384  * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
3385  *
3386  * Redistribution and use in source and binary forms, with or without
3387  * modification, are permitted provided that the following conditions
3388  * are met:
3389  * 1. Redistributions of source code must retain the above copyright
3390  *    notice, and the entire permission notice in its entirety,
3391  *    including the disclaimer of warranties.
3392  * 2. Redistributions in binary form must reproduce the above copyright
3393  *    notice, this list of conditions and the following disclaimer in the
3394  *    documentation and/or other materials provided with the distribution.
3395  * 3. The name of the author may not be used to endorse or promote
3396  *    products derived from this software without specific prior
3397  *    written permission.
3398  *
3399  * ALTERNATIVELY, this product may be distributed under the terms of
3400  * the GNU Public License, in which case the provisions of the GPL are
3401  * required INSTEAD OF the above restrictions.  (This clause is
3402  * necessary due to a potential bad interaction between the GPL and
3403  * the restrictions contained in a BSD-style copyright.)
3404  *
3405  * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
3406  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
3407  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
3408  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
3409  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3410  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
3411  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3412  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3413  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3414  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
3415  * OF THE POSSIBILITY OF SUCH DAMAGE.
3416  */
3417