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
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)
13 #include "pam_winbind.h"
15 static int wbc_error_to_pam_error(wbcErr status)
20 case WBC_ERR_NOT_IMPLEMENTED:
21 return PAM_SERVICE_ERR;
22 case WBC_ERR_UNKNOWN_FAILURE:
24 case WBC_ERR_NO_MEMORY:
26 case WBC_ERR_INVALID_SID:
27 case WBC_ERR_INVALID_PARAM:
29 case WBC_ERR_WINBIND_NOT_AVAILABLE:
30 return PAM_AUTHINFO_UNAVAIL;
31 case WBC_ERR_DOMAIN_NOT_FOUND:
32 return PAM_AUTHINFO_UNAVAIL;
33 case WBC_ERR_INVALID_RESPONSE:
35 case WBC_ERR_NSS_ERROR:
36 return PAM_USER_UNKNOWN;
37 case WBC_ERR_AUTH_ERROR:
39 case WBC_ERR_UNKNOWN_USER:
40 return PAM_USER_UNKNOWN;
41 case WBC_ERR_UNKNOWN_GROUP:
42 return PAM_USER_UNKNOWN;
43 case WBC_ERR_PWD_CHANGE_FAILED:
51 static const char *_pam_error_code_str(int err)
57 return "PAM_OPEN_ERR";
59 return "PAM_SYMBOL_ERR";
61 return "PAM_SERVICE_ERR";
63 return "PAM_SYSTEM_ERR";
67 return "PAM_PERM_DENIED";
69 return "PAM_AUTH_ERR";
70 case PAM_CRED_INSUFFICIENT:
71 return "PAM_CRED_INSUFFICIENT";
72 case PAM_AUTHINFO_UNAVAIL:
73 return "PAM_AUTHINFO_UNAVAIL";
74 case PAM_USER_UNKNOWN:
75 return "PAM_USER_UNKNOWN";
77 return "PAM_MAXTRIES";
78 case PAM_NEW_AUTHTOK_REQD:
79 return "PAM_NEW_AUTHTOK_REQD";
80 case PAM_ACCT_EXPIRED:
81 return "PAM_ACCT_EXPIRED";
83 return "PAM_SESSION_ERR";
84 case PAM_CRED_UNAVAIL:
85 return "PAM_CRED_UNAVAIL";
86 case PAM_CRED_EXPIRED:
87 return "PAM_CRED_EXPIRED";
89 return "PAM_CRED_ERR";
90 case PAM_NO_MODULE_DATA:
91 return "PAM_NO_MODULE_DATA";
93 return "PAM_CONV_ERR";
95 return "PAM_AUTHTOK_ERR";
96 case PAM_AUTHTOK_RECOVERY_ERR:
97 return "PAM_AUTHTOK_RECOVERY_ERR";
98 case PAM_AUTHTOK_LOCK_BUSY:
99 return "PAM_AUTHTOK_LOCK_BUSY";
100 case PAM_AUTHTOK_DISABLE_AGING:
101 return "PAM_AUTHTOK_DISABLE_AGING";
103 return "PAM_TRY_AGAIN";
108 case PAM_AUTHTOK_EXPIRED:
109 return "PAM_AUTHTOK_EXPIRED";
110 #ifdef PAM_MODULE_UNKNOWN
111 case PAM_MODULE_UNKNOWN:
112 return "PAM_MODULE_UNKNOWN";
116 return "PAM_BAD_ITEM";
118 #ifdef PAM_CONV_AGAIN
120 return "PAM_CONV_AGAIN";
122 #ifdef PAM_INCOMPLETE
124 return "PAM_INCOMPLETE";
131 #define _PAM_LOG_FUNCTION_ENTER(function, ctx) \
133 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] ENTER: " \
134 function " (flags: 0x%04x)", ctx->pamh, ctx->flags); \
135 _pam_log_state(ctx); \
138 #define _PAM_LOG_FUNCTION_LEAVE(function, ctx, retval) \
140 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] LEAVE: " \
141 function " returning %d (%s)", ctx->pamh, retval, \
142 _pam_error_code_str(retval)); \
143 _pam_log_state(ctx); \
148 #define MAX_PASSWD_TRIES 3
151 static char initialized = 0;
153 static inline void textdomain_init(void);
154 static inline void textdomain_init(void)
157 bindtextdomain(MODULE_NAME, dyn_LOCALEDIR);
166 * Work around the pam API that has functions with void ** as parameters
167 * These lead to strict aliasing warnings with gcc.
169 static int _pam_get_item(const pam_handle_t *pamh,
173 const void **item = (const void **)_item;
174 return pam_get_item(pamh, item_type, item);
176 static int _pam_get_data(const pam_handle_t *pamh,
177 const char *module_data_name,
180 const void **data = (const void **)_data;
181 return pam_get_data(pamh, module_data_name, data);
184 /* some syslogging */
186 #ifdef HAVE_PAM_VSYSLOG
187 static void _pam_log_int(const pam_handle_t *pamh,
192 pam_vsyslog(pamh, err, format, args);
195 static void _pam_log_int(const pam_handle_t *pamh,
200 char *format2 = NULL;
203 _pam_get_item(pamh, PAM_SERVICE, &service);
205 format2 = (char *)malloc(strlen(MODULE_NAME)+strlen(format)+strlen(service)+5);
206 if (format2 == NULL) {
207 /* what else todo ? */
208 vsyslog(err, format, args);
212 sprintf(format2, "%s(%s): %s", MODULE_NAME, service, format);
213 vsyslog(err, format2, args);
216 #endif /* HAVE_PAM_VSYSLOG */
218 static bool _pam_log_is_silent(int ctrl)
220 return on(ctrl, WINBIND_SILENT);
223 static void _pam_log(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
224 static void _pam_log(struct pwb_context *r, int err, const char *format, ...)
228 if (_pam_log_is_silent(r->ctrl)) {
232 va_start(args, format);
233 _pam_log_int(r->pamh, err, format, args);
236 static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
237 static void __pam_log(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
241 if (_pam_log_is_silent(ctrl)) {
245 va_start(args, format);
246 _pam_log_int(pamh, err, format, args);
250 static bool _pam_log_is_debug_enabled(int ctrl)
256 if (_pam_log_is_silent(ctrl)) {
260 if (!(ctrl & WINBIND_DEBUG_ARG)) {
267 static bool _pam_log_is_debug_state_enabled(int ctrl)
269 if (!(ctrl & WINBIND_DEBUG_STATE)) {
273 return _pam_log_is_debug_enabled(ctrl);
276 static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
277 static void _pam_log_debug(struct pwb_context *r, int err, const char *format, ...)
281 if (!_pam_log_is_debug_enabled(r->ctrl)) {
285 va_start(args, format);
286 _pam_log_int(r->pamh, err, format, args);
289 static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...) PRINTF_ATTRIBUTE(4,5);
290 static void __pam_log_debug(const pam_handle_t *pamh, int ctrl, int err, const char *format, ...)
294 if (!_pam_log_is_debug_enabled(ctrl)) {
298 va_start(args, format);
299 _pam_log_int(pamh, err, format, args);
303 static void _pam_log_state_datum(struct pwb_context *ctx,
308 const void *data = NULL;
309 if (item_type != 0) {
310 pam_get_item(ctx->pamh, item_type, &data);
312 pam_get_data(ctx->pamh, key, &data);
315 const char *type = (item_type != 0) ? "ITEM" : "DATA";
316 if (is_string != 0) {
317 _pam_log_debug(ctx, LOG_DEBUG,
318 "[pamh: %p] STATE: %s(%s) = \"%s\" (%p)",
319 ctx->pamh, type, key, (const char *)data,
322 _pam_log_debug(ctx, LOG_DEBUG,
323 "[pamh: %p] STATE: %s(%s) = %p",
324 ctx->pamh, type, key, data);
329 #define _PAM_LOG_STATE_DATA_POINTER(ctx, module_data_name) \
330 _pam_log_state_datum(ctx, 0, module_data_name, 0)
332 #define _PAM_LOG_STATE_DATA_STRING(ctx, module_data_name) \
333 _pam_log_state_datum(ctx, 0, module_data_name, 1)
335 #define _PAM_LOG_STATE_ITEM_POINTER(ctx, item_type) \
336 _pam_log_state_datum(ctx, item_type, #item_type, 0)
338 #define _PAM_LOG_STATE_ITEM_STRING(ctx, item_type) \
339 _pam_log_state_datum(ctx, item_type, #item_type, 1)
341 #ifdef DEBUG_PASSWORD
342 #define _LOG_PASSWORD_AS_STRING 1
344 #define _LOG_PASSWORD_AS_STRING 0
347 #define _PAM_LOG_STATE_ITEM_PASSWORD(ctx, item_type) \
348 _pam_log_state_datum(ctx, item_type, #item_type, \
349 _LOG_PASSWORD_AS_STRING)
351 static void _pam_log_state(struct pwb_context *ctx)
353 if (!_pam_log_is_debug_state_enabled(ctx->ctrl)) {
357 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_SERVICE);
358 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER);
359 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_TTY);
360 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RHOST);
361 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_RUSER);
362 _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_OLDAUTHTOK);
363 _PAM_LOG_STATE_ITEM_PASSWORD(ctx, PAM_AUTHTOK);
364 _PAM_LOG_STATE_ITEM_STRING(ctx, PAM_USER_PROMPT);
365 _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_CONV);
366 #ifdef PAM_FAIL_DELAY
367 _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_FAIL_DELAY);
369 #ifdef PAM_REPOSITORY
370 _PAM_LOG_STATE_ITEM_POINTER(ctx, PAM_REPOSITORY);
373 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_HOMEDIR);
374 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSCRIPT);
375 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_LOGONSERVER);
376 _PAM_LOG_STATE_DATA_STRING(ctx, PAM_WINBIND_PROFILEPATH);
377 _PAM_LOG_STATE_DATA_STRING(ctx,
378 PAM_WINBIND_NEW_AUTHTOK_REQD);
379 /* Use atoi to get PAM result code */
380 _PAM_LOG_STATE_DATA_STRING(ctx,
381 PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH);
382 _PAM_LOG_STATE_DATA_POINTER(ctx, PAM_WINBIND_PWD_LAST_SET);
385 static int _pam_parse(const pam_handle_t *pamh,
389 dictionary **result_d)
392 const char *config_file = NULL;
395 dictionary *d = NULL;
397 if (flags & PAM_SILENT) {
398 ctrl |= WINBIND_SILENT;
401 for (i=argc,v=argv; i-- > 0; ++v) {
402 if (!strncasecmp(*v, "config", strlen("config"))) {
403 ctrl |= WINBIND_CONFIG_FILE;
409 if (config_file == NULL) {
410 config_file = PAM_WINBIND_CONFIG_FILE;
413 d = iniparser_load(config_file);
415 goto config_from_pam;
418 if (iniparser_getboolean(d, "global:debug", false)) {
419 ctrl |= WINBIND_DEBUG_ARG;
422 if (iniparser_getboolean(d, "global:debug_state", false)) {
423 ctrl |= WINBIND_DEBUG_STATE;
426 if (iniparser_getboolean(d, "global:cached_login", false)) {
427 ctrl |= WINBIND_CACHED_LOGIN;
430 if (iniparser_getboolean(d, "global:krb5_auth", false)) {
431 ctrl |= WINBIND_KRB5_AUTH;
434 if (iniparser_getboolean(d, "global:silent", false)) {
435 ctrl |= WINBIND_SILENT;
438 if (iniparser_getstr(d, "global:krb5_ccache_type") != NULL) {
439 ctrl |= WINBIND_KRB5_CCACHE_TYPE;
442 if ((iniparser_getstr(d, "global:require-membership-of") != NULL) ||
443 (iniparser_getstr(d, "global:require_membership_of") != NULL)) {
444 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
447 if (iniparser_getboolean(d, "global:try_first_pass", false)) {
448 ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
451 if (iniparser_getint(d, "global:warn_pwd_expire", 0)) {
452 ctrl |= WINBIND_WARN_PWD_EXPIRE;
456 /* step through arguments */
457 for (i=argc,v=argv; i-- > 0; ++v) {
459 /* generic options */
460 if (!strcmp(*v,"debug"))
461 ctrl |= WINBIND_DEBUG_ARG;
462 else if (!strcasecmp(*v, "debug_state"))
463 ctrl |= WINBIND_DEBUG_STATE;
464 else if (!strcasecmp(*v, "silent"))
465 ctrl |= WINBIND_SILENT;
466 else if (!strcasecmp(*v, "use_authtok"))
467 ctrl |= WINBIND_USE_AUTHTOK_ARG;
468 else if (!strcasecmp(*v, "use_first_pass"))
469 ctrl |= WINBIND_USE_FIRST_PASS_ARG;
470 else if (!strcasecmp(*v, "try_first_pass"))
471 ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
472 else if (!strcasecmp(*v, "unknown_ok"))
473 ctrl |= WINBIND_UNKNOWN_OK_ARG;
474 else if (!strncasecmp(*v, "require_membership_of",
475 strlen("require_membership_of")))
476 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
477 else if (!strncasecmp(*v, "require-membership-of",
478 strlen("require-membership-of")))
479 ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
480 else if (!strcasecmp(*v, "krb5_auth"))
481 ctrl |= WINBIND_KRB5_AUTH;
482 else if (!strncasecmp(*v, "krb5_ccache_type",
483 strlen("krb5_ccache_type")))
484 ctrl |= WINBIND_KRB5_CCACHE_TYPE;
485 else if (!strcasecmp(*v, "cached_login"))
486 ctrl |= WINBIND_CACHED_LOGIN;
488 __pam_log(pamh, ctrl, LOG_ERR,
489 "pam_parse: unknown option: %s", *v);
499 iniparser_freedict(d);
506 static int _pam_winbind_free_context(struct pwb_context *ctx)
513 iniparser_freedict(ctx->dict);
519 static int _pam_winbind_init_context(pam_handle_t *pamh,
523 struct pwb_context **ctx_p)
525 struct pwb_context *r = NULL;
531 r = TALLOC_ZERO_P(NULL, struct pwb_context);
536 talloc_set_destructor(r, _pam_winbind_free_context);
542 r->ctrl = _pam_parse(pamh, flags, argc, argv, &r->dict);
545 return PAM_SYSTEM_ERR;
553 static void _pam_winbind_cleanup_func(pam_handle_t *pamh,
557 int ctrl = _pam_parse(pamh, 0, 0, NULL, NULL);
558 if (_pam_log_is_debug_state_enabled(ctrl)) {
559 __pam_log_debug(pamh, ctrl, LOG_DEBUG,
560 "[pamh: %p] CLEAN: cleaning up PAM data %p "
561 "(error_status = %d)", pamh, data,
568 static const struct ntstatus_errors {
569 const char *ntstatus_string;
570 const char *error_string;
571 } ntstatus_errors[] = {
574 {"NT_STATUS_BACKUP_CONTROLLER",
575 N_("No primary Domain Controler available")},
576 {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
577 N_("No domain controllers found")},
578 {"NT_STATUS_NO_LOGON_SERVERS",
579 N_("No logon servers")},
580 {"NT_STATUS_PWD_TOO_SHORT",
581 N_("Password too short")},
582 {"NT_STATUS_PWD_TOO_RECENT",
583 N_("The password of this user is too recent to change")},
584 {"NT_STATUS_PWD_HISTORY_CONFLICT",
585 N_("Password is already in password history")},
586 {"NT_STATUS_PASSWORD_EXPIRED",
587 N_("Your password has expired")},
588 {"NT_STATUS_PASSWORD_MUST_CHANGE",
589 N_("You need to change your password now")},
590 {"NT_STATUS_INVALID_WORKSTATION",
591 N_("You are not allowed to logon from this workstation")},
592 {"NT_STATUS_INVALID_LOGON_HOURS",
593 N_("You are not allowed to logon at this time")},
594 {"NT_STATUS_ACCOUNT_EXPIRED",
595 N_("Your account has expired. "
596 "Please contact your System administrator")}, /* SCNR */
597 {"NT_STATUS_ACCOUNT_DISABLED",
598 N_("Your account is disabled. "
599 "Please contact your System administrator")}, /* SCNR */
600 {"NT_STATUS_ACCOUNT_LOCKED_OUT",
601 N_("Your account has been locked. "
602 "Please contact your System administrator")}, /* SCNR */
603 {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
604 N_("Invalid Trust Account")},
605 {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
606 N_("Invalid Trust Account")},
607 {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
608 N_("Invalid Trust Account")},
609 {"NT_STATUS_ACCESS_DENIED",
610 N_("Access is denied")},
614 static const char *_get_ntstatus_error_string(const char *nt_status_string)
617 for (i=0; ntstatus_errors[i].ntstatus_string != NULL; i++) {
618 if (!strcasecmp(ntstatus_errors[i].ntstatus_string,
620 return _(ntstatus_errors[i].error_string);
626 /* --- authentication management functions --- */
628 /* Attempt a conversation */
630 static int converse(const pam_handle_t *pamh,
632 struct pam_message **message,
633 struct pam_response **response)
636 struct pam_conv *conv;
638 retval = _pam_get_item(pamh, PAM_CONV, &conv);
639 if (retval == PAM_SUCCESS) {
640 retval = conv->conv(nargs,
641 (const struct pam_message **)message,
642 response, conv->appdata_ptr);
645 return retval; /* propagate error status */
649 static int _make_remark(struct pwb_context *ctx,
653 int retval = PAM_SUCCESS;
655 struct pam_message *pmsg[1], msg[1];
656 struct pam_response *resp;
658 if (ctx->flags & WINBIND_SILENT) {
663 msg[0].msg = discard_const_p(char, text);
664 msg[0].msg_style = type;
667 retval = converse(ctx->pamh, 1, pmsg, &resp);
670 _pam_drop_reply(resp, 1);
675 static int _make_remark_v(struct pwb_context *ctx,
683 ret = vasprintf(&var, format, args);
685 _pam_log(ctx, LOG_ERR, "memory allocation failure");
689 ret = _make_remark(ctx, type, var);
694 static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
695 static int _make_remark_format(struct pwb_context *ctx, int type, const char *format, ...)
700 va_start(args, format);
701 ret = _make_remark_v(ctx, type, format, args);
706 static int pam_winbind_request(struct pwb_context *ctx,
707 enum winbindd_cmd req_type,
708 struct winbindd_request *request,
709 struct winbindd_response *response)
711 /* Fill in request and send down pipe */
712 winbindd_init_request(request, req_type);
714 if (winbind_write_sock(request, sizeof(*request), 0, 0) == -1) {
715 _pam_log(ctx, LOG_ERR,
716 "pam_winbind_request: write to socket failed!");
717 winbind_close_sock();
718 return PAM_SERVICE_ERR;
722 if (winbindd_read_reply(response) == -1) {
723 _pam_log(ctx, LOG_ERR,
724 "pam_winbind_request: read from socket failed!");
725 winbind_close_sock();
726 return PAM_SERVICE_ERR;
729 /* We are done with the socket - close it and avoid mischeif */
730 winbind_close_sock();
732 /* Copy reply data from socket */
733 if (response->result == WINBINDD_OK) {
737 if (response->data.auth.pam_error != PAM_SUCCESS) {
738 _pam_log(ctx, LOG_ERR,
739 "request failed: %s, "
740 "PAM error was %s (%d), NT error was %s",
741 response->data.auth.error_string,
742 pam_strerror(ctx->pamh, response->data.auth.pam_error),
743 response->data.auth.pam_error,
744 response->data.auth.nt_status_string);
745 return response->data.auth.pam_error;
748 _pam_log(ctx, LOG_ERR, "request failed, but PAM error 0!");
750 return PAM_SERVICE_ERR;
753 static int pam_winbind_request_log(struct pwb_context *ctx,
759 /* incorrect password */
760 _pam_log(ctx, LOG_WARNING, "user '%s' denied access "
761 "(incorrect password or invalid membership)", user);
763 case PAM_ACCT_EXPIRED:
764 /* account expired */
765 _pam_log(ctx, LOG_WARNING, "user '%s' account expired",
768 case PAM_AUTHTOK_EXPIRED:
769 /* password expired */
770 _pam_log(ctx, LOG_WARNING, "user '%s' password expired",
773 case PAM_NEW_AUTHTOK_REQD:
774 /* new password required */
775 _pam_log(ctx, LOG_WARNING, "user '%s' new password "
778 case PAM_USER_UNKNOWN:
779 /* the user does not exist */
780 _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
782 if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
787 /* Otherwise, the authentication looked good */
790 case WINBINDD_PAM_AUTH:
791 _pam_log(ctx, LOG_NOTICE,
792 "user '%s' granted access", user);
794 case WINBINDD_PAM_CHAUTHTOK:
795 _pam_log(ctx, LOG_NOTICE,
796 "user '%s' password changed", user);
799 _pam_log(ctx, LOG_NOTICE,
800 "user '%s' OK", user);
806 /* we don't know anything about this return value */
807 _pam_log(ctx, LOG_ERR,
808 "internal module error (retval = %d, user = '%s')",
814 static int wbc_auth_error_to_pam_error(struct pwb_context *ctx,
815 struct wbcAuthErrorInfo *e,
817 const char *username,
820 int ret = PAM_AUTH_ERR;
822 if (WBC_ERROR_IS_OK(status)) {
823 _pam_log_debug(ctx, LOG_DEBUG, "request %s succeeded",
826 return pam_winbind_request_log(ctx, ret, username);
830 if (e->pam_error != PAM_SUCCESS) {
831 _pam_log(ctx, LOG_ERR,
832 "request %s failed: %s, "
833 "PAM error: %s (%d), NTSTATUS: %s, "
834 "Error message was: %s",
836 wbcErrorString(status),
837 _pam_error_code_str(e->pam_error),
842 return pam_winbind_request_log(ctx, ret, username);
845 _pam_log(ctx, LOG_ERR, "request %s failed, but PAM error 0!", fn);
847 ret = PAM_SERVICE_ERR;
848 return pam_winbind_request_log(ctx, ret, username);
851 ret = wbc_error_to_pam_error(status);
852 return pam_winbind_request_log(ctx, ret, username);
857 * send a password expiry message if required
859 * @param ctx PAM winbind context.
860 * @param next_change expected (calculated) next expiry date.
861 * @param already_expired pointer to a boolean to indicate if the password is
864 * @return boolean Returns true if message has been sent, false if not.
867 static bool _pam_send_password_expiry_message(struct pwb_context *ctx,
871 bool *already_expired)
874 struct tm tm_now, tm_next_change;
876 if (already_expired) {
877 *already_expired = false;
880 if (next_change <= now) {
881 PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PASSWORD_EXPIRED");
882 if (already_expired) {
883 *already_expired = true;
888 if ((next_change < 0) ||
889 (next_change > now + warn_pwd_expire * SECONDS_PER_DAY)) {
893 if ((localtime_r(&now, &tm_now) == NULL) ||
894 (localtime_r(&next_change, &tm_next_change) == NULL)) {
898 days = (tm_next_change.tm_yday+tm_next_change.tm_year*365) -
899 (tm_now.tm_yday+tm_now.tm_year*365);
902 _make_remark(ctx, PAM_TEXT_INFO,
903 _("Your password expires today"));
907 if (days > 0 && days < warn_pwd_expire) {
908 _make_remark_format(ctx, PAM_TEXT_INFO,
909 _("Your password will expire in %d %s"),
910 days, (days > 1) ? _("days"):_("day"));
918 * Send a warning if the password expires in the near future
920 * @param ctx PAM winbind context.
921 * @param response The full authentication response structure.
922 * @param already_expired boolean, is the pwd already expired?
927 static void _pam_warn_password_expiry(struct pwb_context *ctx,
928 const struct winbindd_response *response,
930 bool *already_expired)
932 time_t now = time(NULL);
933 time_t next_change = 0;
935 if (already_expired) {
936 *already_expired = false;
939 /* accounts with ACB_PWNOEXP set never receive a warning */
940 if (response->data.auth.info3.acct_flags & ACB_PWNOEXP) {
944 /* no point in sending a warning if this is a grace logon */
945 if (PAM_WB_GRACE_LOGON(response->data.auth.info3.user_flgs)) {
949 /* check if the info3 must change timestamp has been set */
950 next_change = response->data.auth.info3.pass_must_change_time;
952 if (_pam_send_password_expiry_message(ctx, next_change, now,
958 /* now check for the global password policy */
959 /* good catch from Ralf Haferkamp: an expiry of "never" is translated
961 if (response->data.auth.policy.expire <= 0) {
965 next_change = response->data.auth.info3.pass_last_set_time +
966 response->data.auth.policy.expire;
968 if (_pam_send_password_expiry_message(ctx, next_change, now,
974 /* no warning sent */
977 #define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0)
980 * Append a string, making sure not to overflow and to always return a
981 * NULL-terminated string.
983 * @param dest Destination string buffer (must already be NULL-terminated).
984 * @param src Source string buffer.
985 * @param dest_buffer_size Size of dest buffer in bytes.
987 * @return false if dest buffer is not big enough (no bytes copied), true on
991 static bool safe_append_string(char *dest,
993 int dest_buffer_size)
995 int dest_length = strlen(dest);
996 int src_length = strlen(src);
998 if (dest_length + src_length + 1 > dest_buffer_size) {
1002 memcpy(dest + dest_length, src, src_length + 1);
1007 * Convert a names into a SID string, appending it to a buffer.
1009 * @param ctx PAM winbind context.
1010 * @param user User in PAM request.
1011 * @param name Name to convert.
1012 * @param sid_list_buffer Where to append the string sid.
1013 * @param sid_list_buffer Size of sid_list_buffer (in bytes).
1015 * @return false on failure, true on success.
1017 static bool winbind_name_to_sid_string(struct pwb_context *ctx,
1020 char *sid_list_buffer,
1021 int sid_list_buffer_size)
1023 const char* sid_string;
1026 if (IS_SID_STRING(name)) {
1030 struct wbcDomainSid sid;
1031 enum wbcSidType type;
1034 _pam_log_debug(ctx, LOG_DEBUG,
1035 "no sid given, looking up: %s\n", name);
1037 wbc_status = wbcLookupName("", name, &sid, &type);
1038 if (!WBC_ERROR_IS_OK(wbc_status)) {
1039 _pam_log(ctx, LOG_INFO,
1040 "could not lookup name: %s\n", name);
1044 wbc_status = wbcSidToString(&sid, &sid_str);
1045 if (!WBC_ERROR_IS_OK(wbc_status)) {
1049 wbcFreeMemory(sid_str);
1050 sid_string = sid_str;
1053 if (!safe_append_string(sid_list_buffer, sid_string,
1054 sid_list_buffer_size)) {
1062 * Convert a list of names into a list of sids.
1064 * @param ctx PAM winbind context.
1065 * @param user User in PAM request.
1066 * @param name_list List of names or string sids, separated by commas.
1067 * @param sid_list_buffer Where to put the list of string sids.
1068 * @param sid_list_buffer Size of sid_list_buffer (in bytes).
1070 * @return false on failure, true on success.
1072 static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx,
1074 const char *name_list,
1075 char *sid_list_buffer,
1076 int sid_list_buffer_size)
1078 bool result = false;
1079 char *current_name = NULL;
1080 const char *search_location;
1083 if (sid_list_buffer_size > 0) {
1084 sid_list_buffer[0] = 0;
1087 search_location = name_list;
1088 while ((comma = strstr(search_location, ",")) != NULL) {
1089 current_name = strndup(search_location,
1090 comma - search_location);
1091 if (NULL == current_name) {
1095 if (!winbind_name_to_sid_string(ctx, user,
1098 sid_list_buffer_size)) {
1102 SAFE_FREE(current_name);
1104 if (!safe_append_string(sid_list_buffer, ",",
1105 sid_list_buffer_size)) {
1109 search_location = comma + 1;
1112 if (!winbind_name_to_sid_string(ctx, user, search_location,
1114 sid_list_buffer_size)) {
1121 SAFE_FREE(current_name);
1126 * put krb5ccname variable into environment
1128 * @param ctx PAM winbind context.
1129 * @param krb5ccname env variable retrieved from winbindd.
1134 static void _pam_setup_krb5_env(struct pwb_context *ctx,
1135 const char *krb5ccname)
1140 if (off(ctx->ctrl, WINBIND_KRB5_AUTH)) {
1144 if (!krb5ccname || (strlen(krb5ccname) == 0)) {
1148 _pam_log_debug(ctx, LOG_DEBUG,
1149 "request returned KRB5CCNAME: %s", krb5ccname);
1151 if (snprintf(var, sizeof(var), "KRB5CCNAME=%s", krb5ccname) == -1) {
1155 ret = pam_putenv(ctx->pamh, var);
1157 _pam_log(ctx, LOG_ERR,
1158 "failed to set KRB5CCNAME to %s: %s",
1159 var, pam_strerror(ctx->pamh, ret));
1164 * Set string into the PAM stack.
1166 * @param ctx PAM winbind context.
1167 * @param data_name Key name for pam_set_data.
1168 * @param value String value.
1173 static void _pam_set_data_string(struct pwb_context *ctx,
1174 const char *data_name,
1179 if (!data_name || !value || (strlen(data_name) == 0) ||
1180 (strlen(value) == 0)) {
1184 ret = pam_set_data(ctx->pamh, data_name, talloc_strdup(NULL, value),
1185 _pam_winbind_cleanup_func);
1187 _pam_log_debug(ctx, LOG_DEBUG,
1188 "Could not set data %s: %s\n",
1189 data_name, pam_strerror(ctx->pamh, ret));
1194 * Set info3 strings into the PAM stack.
1196 * @param ctx PAM winbind context.
1197 * @param data_name Key name for pam_set_data.
1198 * @param value String value.
1203 static void _pam_set_data_info3(struct pwb_context *ctx,
1204 struct winbindd_response *response)
1206 _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR,
1207 response->data.auth.info3.home_dir);
1208 _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT,
1209 response->data.auth.info3.logon_script);
1210 _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER,
1211 response->data.auth.info3.logon_srv);
1212 _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH,
1213 response->data.auth.info3.profile_path);
1217 * Free info3 strings in the PAM stack.
1219 * @param pamh PAM handle
1224 static void _pam_free_data_info3(pam_handle_t *pamh)
1226 pam_set_data(pamh, PAM_WINBIND_HOMEDIR, NULL, NULL);
1227 pam_set_data(pamh, PAM_WINBIND_LOGONSCRIPT, NULL, NULL);
1228 pam_set_data(pamh, PAM_WINBIND_LOGONSERVER, NULL, NULL);
1229 pam_set_data(pamh, PAM_WINBIND_PROFILEPATH, NULL, NULL);
1233 * Send PAM_ERROR_MSG for cached or grace logons.
1235 * @param ctx PAM winbind context.
1236 * @param username User in PAM request.
1237 * @param info3_user_flgs Info3 flags containing logon type bits.
1242 static void _pam_warn_logon_type(struct pwb_context *ctx,
1243 const char *username,
1244 uint32_t info3_user_flgs)
1246 /* inform about logon type */
1247 if (PAM_WB_GRACE_LOGON(info3_user_flgs)) {
1249 _make_remark(ctx, PAM_ERROR_MSG,
1251 "Please change your password as soon you're "
1253 _pam_log_debug(ctx, LOG_DEBUG,
1254 "User %s logged on using grace logon\n",
1257 } else if (PAM_WB_CACHED_LOGON(info3_user_flgs)) {
1259 _make_remark(ctx, PAM_ERROR_MSG,
1260 _("Domain Controller unreachable, "
1261 "using cached credentials instead. "
1262 "Network resources may be unavailable"));
1263 _pam_log_debug(ctx, LOG_DEBUG,
1264 "User %s logged on using cached credentials\n",
1270 * Send PAM_ERROR_MSG for krb5 errors.
1272 * @param ctx PAM winbind context.
1273 * @param username User in PAM request.
1274 * @param info3_user_flgs Info3 flags containing logon type bits.
1279 static void _pam_warn_krb5_failure(struct pwb_context *ctx,
1280 const char *username,
1281 uint32_t info3_user_flgs)
1283 if (PAM_WB_KRB5_CLOCK_SKEW(info3_user_flgs)) {
1284 _make_remark(ctx, PAM_ERROR_MSG,
1285 _("Failed to establish your Kerberos Ticket cache "
1286 "due time differences\n"
1287 "with the domain controller. "
1288 "Please verify the system time.\n"));
1289 _pam_log_debug(ctx, LOG_DEBUG,
1290 "User %s: Clock skew when getting Krb5 TGT\n",
1295 static bool _pam_check_remark_auth_err(struct pwb_context *ctx,
1296 const struct wbcAuthErrorInfo *e,
1297 const char *nt_status_string,
1300 const char *ntstatus = NULL;
1301 const char *error_string = NULL;
1303 if (!e || !pam_error) {
1307 ntstatus = e->nt_string;
1312 if (strcasecmp(ntstatus, nt_status_string) == 0) {
1314 error_string = _get_ntstatus_error_string(nt_status_string);
1316 _make_remark(ctx, PAM_ERROR_MSG, error_string);
1317 *pam_error = e->pam_error;
1321 if (e->display_string) {
1322 _make_remark(ctx, PAM_ERROR_MSG, e->display_string);
1323 *pam_error = e->pam_error;
1327 _make_remark(ctx, PAM_ERROR_MSG, nt_status_string);
1328 *pam_error = e->pam_error;
1337 * Compose Password Restriction String for a PAM_ERROR_MSG conversation.
1339 * @param response The struct winbindd_response.
1341 * @return string (caller needs to free).
1343 static char *_pam_compose_pwd_restriction_string(struct pwb_context *ctx,
1344 struct winbindd_response *response)
1348 str = talloc_asprintf(ctx, _("Your password "));
1353 if (response->data.auth.policy.min_length_password > 0) {
1354 str = talloc_asprintf_append(str,
1355 _("must be at least %d characters; "),
1356 response->data.auth.policy.min_length_password);
1362 if (response->data.auth.policy.password_history > 0) {
1363 str = talloc_asprintf_append(str,
1364 _("cannot repeat any of your previous %d "
1366 response->data.auth.policy.password_history);
1372 if (response->data.auth.policy.password_properties &
1373 DOMAIN_PASSWORD_COMPLEX) {
1374 str = talloc_asprintf_append(str,
1375 _("must contain capitals, numerals "
1377 "and cannot contain your account "
1384 str = talloc_asprintf_append(str,
1385 _("Please type a different password. "
1386 "Type a password which meets these requirements in "
1387 "both text boxes."));
1399 /* talk to winbindd */
1400 static int winbind_auth_request(struct pwb_context *ctx,
1405 const int warn_pwd_expire,
1406 struct winbindd_response *p_response,
1407 time_t *pwd_last_set,
1410 struct winbindd_request request;
1411 struct winbindd_response response;
1413 bool already_expired = false;
1415 ZERO_STRUCT(request);
1416 ZERO_STRUCT(response);
1422 strncpy(request.data.auth.user, user,
1423 sizeof(request.data.auth.user)-1);
1425 strncpy(request.data.auth.pass, pass,
1426 sizeof(request.data.auth.pass)-1);
1428 request.data.auth.krb5_cc_type[0] = '\0';
1429 request.data.auth.uid = -1;
1431 request.flags = WBFLAG_PAM_INFO3_TEXT | WBFLAG_PAM_GET_PWD_POLICY;
1433 /* Krb5 auth always has to go against the KDC of the user's realm */
1435 if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1436 request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM;
1439 if (ctx->ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) {
1440 struct passwd *pwd = NULL;
1442 pwd = getpwnam(user);
1444 return PAM_USER_UNKNOWN;
1446 request.data.auth.uid = pwd->pw_uid;
1449 if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1451 _pam_log_debug(ctx, LOG_DEBUG,
1452 "enabling krb5 login flag\n");
1454 request.flags |= WBFLAG_PAM_KRB5 |
1455 WBFLAG_PAM_FALLBACK_AFTER_KRB5;
1458 if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
1459 _pam_log_debug(ctx, LOG_DEBUG,
1460 "enabling cached login flag\n");
1461 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
1466 request.flags |= WBFLAG_PAM_UNIX_NAME;
1469 if (cctype != NULL) {
1470 strncpy(request.data.auth.krb5_cc_type, cctype,
1471 sizeof(request.data.auth.krb5_cc_type) - 1);
1472 _pam_log_debug(ctx, LOG_DEBUG,
1473 "enabling request for a %s krb5 ccache\n",
1477 request.data.auth.require_membership_of_sid[0] = '\0';
1479 if (member != NULL) {
1481 if (!winbind_name_list_to_sid_string_list(ctx, user,
1483 request.data.auth.require_membership_of_sid,
1484 sizeof(request.data.auth.require_membership_of_sid))) {
1486 _pam_log_debug(ctx, LOG_ERR,
1487 "failed to serialize membership of sid "
1488 "\"%s\"\n", member);
1489 return PAM_AUTH_ERR;
1493 ret = pam_winbind_request_log(ctx, WINBINDD_PAM_AUTH,
1494 &request, &response, user);
1497 *pwd_last_set = response.data.auth.info3.pass_last_set_time;
1501 /* We want to process the response in the caller. */
1502 *p_response = response;
1507 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1508 "NT_STATUS_PASSWORD_EXPIRED");
1509 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1510 "NT_STATUS_PASSWORD_MUST_CHANGE");
1511 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1512 "NT_STATUS_INVALID_WORKSTATION");
1513 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1514 "NT_STATUS_INVALID_LOGON_HOURS");
1515 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1516 "NT_STATUS_ACCOUNT_EXPIRED");
1517 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1518 "NT_STATUS_ACCOUNT_DISABLED");
1519 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1520 "NT_STATUS_ACCOUNT_LOCKED_OUT");
1521 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1522 "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT");
1523 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1524 "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT");
1525 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1526 "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT");
1527 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1528 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
1529 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1530 "NT_STATUS_NO_LOGON_SERVERS");
1531 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1532 "NT_STATUS_WRONG_PASSWORD");
1533 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1534 "NT_STATUS_ACCESS_DENIED");
1537 if (ret == PAM_SUCCESS) {
1539 /* warn a user if the password is about to expire soon */
1540 _pam_warn_password_expiry(ctx, &response,
1544 if (already_expired == true) {
1545 SMB_TIME_T last_set;
1546 last_set = response.data.auth.info3.pass_last_set_time;
1547 _pam_log_debug(ctx, LOG_DEBUG,
1548 "Password has expired "
1549 "(Password was last set: %lld, "
1550 "the policy says it should expire here "
1551 "%lld (now it's: %lu))\n",
1552 (long long int)last_set,
1553 (long long int)last_set +
1554 response.data.auth.policy.expire,
1557 return PAM_AUTHTOK_EXPIRED;
1560 /* inform about logon type */
1561 _pam_warn_logon_type(ctx, user,
1562 response.data.auth.info3.user_flgs);
1564 /* inform about krb5 failures */
1565 _pam_warn_krb5_failure(ctx, user,
1566 response.data.auth.info3.user_flgs);
1568 /* set some info3 info for other modules in the stack */
1569 _pam_set_data_info3(ctx, &response);
1571 /* put krb5ccname into env */
1572 _pam_setup_krb5_env(ctx, response.data.auth.krb5ccname);
1574 /* If winbindd returned a username, return the pointer to it
1576 if (user_ret && response.data.auth.unix_username[0]) {
1577 /* We have to trust it's a null terminated string. */
1578 *user_ret = strndup(response.data.auth.unix_username,
1579 sizeof(response.data.auth.unix_username) - 1);
1586 /* talk to winbindd */
1587 static int winbind_chauthtok_request(struct pwb_context *ctx,
1589 const char *oldpass,
1590 const char *newpass,
1591 time_t pwd_last_set)
1593 struct winbindd_request request;
1594 struct winbindd_response response;
1597 ZERO_STRUCT(request);
1598 ZERO_STRUCT(response);
1600 if (request.data.chauthtok.user == NULL) {
1604 strncpy(request.data.chauthtok.user, user,
1605 sizeof(request.data.chauthtok.user) - 1);
1607 if (oldpass != NULL) {
1608 strncpy(request.data.chauthtok.oldpass, oldpass,
1609 sizeof(request.data.chauthtok.oldpass) - 1);
1611 request.data.chauthtok.oldpass[0] = '\0';
1614 if (newpass != NULL) {
1615 strncpy(request.data.chauthtok.newpass, newpass,
1616 sizeof(request.data.chauthtok.newpass) - 1);
1618 request.data.chauthtok.newpass[0] = '\0';
1621 if (ctx->ctrl & WINBIND_KRB5_AUTH) {
1622 request.flags = WBFLAG_PAM_KRB5 |
1623 WBFLAG_PAM_CONTACT_TRUSTDOM;
1626 if (ctx->ctrl & WINBIND_CACHED_LOGIN) {
1627 request.flags |= WBFLAG_PAM_CACHED_LOGIN;
1630 ret = pam_winbind_request_log(ctx, WINBINDD_PAM_CHAUTHTOK,
1631 &request, &response, user);
1633 if (ret == PAM_SUCCESS) {
1637 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1638 "NT_STATUS_BACKUP_CONTROLLER");
1639 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1640 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
1641 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1642 "NT_STATUS_NO_LOGON_SERVERS");
1643 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1644 "NT_STATUS_ACCESS_DENIED");
1646 /* TODO: tell the min pwd length ? */
1647 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1648 "NT_STATUS_PWD_TOO_SHORT");
1650 /* TODO: tell the minage ? */
1651 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1652 "NT_STATUS_PWD_TOO_RECENT");
1654 /* TODO: tell the history length ? */
1655 PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response,
1656 "NT_STATUS_PWD_HISTORY_CONFLICT");
1658 if (!strcasecmp(response.data.auth.nt_status_string,
1659 "NT_STATUS_PASSWORD_RESTRICTION")) {
1661 char *pwd_restriction_string = NULL;
1662 SMB_TIME_T min_pwd_age;
1663 uint32_t reject_reason = response.data.auth.reject_reason;
1664 min_pwd_age = response.data.auth.policy.min_passwordage;
1666 /* FIXME: avoid to send multiple PAM messages after another */
1667 switch (reject_reason) {
1670 case SAMR_REJECT_OTHER:
1671 if ((min_pwd_age > 0) &&
1672 (pwd_last_set + min_pwd_age > time(NULL))) {
1673 PAM_WB_REMARK_DIRECT(ctx,
1674 "NT_STATUS_PWD_TOO_RECENT");
1677 case SAMR_REJECT_TOO_SHORT:
1678 PAM_WB_REMARK_DIRECT(ctx,
1679 "NT_STATUS_PWD_TOO_SHORT");
1681 case SAMR_REJECT_IN_HISTORY:
1682 PAM_WB_REMARK_DIRECT(ctx,
1683 "NT_STATUS_PWD_HISTORY_CONFLICT");
1685 case SAMR_REJECT_COMPLEXITY:
1686 _make_remark(ctx, PAM_ERROR_MSG,
1687 _("Password does not meet "
1688 "complexity requirements"));
1691 _pam_log_debug(ctx, LOG_DEBUG,
1692 "unknown password change "
1693 "reject reason: %d",
1698 pwd_restriction_string =
1699 _pam_compose_pwd_restriction_string(ctx, &response);
1700 if (pwd_restriction_string) {
1701 _make_remark(ctx, PAM_ERROR_MSG,
1702 pwd_restriction_string);
1703 TALLOC_FREE(pwd_restriction_string);
1711 * Checks if a user has an account
1714 * 1 = User not found
1718 static int valid_user(struct pwb_context *ctx,
1721 /* check not only if the user is available over NSS calls, also make
1722 * sure it's really a winbind user, this is important when stacking PAM
1723 * modules in the 'account' or 'password' facility. */
1726 struct passwd *pwd = NULL;
1727 struct passwd *wb_pwd = NULL;
1729 pwd = getpwnam(user);
1734 wbc_status = wbcGetpwnam(user, &wb_pwd);
1735 wbcFreeMemory(wb_pwd);
1736 if (!WBC_ERROR_IS_OK(wbc_status)) {
1737 _pam_log(ctx, LOG_DEBUG, "valid_user: wbcGetpwnam gave %s\n",
1738 wbcErrorString(wbc_status));
1741 switch (wbc_status) {
1742 case WBC_ERR_UNKNOWN_USER:
1744 case WBC_ERR_SUCCESS:
1752 static char *_pam_delete(register char *xx)
1760 * obtain a password from the user
1763 static int _winbind_read_password(struct pwb_context *ctx,
1765 const char *comment,
1766 const char *prompt1,
1767 const char *prompt2,
1775 _pam_log(ctx, LOG_DEBUG, "getting password (0x%08x)", ctrl);
1778 * make sure nothing inappropriate gets returned
1781 *pass = token = NULL;
1784 * which authentication token are we getting?
1787 if (on(WINBIND__OLD_PASSWORD, ctrl)) {
1788 authtok_flag = PAM_OLDAUTHTOK;
1790 authtok_flag = PAM_AUTHTOK;
1794 * should we obtain the password from a PAM item ?
1797 if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) ||
1798 on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
1799 retval = _pam_get_item(ctx->pamh, authtok_flag, &item);
1800 if (retval != PAM_SUCCESS) {
1802 _pam_log(ctx, LOG_ALERT,
1803 "pam_get_item returned error "
1804 "to unix-read-password");
1806 } else if (item != NULL) { /* we have a password! */
1809 _pam_log(ctx, LOG_DEBUG,
1810 "pam_get_item returned a password");
1812 } else if (on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) {
1813 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
1814 } else if (on(WINBIND_USE_AUTHTOK_ARG, ctrl)
1815 && off(WINBIND__OLD_PASSWORD, ctrl)) {
1816 return PAM_AUTHTOK_RECOVER_ERR;
1820 * getting here implies we will have to get the password from the
1825 struct pam_message msg[3], *pmsg[3];
1826 struct pam_response *resp;
1829 /* prepare to converse */
1831 if (comment != NULL && off(ctrl, WINBIND_SILENT)) {
1833 msg[0].msg_style = PAM_TEXT_INFO;
1834 msg[0].msg = discard_const_p(char, comment);
1841 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
1842 msg[i++].msg = discard_const_p(char, prompt1);
1845 if (prompt2 != NULL) {
1847 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
1848 msg[i++].msg = discard_const_p(char, prompt2);
1851 /* so call the conversation expecting i responses */
1853 retval = converse(ctx->pamh, i, pmsg, &resp);
1855 if (retval == PAM_SUCCESS) {
1856 retval = PAM_AUTHTOK_RECOVER_ERR;
1860 if (retval != PAM_SUCCESS) {
1861 _pam_drop_reply(resp, i);
1865 /* interpret the response */
1867 token = x_strdup(resp[i - replies].resp);
1869 _pam_log(ctx, LOG_NOTICE,
1870 "could not recover "
1871 "authentication token");
1872 retval = PAM_AUTHTOK_RECOVER_ERR;
1877 /* verify that password entered correctly */
1878 if (!resp[i - 1].resp ||
1879 strcmp(token, resp[i - 1].resp)) {
1880 _pam_delete(token); /* mistyped */
1881 retval = PAM_AUTHTOK_RECOVER_ERR;
1882 _make_remark(ctx, PAM_ERROR_MSG,
1888 * tidy up the conversation (resp_retcode) is ignored
1889 * -- what is it for anyway? AGM
1891 _pam_drop_reply(resp, i);
1895 if (retval != PAM_SUCCESS) {
1896 _pam_log_debug(ctx, LOG_DEBUG,
1897 "unable to obtain a password");
1900 /* 'token' is the entered password */
1902 /* we store this password as an item */
1904 retval = pam_set_item(ctx->pamh, authtok_flag, token);
1905 _pam_delete(token); /* clean it up */
1906 if (retval != PAM_SUCCESS ||
1907 (retval = _pam_get_item(ctx->pamh, authtok_flag, &item)) != PAM_SUCCESS) {
1909 _pam_log(ctx, LOG_CRIT, "error manipulating password");
1915 item = NULL; /* break link to password */
1920 static const char *get_conf_item_string(struct pwb_context *ctx,
1925 const char *parm_opt = NULL;
1927 if (!(ctx->ctrl & config_flag)) {
1931 /* let the pam opt take precedence over the pam_winbind.conf option */
1932 for (i=0; i<ctx->argc; i++) {
1934 if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
1937 if ((p = strchr(ctx->argv[i], '=')) == NULL) {
1938 _pam_log(ctx, LOG_INFO,
1939 "no \"=\" delimiter for \"%s\" found\n",
1943 _pam_log_debug(ctx, LOG_INFO,
1944 "PAM config: %s '%s'\n", item, p+1);
1952 key = talloc_asprintf(ctx, "global:%s", item);
1957 parm_opt = iniparser_getstr(ctx->dict, key);
1960 _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n",
1967 static int get_config_item_int(struct pwb_context *ctx,
1971 int i, parm_opt = -1;
1973 if (!(ctx->ctrl & config_flag)) {
1977 /* let the pam opt take precedence over the pam_winbind.conf option */
1978 for (i = 0; i < ctx->argc; i++) {
1980 if ((strncmp(ctx->argv[i], item, strlen(item)) == 0)) {
1983 if ((p = strchr(ctx->argv[i], '=')) == NULL) {
1984 _pam_log(ctx, LOG_INFO,
1985 "no \"=\" delimiter for \"%s\" found\n",
1989 parm_opt = atoi(p + 1);
1990 _pam_log_debug(ctx, LOG_INFO,
1991 "PAM config: %s '%d'\n",
2000 key = talloc_asprintf(ctx, "global:%s", item);
2005 parm_opt = iniparser_getint(ctx->dict, key, -1);
2008 _pam_log_debug(ctx, LOG_INFO,
2009 "CONFIG file: %s '%d'\n",
2016 static const char *get_krb5_cc_type_from_config(struct pwb_context *ctx)
2018 return get_conf_item_string(ctx, "krb5_ccache_type",
2019 WINBIND_KRB5_CCACHE_TYPE);
2022 static const char *get_member_from_config(struct pwb_context *ctx)
2024 const char *ret = NULL;
2025 ret = get_conf_item_string(ctx, "require_membership_of",
2026 WINBIND_REQUIRED_MEMBERSHIP);
2030 return get_conf_item_string(ctx, "require-membership-of",
2031 WINBIND_REQUIRED_MEMBERSHIP);
2034 static int get_warn_pwd_expire_from_config(struct pwb_context *ctx)
2037 ret = get_config_item_int(ctx, "warn_pwd_expire",
2038 WINBIND_WARN_PWD_EXPIRE);
2039 /* no or broken setting */
2041 return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES;
2047 * Retrieve the winbind separator.
2049 * @param ctx PAM winbind context.
2051 * @return string separator character. NULL on failure.
2054 static char winbind_get_separator(struct pwb_context *ctx)
2057 static struct wbcInterfaceDetails *details = NULL;
2059 wbc_status = wbcInterfaceDetails(&details);
2060 if (!WBC_ERROR_IS_OK(wbc_status)) {
2061 _pam_log(ctx, LOG_ERR,
2062 "Could not retrieve winbind interface details: %s",
2063 wbcErrorString(wbc_status));
2071 return details->winbind_separator;
2076 * Convert a upn to a name.
2078 * @param ctx PAM winbind context.
2079 * @param upn USer UPN to be trabslated.
2081 * @return converted name. NULL pointer on failure. Caller needs to free.
2084 static char* winbind_upn_to_username(struct pwb_context *ctx,
2088 wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
2089 struct wbcDomainSid sid;
2090 enum wbcSidType type;
2094 /* This cannot work when the winbind separator = @ */
2096 sep = winbind_get_separator(ctx);
2097 if (!sep || sep == '@') {
2101 /* Convert the UPN to a SID */
2103 wbc_status = wbcLookupName("", upn, &sid, &type);
2104 if (!WBC_ERROR_IS_OK(wbc_status)) {
2108 /* Convert the the SID back to the sAMAccountName */
2110 wbc_status = wbcLookupSid(&sid, &domain, &name, &type);
2111 if (!WBC_ERROR_IS_OK(wbc_status)) {
2115 return talloc_asprintf(ctx, "%s\\%s", domain, name);
2119 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
2120 int argc, const char **argv)
2122 const char *username;
2123 const char *password;
2124 const char *member = NULL;
2125 const char *cctype = NULL;
2126 int warn_pwd_expire;
2127 int retval = PAM_AUTH_ERR;
2128 char *username_ret = NULL;
2129 char *new_authtok_required = NULL;
2130 char *real_username = NULL;
2131 struct pwb_context *ctx = NULL;
2133 retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2138 _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", ctx);
2140 /* Get the username */
2141 retval = pam_get_user(pamh, &username, NULL);
2142 if ((retval != PAM_SUCCESS) || (!username)) {
2143 _pam_log_debug(ctx, LOG_DEBUG,
2144 "can not get the username");
2145 retval = PAM_SERVICE_ERR;
2151 /* Decode the user name since AIX does not support logn user
2152 names by default. The name is encoded as _#uid. */
2154 if (username[0] == '_') {
2155 uid_t id = atoi(&username[1]);
2156 struct passwd *pw = NULL;
2158 if ((id!=0) && ((pw = getpwuid(id)) != NULL)) {
2159 real_username = strdup(pw->pw_name);
2164 if (!real_username) {
2165 /* Just making a copy of the username we got from PAM */
2166 if ((real_username = strdup(username)) == NULL) {
2167 _pam_log_debug(ctx, LOG_DEBUG,
2168 "memory allocation failure when copying "
2170 retval = PAM_SERVICE_ERR;
2175 /* Maybe this was a UPN */
2177 if (strchr(real_username, '@') != NULL) {
2178 char *samaccountname = NULL;
2180 samaccountname = winbind_upn_to_username(ctx,
2182 if (samaccountname) {
2183 free(real_username);
2184 real_username = strdup(samaccountname);
2188 retval = _winbind_read_password(ctx, ctx->ctrl, NULL,
2189 _("Password: "), NULL,
2192 if (retval != PAM_SUCCESS) {
2193 _pam_log(ctx, LOG_ERR,
2194 "Could not retrieve user's password");
2195 retval = PAM_AUTHTOK_ERR;
2199 /* Let's not give too much away in the log file */
2201 #ifdef DEBUG_PASSWORD
2202 _pam_log_debug(ctx, LOG_INFO,
2203 "Verify user '%s' with password '%s'",
2204 real_username, password);
2206 _pam_log_debug(ctx, LOG_INFO,
2207 "Verify user '%s'", real_username);
2210 member = get_member_from_config(ctx);
2211 cctype = get_krb5_cc_type_from_config(ctx);
2212 warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
2214 /* Now use the username to look up password */
2215 retval = winbind_auth_request(ctx, real_username, password,
2216 member, cctype, warn_pwd_expire, NULL,
2217 NULL, &username_ret);
2219 if (retval == PAM_NEW_AUTHTOK_REQD ||
2220 retval == PAM_AUTHTOK_EXPIRED) {
2222 char *new_authtok_required_during_auth = NULL;
2224 new_authtok_required = talloc_asprintf(NULL, "%d", retval);
2225 if (!new_authtok_required) {
2226 retval = PAM_BUF_ERR;
2230 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
2231 new_authtok_required,
2232 _pam_winbind_cleanup_func);
2234 retval = PAM_SUCCESS;
2236 new_authtok_required_during_auth = talloc_asprintf(NULL, "%d", true);
2237 if (!new_authtok_required_during_auth) {
2238 retval = PAM_BUF_ERR;
2242 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
2243 new_authtok_required_during_auth,
2244 _pam_winbind_cleanup_func);
2251 pam_set_item (pamh, PAM_USER, username_ret);
2252 _pam_log_debug(ctx, LOG_INFO,
2253 "Returned user was '%s'", username_ret);
2257 if (real_username) {
2258 free(real_username);
2261 if (!new_authtok_required) {
2262 pam_set_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, NULL, NULL);
2265 if (retval != PAM_SUCCESS) {
2266 _pam_free_data_info3(pamh);
2269 _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval);
2277 int pam_sm_setcred(pam_handle_t *pamh, int flags,
2278 int argc, const char **argv)
2280 int ret = PAM_SYSTEM_ERR;
2281 struct pwb_context *ctx = NULL;
2283 ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2288 _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", ctx);
2290 switch (flags & ~PAM_SILENT) {
2292 case PAM_DELETE_CRED:
2293 ret = pam_sm_close_session(pamh, flags, argc, argv);
2295 case PAM_REFRESH_CRED:
2296 _pam_log_debug(ctx, LOG_WARNING,
2297 "PAM_REFRESH_CRED not implemented");
2300 case PAM_REINITIALIZE_CRED:
2301 _pam_log_debug(ctx, LOG_WARNING,
2302 "PAM_REINITIALIZE_CRED not implemented");
2305 case PAM_ESTABLISH_CRED:
2306 _pam_log_debug(ctx, LOG_WARNING,
2307 "PAM_ESTABLISH_CRED not implemented");
2311 ret = PAM_SYSTEM_ERR;
2317 _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret);
2325 * Account management. We want to verify that the account exists
2326 * before returning PAM_SUCCESS
2329 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
2330 int argc, const char **argv)
2332 const char *username;
2333 int ret = PAM_USER_UNKNOWN;
2335 struct pwb_context *ctx = NULL;
2337 ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2342 _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", ctx);
2345 /* Get the username */
2346 ret = pam_get_user(pamh, &username, NULL);
2347 if ((ret != PAM_SUCCESS) || (!username)) {
2348 _pam_log_debug(ctx, LOG_DEBUG,
2349 "can not get the username");
2350 ret = PAM_SERVICE_ERR;
2354 /* Verify the username */
2355 ret = valid_user(ctx, username);
2358 /* some sort of system error. The log was already printed */
2359 ret = PAM_SERVICE_ERR;
2362 /* the user does not exist */
2363 _pam_log_debug(ctx, LOG_NOTICE, "user '%s' not found",
2365 if (ctx->ctrl & WINBIND_UNKNOWN_OK_ARG) {
2369 ret = PAM_USER_UNKNOWN;
2372 pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD,
2373 (const void **)&tmp);
2375 ret = atoi((const char *)tmp);
2377 case PAM_AUTHTOK_EXPIRED:
2378 /* fall through, since new token is required in this case */
2379 case PAM_NEW_AUTHTOK_REQD:
2380 _pam_log(ctx, LOG_WARNING,
2381 "pam_sm_acct_mgmt success but %s is set",
2382 PAM_WINBIND_NEW_AUTHTOK_REQD);
2383 _pam_log(ctx, LOG_NOTICE,
2384 "user '%s' needs new password",
2386 /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
2387 ret = PAM_NEW_AUTHTOK_REQD;
2390 _pam_log(ctx, LOG_WARNING,
2391 "pam_sm_acct_mgmt success");
2392 _pam_log(ctx, LOG_NOTICE,
2393 "user '%s' granted access", username);
2399 /* Otherwise, the authentication looked good */
2400 _pam_log(ctx, LOG_NOTICE,
2401 "user '%s' granted access", username);
2405 /* we don't know anything about this return value */
2406 _pam_log(ctx, LOG_ERR,
2407 "internal module error (ret = %d, user = '%s')",
2409 ret = PAM_SERVICE_ERR;
2413 /* should not be reached */
2418 _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx, ret);
2426 int pam_sm_open_session(pam_handle_t *pamh, int flags,
2427 int argc, const char **argv)
2429 int ret = PAM_SYSTEM_ERR;
2430 struct pwb_context *ctx = NULL;
2432 ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2437 _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx);
2442 _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret);
2450 int pam_sm_close_session(pam_handle_t *pamh, int flags,
2451 int argc, const char **argv)
2453 int retval = PAM_SUCCESS;
2454 struct pwb_context *ctx = NULL;
2456 retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2461 _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", ctx);
2463 if (!(flags & PAM_DELETE_CRED)) {
2464 retval = PAM_SUCCESS;
2468 if (ctx->ctrl & WINBIND_KRB5_AUTH) {
2470 /* destroy the ccache here */
2471 struct winbindd_request request;
2472 struct winbindd_response response;
2474 const char *ccname = NULL;
2475 struct passwd *pwd = NULL;
2477 ZERO_STRUCT(request);
2478 ZERO_STRUCT(response);
2480 retval = pam_get_user(pamh, &user, _("Username: "));
2482 _pam_log(ctx, LOG_ERR,
2483 "could not identify user");
2488 _pam_log(ctx, LOG_ERR,
2489 "username was NULL!");
2490 retval = PAM_USER_UNKNOWN;
2494 _pam_log_debug(ctx, LOG_DEBUG,
2495 "username [%s] obtained", user);
2497 ccname = pam_getenv(pamh, "KRB5CCNAME");
2498 if (ccname == NULL) {
2499 _pam_log_debug(ctx, LOG_DEBUG,
2500 "user has no KRB5CCNAME environment");
2503 strncpy(request.data.logoff.user, user,
2504 sizeof(request.data.logoff.user) - 1);
2507 strncpy(request.data.logoff.krb5ccname, ccname,
2508 sizeof(request.data.logoff.krb5ccname) - 1);
2511 pwd = getpwnam(user);
2513 retval = PAM_USER_UNKNOWN;
2516 request.data.logoff.uid = pwd->pw_uid;
2518 request.flags = WBFLAG_PAM_KRB5 |
2519 WBFLAG_PAM_CONTACT_TRUSTDOM;
2521 retval = pam_winbind_request_log(ctx,
2522 WINBINDD_PAM_LOGOFF,
2523 &request, &response, user);
2528 * Delete the krb5 ccname variable from the PAM environment
2529 * if it was set by winbind.
2531 if (ctx->ctrl & WINBIND_KRB5_AUTH) {
2532 pam_putenv(pamh, "KRB5CCNAME");
2535 _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, retval);
2543 * evaluate whether we need to re-authenticate with kerberos after a
2546 * @param ctx PAM winbind context.
2547 * @param user The username
2549 * @return boolean Returns true if required, false if not.
2552 static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx,
2556 /* Make sure that we only do this if a) the chauthtok got initiated
2557 * during a logon attempt (authenticate->acct_mgmt->chauthtok) b) any
2558 * later password change via the "passwd" command if done by the user
2560 * NB. If we login from gdm or xdm and the password expires,
2561 * we change the password, but there is no memory cache.
2562 * Thus, even for passthrough login, we should do the
2563 * authentication again to update memory cache.
2567 char *new_authtok_reqd_during_auth = NULL;
2568 struct passwd *pwd = NULL;
2570 _pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
2571 &new_authtok_reqd_during_auth);
2572 pam_set_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
2575 if (new_authtok_reqd_during_auth) {
2579 pwd = getpwnam(user);
2584 if (getuid() == pwd->pw_uid) {
2593 int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
2594 int argc, const char **argv)
2598 bool cached_login = false;
2600 /* <DO NOT free() THESE> */
2602 char *pass_old, *pass_new;
2603 /* </DO NOT free() THESE> */
2608 char *username_ret = NULL;
2609 struct winbindd_response response;
2610 struct pwb_context *ctx = NULL;
2612 ZERO_STRUCT(response);
2614 ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
2619 _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", ctx);
2621 cached_login = (ctx->ctrl & WINBIND_CACHED_LOGIN);
2623 /* clearing offline bit for auth */
2624 ctx->ctrl &= ~WINBIND_CACHED_LOGIN;
2627 * First get the name of a user
2629 ret = pam_get_user(pamh, &user, _("Username: "));
2631 _pam_log(ctx, LOG_ERR,
2632 "password - could not identify user");
2637 _pam_log(ctx, LOG_ERR, "username was NULL!");
2638 ret = PAM_USER_UNKNOWN;
2642 _pam_log_debug(ctx, LOG_DEBUG, "username [%s] obtained", user);
2644 /* check if this is really a user in winbindd, not only in NSS */
2645 ret = valid_user(ctx, user);
2648 ret = PAM_USER_UNKNOWN;
2651 ret = PAM_SYSTEM_ERR;
2658 * obtain and verify the current password (OLDAUTHTOK) for
2662 if (flags & PAM_PRELIM_CHECK) {
2663 time_t pwdlastset_prelim = 0;
2665 /* instruct user what is happening */
2666 #define greeting _("Changing password for ")
2667 Announce = (char *) malloc(sizeof(greeting) + strlen(user));
2668 if (Announce == NULL) {
2669 _pam_log(ctx, LOG_CRIT,
2670 "password - out of memory");
2674 (void) strcpy(Announce, greeting);
2675 (void) strcpy(Announce + sizeof(greeting) - 1, user);
2678 lctrl = ctx->ctrl | WINBIND__OLD_PASSWORD;
2679 ret = _winbind_read_password(ctx, lctrl,
2681 _("(current) NT password: "),
2683 (const char **) &pass_old);
2684 TALLOC_FREE(Announce);
2685 if (ret != PAM_SUCCESS) {
2686 _pam_log(ctx, LOG_NOTICE,
2687 "password - (old) token not obtained");
2691 /* verify that this is the password for this user */
2693 ret = winbind_auth_request(ctx, user, pass_old,
2694 NULL, NULL, 0, &response,
2695 &pwdlastset_prelim, NULL);
2697 if (ret != PAM_ACCT_EXPIRED &&
2698 ret != PAM_AUTHTOK_EXPIRED &&
2699 ret != PAM_NEW_AUTHTOK_REQD &&
2700 ret != PAM_SUCCESS) {
2705 pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET,
2706 (void *)pwdlastset_prelim, NULL);
2708 ret = pam_set_item(pamh, PAM_OLDAUTHTOK,
2709 (const void *) pass_old);
2711 if (ret != PAM_SUCCESS) {
2712 _pam_log(ctx, LOG_CRIT,
2713 "failed to set PAM_OLDAUTHTOK");
2715 } else if (flags & PAM_UPDATE_AUTHTOK) {
2717 time_t pwdlastset_update = 0;
2720 * obtain the proposed password
2724 * get the old token back.
2727 ret = _pam_get_item(pamh, PAM_OLDAUTHTOK, &pass_old);
2729 if (ret != PAM_SUCCESS) {
2730 _pam_log(ctx, LOG_NOTICE,
2731 "user not authenticated");
2735 lctrl = ctx->ctrl & ~WINBIND_TRY_FIRST_PASS_ARG;
2737 if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
2738 lctrl |= WINBIND_USE_FIRST_PASS_ARG;
2741 ret = PAM_AUTHTOK_ERR;
2742 while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
2744 * use_authtok is to force the use of a previously entered
2745 * password -- needed for pluggable password strength checking
2748 ret = _winbind_read_password(ctx, lctrl,
2750 _("Enter new NT password: "),
2751 _("Retype new NT password: "),
2752 (const char **)&pass_new);
2754 if (ret != PAM_SUCCESS) {
2755 _pam_log_debug(ctx, LOG_ALERT,
2757 "new password not obtained");
2758 pass_old = NULL;/* tidy up */
2763 * At this point we know who the user is and what they
2764 * propose as their new password. Verify that the new
2765 * password is acceptable.
2768 if (pass_new[0] == '\0') {/* "\0" password = NULL */
2774 * By reaching here we have approved the passwords and must now
2775 * rebuild the password database file.
2777 _pam_get_data(pamh, PAM_WINBIND_PWD_LAST_SET,
2778 &pwdlastset_update);
2781 * if cached creds were enabled, make sure to set the
2782 * WINBIND_CACHED_LOGIN bit here in order to have winbindd
2783 * update the cached creds storage - gd
2786 ctx->ctrl |= WINBIND_CACHED_LOGIN;
2789 ret = winbind_chauthtok_request(ctx, user, pass_old,
2790 pass_new, pwdlastset_update);
2792 _pam_overwrite(pass_new);
2793 _pam_overwrite(pass_old);
2794 pass_old = pass_new = NULL;
2798 if (_pam_require_krb5_auth_after_chauthtok(ctx, user)) {
2800 const char *member = NULL;
2801 const char *cctype = NULL;
2802 int warn_pwd_expire;
2804 member = get_member_from_config(ctx);
2805 cctype = get_krb5_cc_type_from_config(ctx);
2806 warn_pwd_expire = get_warn_pwd_expire_from_config(ctx);
2808 /* Keep WINBIND_CACHED_LOGIN bit for
2809 * authentication after changing the password.
2810 * This will update the cached credentials in case
2811 * that winbindd_dual_pam_chauthtok() fails
2816 ret = winbind_auth_request(ctx, user, pass_new,
2817 member, cctype, 0, &response,
2818 NULL, &username_ret);
2819 _pam_overwrite(pass_new);
2820 _pam_overwrite(pass_old);
2821 pass_old = pass_new = NULL;
2823 if (ret == PAM_SUCCESS) {
2825 /* warn a user if the password is about to
2827 _pam_warn_password_expiry(ctx, &response,
2831 /* set some info3 info for other modules in the
2833 _pam_set_data_info3(ctx, &response);
2835 /* put krb5ccname into env */
2836 _pam_setup_krb5_env(ctx,
2837 response.data.auth.krb5ccname);
2840 pam_set_item(pamh, PAM_USER,
2842 _pam_log_debug(ctx, LOG_INFO,
2843 "Returned user was '%s'",
2852 ret = PAM_SERVICE_ERR;
2857 /* Deal with offline errors. */
2858 PAM_WB_REMARK_CHECK_RESPONSE(ctx, response,
2859 "NT_STATUS_NO_LOGON_SERVERS");
2860 PAM_WB_REMARK_CHECK_RESPONSE(ctx, response,
2861 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND");
2862 PAM_WB_REMARK_CHECK_RESPONSE(ctx, response,
2863 "NT_STATUS_ACCESS_DENIED");
2865 _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx, ret);
2874 /* static module data */
2876 struct pam_module _pam_winbind_modstruct = {
2878 pam_sm_authenticate,
2881 pam_sm_open_session,
2882 pam_sm_close_session,
2889 * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
2890 * Copyright (c) Tim Potter <tpot@samba.org> 2000
2891 * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
2892 * Copyright (c) Guenther Deschner <gd@samba.org> 2005-2008
2893 * Copyright (c) Jan Rêkorajski 1999.
2894 * Copyright (c) Andrew G. Morgan 1996-8.
2895 * Copyright (c) Alex O. Yuriev, 1996.
2896 * Copyright (c) Cristian Gafton 1996.
2897 * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
2899 * Redistribution and use in source and binary forms, with or without
2900 * modification, are permitted provided that the following conditions
2902 * 1. Redistributions of source code must retain the above copyright
2903 * notice, and the entire permission notice in its entirety,
2904 * including the disclaimer of warranties.
2905 * 2. Redistributions in binary form must reproduce the above copyright
2906 * notice, this list of conditions and the following disclaimer in the
2907 * documentation and/or other materials provided with the distribution.
2908 * 3. The name of the author may not be used to endorse or promote
2909 * products derived from this software without specific prior
2910 * written permission.
2912 * ALTERNATIVELY, this product may be distributed under the terms of
2913 * the GNU Public License, in which case the provisions of the GPL are
2914 * required INSTEAD OF the above restrictions. (This clause is
2915 * necessary due to a potential bad interaction between the GPL and
2916 * the restrictions contained in a BSD-style copyright.)
2918 * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
2919 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2920 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2921 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2922 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2923 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2924 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2925 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2926 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2927 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
2928 * OF THE POSSIBILITY OF SUCH DAMAGE.