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 enum pam_winbind_request_type
17 PAM_WINBIND_AUTHENTICATE
,
19 PAM_WINBIND_ACCT_MGMT
,
20 PAM_WINBIND_OPEN_SESSION
,
21 PAM_WINBIND_CLOSE_SESSION
,
22 PAM_WINBIND_CHAUTHTOK
,
26 static int wbc_error_to_pam_error(wbcErr status
)
31 case WBC_ERR_NOT_IMPLEMENTED
:
32 return PAM_SERVICE_ERR
;
33 case WBC_ERR_UNKNOWN_FAILURE
:
35 case WBC_ERR_NO_MEMORY
:
37 case WBC_ERR_INVALID_SID
:
38 case WBC_ERR_INVALID_PARAM
:
40 case WBC_ERR_WINBIND_NOT_AVAILABLE
:
41 return PAM_AUTHINFO_UNAVAIL
;
42 case WBC_ERR_DOMAIN_NOT_FOUND
:
43 case WBC_ERR_NOT_MAPPED
:
44 return PAM_AUTHINFO_UNAVAIL
;
45 case WBC_ERR_INVALID_RESPONSE
:
47 case WBC_ERR_NSS_ERROR
:
48 return PAM_USER_UNKNOWN
;
49 case WBC_ERR_AUTH_ERROR
:
51 case WBC_ERR_UNKNOWN_USER
:
52 return PAM_USER_UNKNOWN
;
53 case WBC_ERR_UNKNOWN_GROUP
:
54 return PAM_USER_UNKNOWN
;
55 case WBC_ERR_PWD_CHANGE_FAILED
:
63 static const char *_pam_error_code_str(int err
)
69 return "PAM_OPEN_ERR";
71 return "PAM_SYMBOL_ERR";
73 return "PAM_SERVICE_ERR";
75 return "PAM_SYSTEM_ERR";
79 return "PAM_PERM_DENIED";
81 return "PAM_AUTH_ERR";
82 case PAM_CRED_INSUFFICIENT
:
83 return "PAM_CRED_INSUFFICIENT";
84 case PAM_AUTHINFO_UNAVAIL
:
85 return "PAM_AUTHINFO_UNAVAIL";
86 case PAM_USER_UNKNOWN
:
87 return "PAM_USER_UNKNOWN";
89 return "PAM_MAXTRIES";
90 case PAM_NEW_AUTHTOK_REQD
:
91 return "PAM_NEW_AUTHTOK_REQD";
92 case PAM_ACCT_EXPIRED
:
93 return "PAM_ACCT_EXPIRED";
95 return "PAM_SESSION_ERR";
96 case PAM_CRED_UNAVAIL
:
97 return "PAM_CRED_UNAVAIL";
98 case PAM_CRED_EXPIRED
:
99 return "PAM_CRED_EXPIRED";
101 return "PAM_CRED_ERR";
102 case PAM_NO_MODULE_DATA
:
103 return "PAM_NO_MODULE_DATA";
105 return "PAM_CONV_ERR";
106 case PAM_AUTHTOK_ERR
:
107 return "PAM_AUTHTOK_ERR";
108 case PAM_AUTHTOK_RECOVER_ERR
:
109 return "PAM_AUTHTOK_RECOVER_ERR";
110 case PAM_AUTHTOK_LOCK_BUSY
:
111 return "PAM_AUTHTOK_LOCK_BUSY";
112 case PAM_AUTHTOK_DISABLE_AGING
:
113 return "PAM_AUTHTOK_DISABLE_AGING";
115 return "PAM_TRY_AGAIN";
120 case PAM_AUTHTOK_EXPIRED
:
121 return "PAM_AUTHTOK_EXPIRED";
122 #ifdef PAM_MODULE_UNKNOWN
123 case PAM_MODULE_UNKNOWN
:
124 return "PAM_MODULE_UNKNOWN";
128 return "PAM_BAD_ITEM";
130 #ifdef PAM_CONV_AGAIN
132 return "PAM_CONV_AGAIN";
134 #ifdef PAM_INCOMPLETE
136 return "PAM_INCOMPLETE";
143 #define _PAM_LOG_FUNCTION_ENTER(function, ctx) \
145 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] ENTER: " \
146 function " (flags: 0x%04x)", ctx->pamh, ctx->flags); \
147 _pam_log_state(ctx); \
150 #define _PAM_LOG_FUNCTION_LEAVE(function, ctx, retval) \
152 _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] LEAVE: " \
153 function " returning %d (%s)", ctx ? ctx->pamh : NULL, retval, \
154 _pam_error_code_str(retval)); \
155 _pam_log_state(ctx); \
160 #define MAX_PASSWD_TRIES 3
163 static char initialized
= 0;
165 static inline void textdomain_init(void);
166 static inline void textdomain_init(void)
169 bindtextdomain(MODULE_NAME
, LOCALEDIR
);
177 /* some syslogging */
178 static void _pam_log_int(const pam_handle_t
*pamh
,
181 va_list args
) PRINTF_ATTRIBUTE(3, 0);
183 #ifdef HAVE_PAM_VSYSLOG
184 static void _pam_log_int(const pam_handle_t
*pamh
,
189 pam_vsyslog(pamh
, err
, format
, args
);
192 static void _pam_log_int(const pam_handle_t
*pamh
,
202 va_copy(args2
, args
);
204 pam_get_item(pamh
, PAM_SERVICE
, (const void **) &service
);
206 ret
= vasprintf(&base
, format
, args
);
208 /* what else todo ? */
209 vsyslog(err
, format
, args2
);
214 syslog(err
, "%s(%s): %s", MODULE_NAME
, service
, base
);
218 #endif /* HAVE_PAM_VSYSLOG */
220 static bool _pam_log_is_silent(int ctrl
)
222 return on(ctrl
, WINBIND_SILENT
);
225 static void _pam_log(struct pwb_context
*r
, int err
, const char *format
, ...) PRINTF_ATTRIBUTE(3,4);
226 static void _pam_log(struct pwb_context
*r
, int err
, const char *format
, ...)
230 if (_pam_log_is_silent(r
->ctrl
)) {
234 va_start(args
, format
);
235 _pam_log_int(r
->pamh
, err
, format
, args
);
238 static void __pam_log(const pam_handle_t
*pamh
, int ctrl
, int err
, const char *format
, ...) PRINTF_ATTRIBUTE(4,5);
239 static void __pam_log(const pam_handle_t
*pamh
, int ctrl
, int err
, const char *format
, ...)
243 if (_pam_log_is_silent(ctrl
)) {
247 va_start(args
, format
);
248 _pam_log_int(pamh
, err
, format
, args
);
252 static bool _pam_log_is_debug_enabled(int ctrl
)
258 if (_pam_log_is_silent(ctrl
)) {
262 if (!(ctrl
& WINBIND_DEBUG_ARG
)) {
269 static bool _pam_log_is_debug_state_enabled(int ctrl
)
271 if (!(ctrl
& WINBIND_DEBUG_STATE
)) {
275 return _pam_log_is_debug_enabled(ctrl
);
278 static void _pam_log_debug(struct pwb_context
*r
, int err
, const char *format
, ...) PRINTF_ATTRIBUTE(3,4);
279 static void _pam_log_debug(struct pwb_context
*r
, int err
, const char *format
, ...)
283 if (!r
|| !_pam_log_is_debug_enabled(r
->ctrl
)) {
287 va_start(args
, format
);
288 _pam_log_int(r
->pamh
, err
, format
, args
);
291 static void __pam_log_debug(const pam_handle_t
*pamh
, int ctrl
, int err
, const char *format
, ...) PRINTF_ATTRIBUTE(4,5);
292 static void __pam_log_debug(const pam_handle_t
*pamh
, int ctrl
, int err
, const char *format
, ...)
296 if (!_pam_log_is_debug_enabled(ctrl
)) {
300 va_start(args
, format
);
301 _pam_log_int(pamh
, err
, format
, args
);
305 static void _pam_log_state_datum(struct pwb_context
*ctx
,
310 const void *data
= NULL
;
311 if (item_type
!= 0) {
312 pam_get_item(ctx
->pamh
, item_type
, &data
);
314 pam_get_data(ctx
->pamh
, key
, &data
);
317 const char *type
= (item_type
!= 0) ? "ITEM" : "DATA";
318 if (is_string
!= 0) {
319 _pam_log_debug(ctx
, LOG_DEBUG
,
320 "[pamh: %p] STATE: %s(%s) = \"%s\" (%p)",
321 ctx
->pamh
, type
, key
, (const char *)data
,
324 _pam_log_debug(ctx
, LOG_DEBUG
,
325 "[pamh: %p] STATE: %s(%s) = %p",
326 ctx
->pamh
, type
, key
, data
);
331 #define _PAM_LOG_STATE_DATA_POINTER(ctx, module_data_name) \
332 _pam_log_state_datum(ctx, 0, module_data_name, 0)
334 #define _PAM_LOG_STATE_DATA_STRING(ctx, module_data_name) \
335 _pam_log_state_datum(ctx, 0, module_data_name, 1)
337 #define _PAM_LOG_STATE_ITEM_POINTER(ctx, item_type) \
338 _pam_log_state_datum(ctx, item_type, #item_type, 0)
340 #define _PAM_LOG_STATE_ITEM_STRING(ctx, item_type) \
341 _pam_log_state_datum(ctx, item_type, #item_type, 1)
343 #ifdef DEBUG_PASSWORD
344 #define _LOG_PASSWORD_AS_STRING 1
346 #define _LOG_PASSWORD_AS_STRING 0
349 #define _PAM_LOG_STATE_ITEM_PASSWORD(ctx, item_type) \
350 _pam_log_state_datum(ctx, item_type, #item_type, \
351 _LOG_PASSWORD_AS_STRING)
353 * wrapper to preserve old behaviour of iniparser which ignored
354 * key values that had no value assigned like
356 * for a key like above newer iniparser will return a zero-length
357 * string, previously iniparser would return NULL
359 * JRA: For compatibility, tiniparser behaves like iniparser.
361 static const char *tiniparser_getstring_nonempty(struct tiniparser_dictionary
*d
,
365 const char *ret
= tiniparser_getstring(d
, key
, def
);
366 if (ret
&& strlen(ret
) == 0) {
372 static void _pam_log_state(struct pwb_context
*ctx
)
374 if (!ctx
|| !_pam_log_is_debug_state_enabled(ctx
->ctrl
)) {
378 _PAM_LOG_STATE_ITEM_STRING(ctx
, PAM_SERVICE
);
379 _PAM_LOG_STATE_ITEM_STRING(ctx
, PAM_USER
);
380 _PAM_LOG_STATE_ITEM_STRING(ctx
, PAM_TTY
);
381 _PAM_LOG_STATE_ITEM_STRING(ctx
, PAM_RHOST
);
382 _PAM_LOG_STATE_ITEM_STRING(ctx
, PAM_RUSER
);
383 _PAM_LOG_STATE_ITEM_PASSWORD(ctx
, PAM_OLDAUTHTOK
);
384 _PAM_LOG_STATE_ITEM_PASSWORD(ctx
, PAM_AUTHTOK
);
385 _PAM_LOG_STATE_ITEM_STRING(ctx
, PAM_USER_PROMPT
);
386 _PAM_LOG_STATE_ITEM_POINTER(ctx
, PAM_CONV
);
387 #ifdef PAM_FAIL_DELAY
388 _PAM_LOG_STATE_ITEM_POINTER(ctx
, PAM_FAIL_DELAY
);
390 #ifdef PAM_REPOSITORY
391 _PAM_LOG_STATE_ITEM_POINTER(ctx
, PAM_REPOSITORY
);
394 _PAM_LOG_STATE_DATA_STRING(ctx
, PAM_WINBIND_HOMEDIR
);
395 _PAM_LOG_STATE_DATA_STRING(ctx
, PAM_WINBIND_LOGONSCRIPT
);
396 _PAM_LOG_STATE_DATA_STRING(ctx
, PAM_WINBIND_LOGONSERVER
);
397 _PAM_LOG_STATE_DATA_STRING(ctx
, PAM_WINBIND_PROFILEPATH
);
398 _PAM_LOG_STATE_DATA_STRING(ctx
,
399 PAM_WINBIND_NEW_AUTHTOK_REQD
);
400 /* Use atoi to get PAM result code */
401 _PAM_LOG_STATE_DATA_STRING(ctx
,
402 PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH
);
403 _PAM_LOG_STATE_DATA_POINTER(ctx
, PAM_WINBIND_PWD_LAST_SET
);
406 static int _pam_parse(const pam_handle_t
*pamh
,
410 enum pam_winbind_request_type type
,
411 struct tiniparser_dictionary
**result_d
)
414 const char *config_file
= NULL
;
417 struct tiniparser_dictionary
*d
= NULL
;
419 if (flags
& PAM_SILENT
) {
420 ctrl
|= WINBIND_SILENT
;
423 for (i
=argc
,v
=argv
; i
-- > 0; ++v
) {
424 if (!strncasecmp(*v
, "config", strlen("config"))) {
425 ctrl
|= WINBIND_CONFIG_FILE
;
431 if (config_file
== NULL
) {
432 config_file
= PAM_WINBIND_CONFIG_FILE
;
435 d
= tiniparser_load(config_file
);
437 goto config_from_pam
;
440 if (tiniparser_getboolean(d
, "global:debug", false)) {
441 ctrl
|= WINBIND_DEBUG_ARG
;
444 if (tiniparser_getboolean(d
, "global:debug_state", false)) {
445 ctrl
|= WINBIND_DEBUG_STATE
;
448 if (tiniparser_getboolean(d
, "global:cached_login", false)) {
449 ctrl
|= WINBIND_CACHED_LOGIN
;
452 if (tiniparser_getboolean(d
, "global:krb5_auth", false)) {
453 ctrl
|= WINBIND_KRB5_AUTH
;
456 if (tiniparser_getboolean(d
, "global:silent", false)) {
457 ctrl
|= WINBIND_SILENT
;
460 if (tiniparser_getstring_nonempty(d
, "global:krb5_ccache_type", NULL
) != NULL
) {
461 ctrl
|= WINBIND_KRB5_CCACHE_TYPE
;
464 if ((tiniparser_getstring_nonempty(d
, "global:require-membership-of", NULL
)
466 (tiniparser_getstring_nonempty(d
, "global:require_membership_of", NULL
)
468 ctrl
|= WINBIND_REQUIRED_MEMBERSHIP
;
471 if (tiniparser_getboolean(d
, "global:try_first_pass", false)) {
472 ctrl
|= WINBIND_TRY_FIRST_PASS_ARG
;
475 if (tiniparser_getint(d
, "global:warn_pwd_expire", 0)) {
476 ctrl
|= WINBIND_WARN_PWD_EXPIRE
;
479 if (tiniparser_getboolean(d
, "global:mkhomedir", false)) {
480 ctrl
|= WINBIND_MKHOMEDIR
;
483 if (tiniparser_getboolean(d
, "global:pwd_change_prompt", false)) {
484 ctrl
|= WINBIND_PWD_CHANGE_PROMPT
;
488 /* step through arguments */
489 for (i
=argc
,v
=argv
; i
-- > 0; ++v
) {
491 /* generic options */
492 if (!strcmp(*v
,"debug"))
493 ctrl
|= WINBIND_DEBUG_ARG
;
494 else if (!strcasecmp(*v
, "debug_state"))
495 ctrl
|= WINBIND_DEBUG_STATE
;
496 else if (!strcasecmp(*v
, "silent"))
497 ctrl
|= WINBIND_SILENT
;
498 else if (!strcasecmp(*v
, "use_authtok"))
499 ctrl
|= WINBIND_USE_AUTHTOK_ARG
;
500 else if (!strcasecmp(*v
, "try_authtok"))
501 ctrl
|= WINBIND_TRY_AUTHTOK_ARG
;
502 else if (!strcasecmp(*v
, "use_first_pass"))
503 ctrl
|= WINBIND_USE_FIRST_PASS_ARG
;
504 else if (!strcasecmp(*v
, "try_first_pass"))
505 ctrl
|= WINBIND_TRY_FIRST_PASS_ARG
;
506 else if (!strcasecmp(*v
, "unknown_ok"))
507 ctrl
|= WINBIND_UNKNOWN_OK_ARG
;
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 ((type
== PAM_WINBIND_AUTHENTICATE
514 || type
== PAM_WINBIND_SETCRED
)
515 && !strncasecmp(*v
, "require-membership-of",
516 strlen("require-membership-of")))
517 ctrl
|= WINBIND_REQUIRED_MEMBERSHIP
;
518 else if (!strcasecmp(*v
, "krb5_auth"))
519 ctrl
|= WINBIND_KRB5_AUTH
;
520 else if (!strncasecmp(*v
, "krb5_ccache_type",
521 strlen("krb5_ccache_type")))
522 ctrl
|= WINBIND_KRB5_CCACHE_TYPE
;
523 else if (!strcasecmp(*v
, "cached_login"))
524 ctrl
|= WINBIND_CACHED_LOGIN
;
525 else if (!strcasecmp(*v
, "mkhomedir"))
526 ctrl
|= WINBIND_MKHOMEDIR
;
527 else if (!strncasecmp(*v
, "warn_pwd_expire",
528 strlen("warn_pwd_expire")))
529 ctrl
|= WINBIND_WARN_PWD_EXPIRE
;
530 else if (!strcasecmp(*v
, "pwd_change_prompt"))
531 ctrl
|= WINBIND_PWD_CHANGE_PROMPT
;
532 else if (type
!= PAM_WINBIND_CLEANUP
) {
533 __pam_log(pamh
, ctrl
, LOG_ERR
,
534 "pam_parse: unknown option: %s", *v
);
544 tiniparser_freedict(d
);
551 static int _pam_winbind_free_context(struct pwb_context
*ctx
)
558 tiniparser_freedict(ctx
->dict
);
561 wbcCtxFree(ctx
->wbc_ctx
);
566 static int _pam_winbind_init_context(pam_handle_t
*pamh
,
570 enum pam_winbind_request_type type
,
571 struct pwb_context
**ctx_p
)
573 struct pwb_context
*r
= NULL
;
574 const char *service
= NULL
;
575 char service_name
[32] = {0};
582 r
= talloc_zero(NULL
, struct pwb_context
);
587 talloc_set_destructor(r
, _pam_winbind_free_context
);
593 ctrl_code
= _pam_parse(pamh
, flags
, argc
, argv
, type
, &r
->dict
);
594 if (ctrl_code
== -1) {
596 return PAM_SYSTEM_ERR
;
600 r
->wbc_ctx
= wbcCtxCreate();
601 if (r
->wbc_ctx
== NULL
) {
603 return PAM_SYSTEM_ERR
;
606 pam_get_item(pamh
, PAM_SERVICE
, (const void **)&service
);
608 snprintf(service_name
, sizeof(service_name
), "PAM_WINBIND[%s]", service
);
610 wbcSetClientProcessName(service_name
);
617 static void _pam_winbind_cleanup_func(pam_handle_t
*pamh
,
621 int ctrl
= _pam_parse(pamh
, 0, 0, NULL
, PAM_WINBIND_CLEANUP
, NULL
);
622 if (_pam_log_is_debug_state_enabled(ctrl
)) {
623 __pam_log_debug(pamh
, ctrl
, LOG_DEBUG
,
624 "[pamh: %p] CLEAN: cleaning up PAM data %p "
625 "(error_status = %d)", pamh
, data
,
632 static const struct ntstatus_errors
{
633 const char *ntstatus_string
;
634 const char *error_string
;
635 } ntstatus_errors
[] = {
638 {"NT_STATUS_BACKUP_CONTROLLER",
639 N_("No primary Domain Controller available")},
640 {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
641 N_("No domain controllers found")},
642 {"NT_STATUS_NO_LOGON_SERVERS",
643 N_("No logon servers")},
644 {"NT_STATUS_PWD_TOO_SHORT",
645 N_("Password too short")},
646 {"NT_STATUS_PWD_TOO_RECENT",
647 N_("The password was recently changed and cannot be changed again before %s")},
648 {"NT_STATUS_PWD_HISTORY_CONFLICT",
649 N_("Password is already in password history")},
650 {"NT_STATUS_PASSWORD_EXPIRED",
651 N_("Your password has expired")},
652 {"NT_STATUS_PASSWORD_MUST_CHANGE",
653 N_("You need to change your password now")},
654 {"NT_STATUS_INVALID_WORKSTATION",
655 N_("You are not allowed to logon from this workstation")},
656 {"NT_STATUS_INVALID_LOGON_HOURS",
657 N_("You are not allowed to logon at this time")},
658 {"NT_STATUS_ACCOUNT_EXPIRED",
659 N_("Your account has expired. "
660 "Please contact your System administrator")}, /* SCNR */
661 {"NT_STATUS_ACCOUNT_DISABLED",
662 N_("Your account is disabled. "
663 "Please contact your System administrator")}, /* SCNR */
664 {"NT_STATUS_ACCOUNT_LOCKED_OUT",
665 N_("Your account has been locked. "
666 "Please contact your System administrator")}, /* SCNR */
667 {"NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
668 N_("Invalid Trust Account")},
669 {"NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
670 N_("Invalid Trust Account")},
671 {"NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
672 N_("Invalid Trust Account")},
673 {"NT_STATUS_ACCESS_DENIED",
674 N_("Access is denied")},
678 static const char *_get_ntstatus_error_string(const char *nt_status_string
)
681 for (i
=0; ntstatus_errors
[i
].ntstatus_string
!= NULL
; i
++) {
682 if (!strcasecmp(ntstatus_errors
[i
].ntstatus_string
,
684 return _(ntstatus_errors
[i
].error_string
);
690 /* --- authentication management functions --- */
692 /* Attempt a conversation */
694 static int converse(const pam_handle_t
*pamh
,
696 const struct pam_message
**message
,
697 struct pam_response
**response
)
700 const struct pam_conv
*conv
;
702 retval
= pam_get_item(pamh
, PAM_CONV
, (const void **) &conv
);
703 if (retval
== PAM_SUCCESS
) {
704 retval
= conv
->conv(nargs
,
705 discard_const_p(const struct pam_message
*, message
),
706 response
, conv
->appdata_ptr
);
709 return retval
; /* propagate error status */
713 static int _make_remark(struct pwb_context
*ctx
,
717 int retval
= PAM_SUCCESS
;
719 const struct pam_message
*pmsg
[1];
720 struct pam_message msg
[1];
721 struct pam_response
*resp
;
723 if (ctx
->flags
& WINBIND_SILENT
) {
728 msg
[0].msg
= discard_const_p(char, text
);
729 msg
[0].msg_style
= type
;
732 retval
= converse(ctx
->pamh
, 1, pmsg
, &resp
);
735 _pam_drop_reply(resp
, 1);
740 static int _make_remark_v(struct pwb_context
*ctx
,
743 va_list args
) PRINTF_ATTRIBUTE(3, 0);
745 static int _make_remark_v(struct pwb_context
*ctx
,
753 ret
= vasprintf(&var
, format
, args
);
755 _pam_log(ctx
, LOG_ERR
, "memory allocation failure");
759 ret
= _make_remark(ctx
, type
, var
);
764 static int _make_remark_format(struct pwb_context
*ctx
, int type
, const char *format
, ...) PRINTF_ATTRIBUTE(3,4);
765 static int _make_remark_format(struct pwb_context
*ctx
, int type
, const char *format
, ...)
770 va_start(args
, format
);
771 ret
= _make_remark_v(ctx
, type
, format
, args
);
776 static int pam_winbind_request_log(struct pwb_context
*ctx
,
783 /* incorrect password */
784 _pam_log(ctx
, LOG_WARNING
, "user '%s' denied access "
785 "(incorrect password or invalid membership)", user
);
787 case PAM_ACCT_EXPIRED
:
788 /* account expired */
789 _pam_log(ctx
, LOG_WARNING
, "user '%s' account expired",
792 case PAM_AUTHTOK_EXPIRED
:
793 /* password expired */
794 _pam_log(ctx
, LOG_WARNING
, "user '%s' password expired",
797 case PAM_NEW_AUTHTOK_REQD
:
798 /* new password required */
799 _pam_log(ctx
, LOG_WARNING
, "user '%s' new password "
802 case PAM_USER_UNKNOWN
:
803 /* the user does not exist */
804 _pam_log_debug(ctx
, LOG_NOTICE
, "user '%s' not found",
806 if (ctx
->ctrl
& WINBIND_UNKNOWN_OK_ARG
) {
810 case PAM_AUTHTOK_ERR
:
811 /* Authentication token manipulation error */
812 _pam_log(ctx
, LOG_WARNING
, "user `%s' authentication token change failed "
813 "(pwd complexity/history/min_age not met?)", user
);
816 /* Otherwise, the authentication looked good */
817 if (strcmp(fn
, "wbcLogonUser") == 0) {
818 _pam_log(ctx
, LOG_NOTICE
,
819 "user '%s' granted access", user
);
821 _pam_log(ctx
, LOG_NOTICE
,
822 "user '%s' OK", user
);
826 /* we don't know anything about this return value */
827 _pam_log(ctx
, LOG_ERR
,
828 "internal module error (retval = %s(%d), user = '%s')",
829 _pam_error_code_str(retval
), retval
, user
);
834 static int wbc_auth_error_to_pam_error(struct pwb_context
*ctx
,
835 struct wbcAuthErrorInfo
*e
,
837 const char *username
,
840 int ret
= PAM_AUTH_ERR
;
842 if (WBC_ERROR_IS_OK(status
)) {
843 _pam_log_debug(ctx
, LOG_DEBUG
, "request %s succeeded",
846 return pam_winbind_request_log(ctx
, ret
, username
, fn
);
850 if (e
->pam_error
!= PAM_SUCCESS
) {
851 _pam_log(ctx
, LOG_ERR
,
852 "request %s failed: %s, "
853 "PAM error: %s (%d), NTSTATUS: %s, "
854 "Error message was: %s",
856 wbcErrorString(status
),
857 _pam_error_code_str(e
->pam_error
),
862 return pam_winbind_request_log(ctx
, ret
, username
, fn
);
865 _pam_log(ctx
, LOG_ERR
, "request %s failed, but PAM error 0!", fn
);
867 ret
= PAM_SERVICE_ERR
;
868 return pam_winbind_request_log(ctx
, ret
, username
, fn
);
871 ret
= wbc_error_to_pam_error(status
);
872 _pam_log(ctx
, LOG_ERR
,
873 "request %s failed: %s, PAM error: %s (%d)!",
874 fn
, wbcErrorString(status
),
875 _pam_error_code_str(ret
), ret
);
876 return pam_winbind_request_log(ctx
, ret
, username
, fn
);
879 #if defined(HAVE_PAM_RADIO_TYPE)
880 static bool _pam_winbind_change_pwd(struct pwb_context
*ctx
)
882 struct pam_message msg
;
883 const struct pam_message
*pmsg
;
884 struct pam_response
*resp
= NULL
;
888 msg
.msg_style
= PAM_RADIO_TYPE
;
889 msg
.msg
= _("Do you want to change your password now?");
890 ret
= converse(ctx
->pamh
, 1, &pmsg
, &resp
);
892 if (ret
== PAM_SUCCESS
) {
893 _pam_log(ctx
, LOG_CRIT
, "pam_winbind: system error!\n");
897 if (ret
!= PAM_SUCCESS
) {
900 _pam_log(ctx
, LOG_CRIT
, "Received [%s] reply from application.\n", resp
->resp
);
902 if ((resp
->resp
!= NULL
) && (strcasecmp(resp
->resp
, "yes") == 0)) {
906 _pam_drop_reply(resp
, 1);
910 static bool _pam_winbind_change_pwd(struct pwb_context
*ctx
)
917 * send a password expiry message if required
919 * @param ctx PAM winbind context.
920 * @param next_change expected (calculated) next expiry date.
921 * @param already_expired pointer to a boolean to indicate if the password is
924 * @return boolean Returns true if message has been sent, false if not.
927 static bool _pam_send_password_expiry_message(struct pwb_context
*ctx
,
931 bool *already_expired
,
935 struct tm tm_now
, tm_next_change
;
939 if (already_expired
) {
940 *already_expired
= false;
947 if (next_change
<= now
) {
948 PAM_WB_REMARK_DIRECT(ctx
, "NT_STATUS_PASSWORD_EXPIRED");
949 if (already_expired
) {
950 *already_expired
= true;
955 if ((next_change
< 0) ||
956 (next_change
> now
+ warn_pwd_expire
* SECONDS_PER_DAY
)) {
960 if ((localtime_r(&now
, &tm_now
) == NULL
) ||
961 (localtime_r(&next_change
, &tm_next_change
) == NULL
)) {
965 days
= (tm_next_change
.tm_yday
+tm_next_change
.tm_year
*365) -
966 (tm_now
.tm_yday
+tm_now
.tm_year
*365);
969 ret
= _make_remark(ctx
, PAM_TEXT_INFO
,
970 _("Your password expires today.\n"));
973 * If change_pwd and already_expired is null.
974 * We are just sending a notification message.
975 * We don't expect any response in this case.
978 if (!change_pwd
&& !already_expired
) {
983 * successfully sent the warning message.
984 * Give the user a chance to change pwd.
986 if (ret
== PAM_SUCCESS
&&
987 (ctx
->ctrl
& WINBIND_PWD_CHANGE_PROMPT
)) {
989 retval
= _pam_winbind_change_pwd(ctx
);
998 if (days
> 0 && days
< warn_pwd_expire
) {
1000 ret
= _make_remark_format(ctx
, PAM_TEXT_INFO
,
1001 _("Your password will expire in %d %s.\n"),
1002 days
, (days
> 1) ? _("days"):_("day"));
1004 * If change_pwd and already_expired is null.
1005 * We are just sending a notification message.
1006 * We don't expect any response in this case.
1009 if (!change_pwd
&& !already_expired
) {
1014 * successfully sent the warning message.
1015 * Give the user a chance to change pwd.
1017 if (ret
== PAM_SUCCESS
&&
1018 (ctx
->ctrl
& WINBIND_PWD_CHANGE_PROMPT
)) {
1020 retval
= _pam_winbind_change_pwd(ctx
);
1033 * Send a warning if the password expires in the near future
1035 * @param ctx PAM winbind context.
1036 * @param response The full authentication response structure.
1037 * @param already_expired boolean, is the pwd already expired?
1042 static void _pam_warn_password_expiry(struct pwb_context
*ctx
,
1043 const struct wbcAuthUserInfo
*info
,
1044 int warn_pwd_expire
,
1045 bool *already_expired
,
1048 time_t now
= time(NULL
);
1049 time_t next_change
= 0;
1055 if (already_expired
) {
1056 *already_expired
= false;
1060 *change_pwd
= false;
1063 /* accounts with WBC_ACB_PWNOEXP set never receive a warning */
1064 if (info
->acct_flags
& WBC_ACB_PWNOEXP
) {
1068 /* no point in sending a warning if this is a grace logon */
1069 if (PAM_WB_GRACE_LOGON(info
->user_flags
)) {
1073 /* check if the info3 must change timestamp has been set */
1074 next_change
= info
->pass_must_change_time
;
1076 if (_pam_send_password_expiry_message(ctx
, next_change
, now
,
1083 /* no warning sent */
1086 #define IS_SID_STRING(name) (strncmp("S-", name, 2) == 0)
1089 * Append a string, making sure not to overflow and to always return a
1090 * NULL-terminated string.
1092 * @param dest Destination string buffer (must already be NULL-terminated).
1093 * @param src Source string buffer.
1094 * @param dest_buffer_size Size of dest buffer in bytes.
1096 * @return false if dest buffer is not big enough (no bytes copied), true on
1100 static bool safe_append_string(char *dest
,
1102 int dest_buffer_size
)
1105 len
= strlcat(dest
, src
, dest_buffer_size
);
1106 return (len
< dest_buffer_size
);
1110 * Convert a names into a SID string, appending it to a buffer.
1112 * @param ctx PAM winbind context.
1113 * @param user User in PAM request.
1114 * @param name Name to convert.
1115 * @param sid_list_buffer Where to append the string sid.
1116 * @param sid_list_buffer Size of sid_list_buffer (in bytes).
1118 * @return false on failure, true on success.
1120 static bool winbind_name_to_sid_string(struct pwb_context
*ctx
,
1123 char *sid_list_buffer
,
1124 int sid_list_buffer_size
)
1126 char sid_string
[WBC_SID_STRING_BUFLEN
];
1129 if (IS_SID_STRING(name
)) {
1130 strlcpy(sid_string
, name
, sizeof(sid_string
));
1133 struct wbcDomainSid sid
;
1134 enum wbcSidType type
;
1136 _pam_log_debug(ctx
, LOG_DEBUG
,
1137 "no sid given, looking up: %s\n", name
);
1139 wbc_status
= wbcCtxLookupName(ctx
->wbc_ctx
,
1144 if (!WBC_ERROR_IS_OK(wbc_status
)) {
1145 _pam_log(ctx
, LOG_INFO
,
1146 "could not lookup name: %s\n", name
);
1150 wbcSidToStringBuf(&sid
, sid_string
, sizeof(sid_string
));
1153 if (!safe_append_string(sid_list_buffer
, sid_string
,
1154 sid_list_buffer_size
)) {
1161 * Convert a list of names into a list of sids.
1163 * @param ctx PAM winbind context.
1164 * @param user User in PAM request.
1165 * @param name_list List of names or string sids, separated by commas.
1166 * @param sid_list_buffer Where to put the list of string sids.
1167 * @param sid_list_buffer Size of sid_list_buffer (in bytes).
1169 * @return false on failure, true on success.
1171 static bool winbind_name_list_to_sid_string_list(struct pwb_context
*ctx
,
1173 const char *name_list
,
1174 char *sid_list_buffer
,
1175 int sid_list_buffer_size
)
1177 bool result
= false;
1178 char *current_name
= NULL
;
1179 const char *search_location
;
1183 if (sid_list_buffer_size
> 0) {
1184 sid_list_buffer
[0] = 0;
1187 search_location
= name_list
;
1188 while ((comma
= strchr(search_location
, ',')) != NULL
) {
1189 current_name
= strndup(search_location
,
1190 comma
- search_location
);
1191 if (NULL
== current_name
) {
1195 if (!winbind_name_to_sid_string(ctx
, user
,
1198 sid_list_buffer_size
)) {
1200 * If one group name failed, we must not fail
1201 * the authentication totally, continue with
1202 * the following group names. If user belongs to
1203 * one of the valid groups, we must allow it
1207 _pam_log(ctx
, LOG_INFO
, "cannot convert group %s to sid, "
1208 "check if group %s is valid group.", current_name
,
1210 _make_remark_format(ctx
, PAM_TEXT_INFO
, _("Cannot convert group %s "
1211 "to sid, please contact your administrator to see "
1212 "if group %s is valid."), current_name
, current_name
);
1213 SAFE_FREE(current_name
);
1214 search_location
= comma
+ 1;
1218 SAFE_FREE(current_name
);
1220 if (!safe_append_string(sid_list_buffer
, ",",
1221 sid_list_buffer_size
)) {
1225 search_location
= comma
+ 1;
1228 if (!winbind_name_to_sid_string(ctx
, user
, search_location
,
1230 sid_list_buffer_size
)) {
1231 _pam_log(ctx
, LOG_INFO
, "cannot convert group %s to sid, "
1232 "check if group %s is valid group.", search_location
,
1234 _make_remark_format(ctx
, PAM_TEXT_INFO
, _("Cannot convert group %s "
1235 "to sid, please contact your administrator to see "
1236 "if group %s is valid."), search_location
, search_location
);
1238 /* If no valid groups were converted we should fail outright */
1239 if (name_list
!= NULL
&& strlen(sid_list_buffer
) == 0) {
1244 * The lookup of the last name failed..
1245 * It results in require_member_of_sid ends with ','
1246 * It is malformatted parameter here, overwrite the last ','.
1248 len
= strlen(sid_list_buffer
);
1249 if ((len
!= 0) && (sid_list_buffer
[len
- 1] == ',')) {
1250 sid_list_buffer
[len
- 1] = '\0';
1257 SAFE_FREE(current_name
);
1262 * put krb5ccname variable into environment
1264 * @param ctx PAM winbind context.
1265 * @param krb5ccname env variable retrieved from winbindd.
1270 static void _pam_setup_krb5_env(struct pwb_context
*ctx
,
1271 struct wbcLogonUserInfo
*info
)
1276 const char *krb5ccname
= NULL
;
1278 if (off(ctx
->ctrl
, WINBIND_KRB5_AUTH
)) {
1286 for (i
=0; i
< info
->num_blobs
; i
++) {
1287 if (strcasecmp(info
->blobs
[i
].name
, "krb5ccname") == 0) {
1288 krb5ccname
= (const char *)info
->blobs
[i
].blob
.data
;
1293 if (!krb5ccname
|| (strlen(krb5ccname
) == 0)) {
1297 _pam_log_debug(ctx
, LOG_DEBUG
,
1298 "request returned KRB5CCNAME: %s", krb5ccname
);
1300 if (asprintf(&var
, "KRB5CCNAME=%s", krb5ccname
) == -1) {
1304 ret
= pam_putenv(ctx
->pamh
, var
);
1305 if (ret
!= PAM_SUCCESS
) {
1306 _pam_log(ctx
, LOG_ERR
,
1307 "failed to set KRB5CCNAME to %s: %s",
1308 var
, pam_strerror(ctx
->pamh
, ret
));
1314 * Copy unix username if available (further processed in PAM).
1316 * @param ctx PAM winbind context
1317 * @param user_ret A pointer that holds a pointer to a string
1318 * @param unix_username A username
1323 static void _pam_setup_unix_username(struct pwb_context
*ctx
,
1325 struct wbcLogonUserInfo
*info
)
1327 const char *unix_username
= NULL
;
1330 if (!user_ret
|| !info
) {
1334 for (i
=0; i
< info
->num_blobs
; i
++) {
1335 if (strcasecmp(info
->blobs
[i
].name
, "unix_username") == 0) {
1336 unix_username
= (const char *)info
->blobs
[i
].blob
.data
;
1341 if (!unix_username
|| !unix_username
[0]) {
1345 *user_ret
= strdup(unix_username
);
1349 * Set string into the PAM stack.
1351 * @param ctx PAM winbind context.
1352 * @param data_name Key name for pam_set_data.
1353 * @param value String value.
1358 static void _pam_set_data_string(struct pwb_context
*ctx
,
1359 const char *data_name
,
1364 if (!data_name
|| !value
|| (strlen(data_name
) == 0) ||
1365 (strlen(value
) == 0)) {
1369 ret
= pam_set_data(ctx
->pamh
, data_name
, talloc_strdup(NULL
, value
),
1370 _pam_winbind_cleanup_func
);
1371 if (ret
!= PAM_SUCCESS
) {
1372 _pam_log_debug(ctx
, LOG_DEBUG
,
1373 "Could not set data %s: %s\n",
1374 data_name
, pam_strerror(ctx
->pamh
, ret
));
1379 * Set info3 strings into the PAM stack.
1381 * @param ctx PAM winbind context.
1382 * @param data_name Key name for pam_set_data.
1383 * @param value String value.
1388 static void _pam_set_data_info3(struct pwb_context
*ctx
,
1389 const struct wbcAuthUserInfo
*info
)
1392 _pam_set_data_string(ctx
, PAM_WINBIND_HOMEDIR
,
1393 info
->home_directory
);
1394 _pam_set_data_string(ctx
, PAM_WINBIND_LOGONSCRIPT
,
1395 info
->logon_script
);
1396 _pam_set_data_string(ctx
, PAM_WINBIND_LOGONSERVER
,
1397 info
->logon_server
);
1398 _pam_set_data_string(ctx
, PAM_WINBIND_PROFILEPATH
,
1399 info
->profile_path
);
1404 * Free info3 strings in the PAM stack.
1406 * @param pamh PAM handle
1411 static void _pam_free_data_info3(pam_handle_t
*pamh
)
1413 pam_set_data(pamh
, PAM_WINBIND_HOMEDIR
, NULL
, NULL
);
1414 pam_set_data(pamh
, PAM_WINBIND_LOGONSCRIPT
, NULL
, NULL
);
1415 pam_set_data(pamh
, PAM_WINBIND_LOGONSERVER
, NULL
, NULL
);
1416 pam_set_data(pamh
, PAM_WINBIND_PROFILEPATH
, NULL
, NULL
);
1420 * Send PAM_ERROR_MSG for cached or grace logons.
1422 * @param ctx PAM winbind context.
1423 * @param username User in PAM request.
1424 * @param info3_user_flgs Info3 flags containing logon type bits.
1429 static void _pam_warn_logon_type(struct pwb_context
*ctx
,
1430 const char *username
,
1431 uint32_t info3_user_flgs
)
1433 /* inform about logon type */
1434 if (PAM_WB_GRACE_LOGON(info3_user_flgs
)) {
1436 _make_remark(ctx
, PAM_ERROR_MSG
,
1438 "Please change your password as soon you're "
1440 _pam_log_debug(ctx
, LOG_DEBUG
,
1441 "User %s logged on using grace logon\n",
1444 } else if (PAM_WB_CACHED_LOGON(info3_user_flgs
)) {
1446 _make_remark(ctx
, PAM_ERROR_MSG
,
1447 _("Domain Controller unreachable, "
1448 "using cached credentials instead. "
1449 "Network resources may be unavailable"));
1450 _pam_log_debug(ctx
, LOG_DEBUG
,
1451 "User %s logged on using cached credentials\n",
1457 * Send PAM_ERROR_MSG for krb5 errors.
1459 * @param ctx PAM winbind context.
1460 * @param username User in PAM request.
1461 * @param info3_user_flgs Info3 flags containing logon type bits.
1466 static void _pam_warn_krb5_failure(struct pwb_context
*ctx
,
1467 const char *username
,
1468 uint32_t info3_user_flgs
)
1470 if (PAM_WB_KRB5_CLOCK_SKEW(info3_user_flgs
)) {
1471 _make_remark(ctx
, PAM_ERROR_MSG
,
1472 _("Failed to establish your Kerberos Ticket cache "
1473 "due time differences\n"
1474 "with the domain controller. "
1475 "Please verify the system time.\n"));
1476 _pam_log_debug(ctx
, LOG_DEBUG
,
1477 "User %s: Clock skew when getting Krb5 TGT\n",
1482 static bool _pam_check_remark_auth_err(struct pwb_context
*ctx
,
1483 const struct wbcAuthErrorInfo
*e
,
1484 const char *nt_status_string
,
1487 const char *ntstatus
= NULL
;
1488 const char *error_string
= NULL
;
1490 if (!e
|| !pam_err
) {
1494 ntstatus
= e
->nt_string
;
1499 if (strcasecmp(ntstatus
, nt_status_string
) == 0) {
1501 error_string
= _get_ntstatus_error_string(nt_status_string
);
1503 _make_remark(ctx
, PAM_ERROR_MSG
, error_string
);
1504 *pam_err
= e
->pam_error
;
1508 if (e
->display_string
) {
1509 _make_remark(ctx
, PAM_ERROR_MSG
, _(e
->display_string
));
1510 *pam_err
= e
->pam_error
;
1514 _make_remark(ctx
, PAM_ERROR_MSG
, nt_status_string
);
1515 *pam_err
= e
->pam_error
;
1524 * Compose Password Restriction String for a PAM_ERROR_MSG conversation.
1526 * @param i The wbcUserPasswordPolicyInfo struct.
1528 * @return string (caller needs to talloc_free).
1531 static char *_pam_compose_pwd_restriction_string(struct pwb_context
*ctx
,
1532 struct wbcUserPasswordPolicyInfo
*i
)
1540 str
= talloc_asprintf(ctx
, _("Your password "));
1545 if (i
->min_length_password
> 0) {
1546 str
= talloc_asprintf_append(str
,
1547 _("must be at least %d characters; "),
1548 i
->min_length_password
);
1554 if (i
->password_history
> 0) {
1555 str
= talloc_asprintf_append(str
,
1556 _("cannot repeat any of your previous %d "
1558 i
->password_history
);
1564 if (i
->password_properties
& WBC_DOMAIN_PASSWORD_COMPLEX
) {
1565 str
= talloc_asprintf_append(str
,
1566 _("must contain capitals, numerals "
1568 "and cannot contain your account "
1575 str
= talloc_asprintf_append(str
,
1576 _("Please type a different password. "
1577 "Type a password which meets these requirements in "
1578 "both text boxes."));
1590 static int _pam_create_homedir(struct pwb_context
*ctx
,
1591 const char *dirname
,
1596 ret
= mkdir(dirname
, mode
);
1597 if (ret
!= 0 && errno
== EEXIST
) {
1600 ret
= stat(dirname
, &sbuf
);
1602 return PAM_PERM_DENIED
;
1605 if (!S_ISDIR(sbuf
.st_mode
)) {
1606 return PAM_PERM_DENIED
;
1611 _make_remark_format(ctx
, PAM_TEXT_INFO
,
1612 _("Creating directory: %s failed: %s"),
1613 dirname
, strerror(errno
));
1614 _pam_log(ctx
, LOG_ERR
, "could not create dir: %s (%s)",
1615 dirname
, strerror(errno
));
1616 return PAM_PERM_DENIED
;
1622 static int _pam_chown_homedir(struct pwb_context
*ctx
,
1623 const char *dirname
,
1627 if (chown(dirname
, uid
, gid
) != 0) {
1628 _pam_log(ctx
, LOG_ERR
, "failed to chown user homedir: %s (%s)",
1629 dirname
, strerror(errno
));
1630 return PAM_PERM_DENIED
;
1636 static int _pam_mkhomedir(struct pwb_context
*ctx
)
1638 struct passwd
*pwd
= NULL
;
1640 char *create_dir
= NULL
;
1641 char *user_dir
= NULL
;
1643 const char *username
;
1645 char *safe_ptr
= NULL
;
1648 /* Get the username */
1649 ret
= pam_get_user(ctx
->pamh
, &username
, NULL
);
1650 if ((ret
!= PAM_SUCCESS
) || (!username
)) {
1651 _pam_log_debug(ctx
, LOG_DEBUG
, "can not get the username");
1652 return PAM_SERVICE_ERR
;
1655 pwd
= getpwnam(username
);
1657 _pam_log_debug(ctx
, LOG_DEBUG
, "can not get the username");
1658 return PAM_USER_UNKNOWN
;
1660 _pam_log_debug(ctx
, LOG_DEBUG
, "homedir is: %s", pwd
->pw_dir
);
1662 ret
= _pam_create_homedir(ctx
, pwd
->pw_dir
, 0700);
1663 if (ret
== PAM_SUCCESS
) {
1664 ret
= _pam_chown_homedir(ctx
, pwd
->pw_dir
,
1669 if (ret
== PAM_SUCCESS
) {
1673 /* maybe we need to create parent dirs */
1674 create_dir
= talloc_strdup(ctx
, "/");
1679 /* find final directory */
1680 user_dir
= strrchr(pwd
->pw_dir
, '/');
1686 _pam_log(ctx
, LOG_DEBUG
, "final directory: %s", user_dir
);
1690 while ((token
= strtok_r(p
, "/", &safe_ptr
)) != NULL
) {
1696 _pam_log_debug(ctx
, LOG_DEBUG
, "token is %s", token
);
1698 create_dir
= talloc_asprintf_append(create_dir
, "%s/", token
);
1702 _pam_log_debug(ctx
, LOG_DEBUG
, "current_dir is %s", create_dir
);
1704 if (strcmp(token
, user_dir
) == 0) {
1705 _pam_log_debug(ctx
, LOG_DEBUG
, "assuming last directory: %s", token
);
1709 ret
= _pam_create_homedir(ctx
, create_dir
, mode
);
1710 if (ret
!= PAM_SUCCESS
) {
1715 return _pam_chown_homedir(ctx
, create_dir
,
1720 /* talk to winbindd */
1721 static int winbind_auth_request(struct pwb_context
*ctx
,
1726 const int warn_pwd_expire
,
1727 struct wbcAuthErrorInfo
**p_error
,
1728 struct wbcLogonUserInfo
**p_info
,
1729 time_t *pwd_last_set
,
1733 struct wbcLogonUserParams logon
;
1734 char membership_of
[1024];
1735 uid_t user_uid
= -1;
1736 uint32_t flags
= WBFLAG_PAM_INFO3_TEXT
;
1737 struct wbcLogonUserInfo
*info
= NULL
;
1738 struct wbcAuthUserInfo
*user_info
= NULL
;
1739 struct wbcAuthErrorInfo
*error
= NULL
;
1740 int ret
= PAM_AUTH_ERR
;
1742 const char *codes
[] = {
1743 "NT_STATUS_PASSWORD_EXPIRED",
1744 "NT_STATUS_PASSWORD_MUST_CHANGE",
1745 "NT_STATUS_INVALID_WORKSTATION",
1746 "NT_STATUS_INVALID_LOGON_HOURS",
1747 "NT_STATUS_ACCOUNT_EXPIRED",
1748 "NT_STATUS_ACCOUNT_DISABLED",
1749 "NT_STATUS_ACCOUNT_LOCKED_OUT",
1750 "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT",
1751 "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT",
1752 "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT",
1753 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
1754 "NT_STATUS_NO_LOGON_SERVERS",
1755 "NT_STATUS_WRONG_PASSWORD",
1756 "NT_STATUS_ACCESS_DENIED"
1763 /* Krb5 auth always has to go against the KDC of the user's realm */
1765 if (ctx
->ctrl
& WINBIND_KRB5_AUTH
) {
1766 flags
|= WBFLAG_PAM_CONTACT_TRUSTDOM
;
1769 if (ctx
->ctrl
& (WINBIND_KRB5_AUTH
|WINBIND_CACHED_LOGIN
)) {
1770 struct passwd
*pwd
= NULL
;
1772 pwd
= getpwnam(user
);
1774 return PAM_USER_UNKNOWN
;
1776 user_uid
= pwd
->pw_uid
;
1779 if (ctx
->ctrl
& WINBIND_KRB5_AUTH
) {
1781 _pam_log_debug(ctx
, LOG_DEBUG
,
1782 "enabling krb5 login flag\n");
1784 flags
|= WBFLAG_PAM_KRB5
|
1785 WBFLAG_PAM_FALLBACK_AFTER_KRB5
;
1788 if (ctx
->ctrl
& WINBIND_CACHED_LOGIN
) {
1789 _pam_log_debug(ctx
, LOG_DEBUG
,
1790 "enabling cached login flag\n");
1791 flags
|= WBFLAG_PAM_CACHED_LOGIN
;
1796 flags
|= WBFLAG_PAM_UNIX_NAME
;
1799 if (cctype
!= NULL
) {
1800 _pam_log_debug(ctx
, LOG_DEBUG
,
1801 "enabling request for a %s krb5 ccache\n",
1805 if (member
!= NULL
) {
1807 ZERO_STRUCT(membership_of
);
1809 if (!winbind_name_list_to_sid_string_list(ctx
, user
, member
,
1811 sizeof(membership_of
))) {
1812 _pam_log_debug(ctx
, LOG_ERR
,
1813 "failed to serialize membership of sid "
1814 "\"%s\"\n", member
);
1815 return PAM_AUTH_ERR
;
1821 logon
.username
= user
;
1822 logon
.password
= pass
;
1825 wbc_status
= wbcAddNamedBlob(&logon
.num_blobs
,
1829 discard_const_p(uint8_t, cctype
),
1831 if (!WBC_ERROR_IS_OK(wbc_status
)) {
1836 wbc_status
= wbcAddNamedBlob(&logon
.num_blobs
,
1842 if (!WBC_ERROR_IS_OK(wbc_status
)) {
1846 wbc_status
= wbcAddNamedBlob(&logon
.num_blobs
,
1850 (uint8_t *)&user_uid
,
1852 if (!WBC_ERROR_IS_OK(wbc_status
)) {
1857 wbc_status
= wbcAddNamedBlob(&logon
.num_blobs
,
1861 (uint8_t *)membership_of
,
1862 sizeof(membership_of
));
1863 if (!WBC_ERROR_IS_OK(wbc_status
)) {
1868 wbc_status
= wbcCtxLogonUser(ctx
->wbc_ctx
,
1873 ret
= wbc_auth_error_to_pam_error(ctx
, error
, wbc_status
,
1874 user
, "wbcLogonUser");
1875 wbcFreeMemory(logon
.blobs
);
1878 if (info
&& info
->info
) {
1879 user_info
= info
->info
;
1882 if (pwd_last_set
&& user_info
) {
1883 *pwd_last_set
= user_info
->pass_last_set_time
;
1886 if (p_info
&& info
) {
1890 if (p_error
&& error
) {
1891 /* We want to process the error in the caller. */
1896 for (i
=0; i
<ARRAY_SIZE(codes
); i
++) {
1898 if (_pam_check_remark_auth_err(ctx
, error
, codes
[i
], &_ret
)) {
1904 if ((ret
== PAM_SUCCESS
) && user_info
&& info
) {
1906 bool already_expired
= false;
1907 bool change_pwd
= false;
1909 /* warn a user if the password is about to expire soon */
1910 _pam_warn_password_expiry(ctx
, user_info
,
1915 if (already_expired
== true) {
1917 SMB_TIME_T last_set
= user_info
->pass_last_set_time
;
1918 SMB_TIME_T must_set
= user_info
->pass_must_change_time
;
1920 _pam_log_debug(ctx
, LOG_DEBUG
,
1921 "Password has expired "
1922 "(Password was last set: %lld, "
1923 "it must be changed here "
1924 "%lld (now it's: %ld))\n",
1925 (long long int)last_set
,
1926 (long long int)must_set
,
1929 return PAM_AUTHTOK_EXPIRED
;
1933 ret
= PAM_NEW_AUTHTOK_REQD
;
1937 /* inform about logon type */
1938 _pam_warn_logon_type(ctx
, user
, user_info
->user_flags
);
1940 /* inform about krb5 failures */
1941 _pam_warn_krb5_failure(ctx
, user
, user_info
->user_flags
);
1943 /* set some info3 info for other modules in the stack */
1944 _pam_set_data_info3(ctx
, user_info
);
1946 /* put krb5ccname into env */
1947 _pam_setup_krb5_env(ctx
, info
);
1949 /* If winbindd returned a username, return the pointer to it
1951 _pam_setup_unix_username(ctx
, user_ret
, info
);
1955 wbcFreeMemory(logon
.blobs
);
1956 if (info
&& info
->blobs
&& !p_info
) {
1957 wbcFreeMemory(info
->blobs
);
1959 * We set blobs to NULL to prevent a use after free in the
1960 * in the wbcLogonUserInfoDestructor
1964 if (error
&& !p_error
) {
1965 wbcFreeMemory(error
);
1967 if (info
&& !p_info
) {
1968 wbcFreeMemory(info
);
1974 /* talk to winbindd */
1975 static int winbind_chauthtok_request(struct pwb_context
*ctx
,
1977 const char *oldpass
,
1978 const char *newpass
,
1979 time_t pwd_last_set
)
1982 struct wbcChangePasswordParams params
;
1983 struct wbcAuthErrorInfo
*error
= NULL
;
1984 struct wbcUserPasswordPolicyInfo
*policy
= NULL
;
1985 enum wbcPasswordChangeRejectReason reject_reason
= -1;
1989 const char *codes
[] = {
1990 "NT_STATUS_BACKUP_CONTROLLER",
1991 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
1992 "NT_STATUS_NO_LOGON_SERVERS",
1993 "NT_STATUS_ACCESS_DENIED",
1994 "NT_STATUS_PWD_TOO_SHORT", /* TODO: tell the min pwd length ? */
1995 "NT_STATUS_PWD_TOO_RECENT", /* TODO: tell the minage ? */
1996 "NT_STATUS_PWD_HISTORY_CONFLICT" /* TODO: tell the history length ? */
1998 int ret
= PAM_AUTH_ERR
;
2000 ZERO_STRUCT(params
);
2002 if (ctx
->ctrl
& WINBIND_KRB5_AUTH
) {
2003 flags
|= WBFLAG_PAM_KRB5
|
2004 WBFLAG_PAM_CONTACT_TRUSTDOM
;
2007 if (ctx
->ctrl
& WINBIND_CACHED_LOGIN
) {
2008 flags
|= WBFLAG_PAM_CACHED_LOGIN
;
2011 params
.account_name
= user
;
2012 params
.level
= WBC_CHANGE_PASSWORD_LEVEL_PLAIN
;
2013 params
.old_password
.plaintext
= oldpass
;
2014 params
.new_password
.plaintext
= newpass
;
2015 params
.flags
= flags
;
2017 wbc_status
= wbcCtxChangeUserPasswordEx(ctx
->wbc_ctx
,
2022 ret
= wbc_auth_error_to_pam_error(ctx
, error
, wbc_status
,
2023 user
, "wbcChangeUserPasswordEx");
2025 if (WBC_ERROR_IS_OK(wbc_status
)) {
2026 _pam_log(ctx
, LOG_NOTICE
,
2027 "user '%s' password changed", user
);
2032 wbcFreeMemory(policy
);
2036 for (i
=0; i
<ARRAY_SIZE(codes
); i
++) {
2038 if (_pam_check_remark_auth_err(ctx
, error
, codes
[i
], &_ret
)) {
2044 if (!strcasecmp(error
->nt_string
,
2045 "NT_STATUS_PASSWORD_RESTRICTION")) {
2047 char *pwd_restriction_string
= NULL
;
2048 SMB_TIME_T min_pwd_age
= 0;
2051 min_pwd_age
= policy
->min_passwordage
;
2054 /* FIXME: avoid to send multiple PAM messages after another */
2055 switch ((int)reject_reason
) {
2058 case WBC_PWD_CHANGE_NO_ERROR
:
2059 if ((min_pwd_age
> 0) &&
2060 (pwd_last_set
+ min_pwd_age
> time(NULL
))) {
2061 time_t next_change
= pwd_last_set
+ min_pwd_age
;
2062 #if defined(__clang__)
2063 #pragma clang diagnostic push
2064 #pragma clang diagnostic ignored "-Wformat-nonliteral"
2066 _make_remark_format(ctx
, PAM_ERROR_MSG
,
2067 _get_ntstatus_error_string("NT_STATUS_PWD_TOO_RECENT"),
2068 ctime(&next_change
));
2069 #if defined(__clang__)
2070 #pragma clang diagnostic pop
2075 case WBC_PWD_CHANGE_PASSWORD_TOO_SHORT
:
2076 PAM_WB_REMARK_DIRECT(ctx
,
2077 "NT_STATUS_PWD_TOO_SHORT");
2079 case WBC_PWD_CHANGE_PWD_IN_HISTORY
:
2080 PAM_WB_REMARK_DIRECT(ctx
,
2081 "NT_STATUS_PWD_HISTORY_CONFLICT");
2083 case WBC_PWD_CHANGE_NOT_COMPLEX
:
2084 _make_remark(ctx
, PAM_ERROR_MSG
,
2085 _("Password does not meet "
2086 "complexity requirements"));
2089 _pam_log_debug(ctx
, LOG_DEBUG
,
2090 "unknown password change "
2091 "reject reason: %d",
2096 pwd_restriction_string
=
2097 _pam_compose_pwd_restriction_string(ctx
, policy
);
2098 if (pwd_restriction_string
) {
2099 _make_remark(ctx
, PAM_ERROR_MSG
,
2100 pwd_restriction_string
);
2101 TALLOC_FREE(pwd_restriction_string
);
2105 wbcFreeMemory(error
);
2106 wbcFreeMemory(policy
);
2112 * Checks if a user has an account
2115 * 1 = User not found
2119 static int valid_user(struct pwb_context
*ctx
,
2122 /* check not only if the user is available over NSS calls, also make
2123 * sure it's really a winbind user, this is important when stacking PAM
2124 * modules in the 'account' or 'password' facility. */
2127 struct passwd
*pwd
= NULL
;
2128 struct passwd
*wb_pwd
= NULL
;
2130 pwd
= getpwnam(user
);
2135 wbc_status
= wbcCtxGetpwnam(ctx
->wbc_ctx
, user
, &wb_pwd
);
2136 wbcFreeMemory(wb_pwd
);
2137 if (!WBC_ERROR_IS_OK(wbc_status
)) {
2138 _pam_log(ctx
, LOG_DEBUG
, "valid_user: wbcGetpwnam gave %s\n",
2139 wbcErrorString(wbc_status
));
2142 switch (wbc_status
) {
2143 case WBC_ERR_UNKNOWN_USER
:
2144 /* match other insane libwbclient return codes */
2145 case WBC_ERR_WINBIND_NOT_AVAILABLE
:
2146 case WBC_ERR_DOMAIN_NOT_FOUND
:
2147 case WBC_ERR_NOT_MAPPED
:
2149 case WBC_ERR_SUCCESS
:
2157 static char *_pam_delete(register char *xx
)
2165 * obtain a password from the user
2168 static int _winbind_read_password(struct pwb_context
*ctx
,
2170 const char *comment
,
2171 const char *prompt1
,
2172 const char *prompt2
,
2180 _pam_log(ctx
, LOG_DEBUG
, "getting password (0x%08x)", ctrl
);
2183 * make sure nothing inappropriate gets returned
2186 *pass
= token
= NULL
;
2189 * which authentication token are we getting?
2192 if (on(WINBIND__OLD_PASSWORD
, ctrl
)) {
2193 authtok_flag
= PAM_OLDAUTHTOK
;
2195 authtok_flag
= PAM_AUTHTOK
;
2199 * should we obtain the password from a PAM item ?
2202 if (on(WINBIND_TRY_FIRST_PASS_ARG
, ctrl
) ||
2203 on(WINBIND_USE_FIRST_PASS_ARG
, ctrl
)) {
2204 retval
= pam_get_item(ctx
->pamh
,
2206 (const void **) &item
);
2207 if (retval
!= PAM_SUCCESS
) {
2209 _pam_log(ctx
, LOG_ALERT
,
2210 "pam_get_item returned error "
2211 "to unix-read-password");
2213 } else if (item
!= NULL
) { /* we have a password! */
2216 _pam_log(ctx
, LOG_DEBUG
,
2217 "pam_get_item returned a password");
2219 } else if (on(WINBIND_USE_FIRST_PASS_ARG
, ctrl
)) {
2220 return PAM_AUTHTOK_RECOVER_ERR
; /* didn't work */
2221 } else if (on(WINBIND_USE_AUTHTOK_ARG
, ctrl
)
2222 && off(WINBIND__OLD_PASSWORD
, ctrl
)) {
2223 return PAM_AUTHTOK_RECOVER_ERR
;
2227 * getting here implies we will have to get the password from the
2232 struct pam_message msg
[3];
2233 const struct pam_message
*pmsg
[3];
2234 struct pam_response
*resp
;
2237 /* prepare to converse */
2239 if (comment
!= NULL
&& off(ctrl
, WINBIND_SILENT
)) {
2241 msg
[0].msg_style
= PAM_TEXT_INFO
;
2242 msg
[0].msg
= discard_const_p(char, comment
);
2249 msg
[i
].msg_style
= PAM_PROMPT_ECHO_OFF
;
2250 msg
[i
++].msg
= discard_const_p(char, prompt1
);
2253 if (prompt2
!= NULL
) {
2255 msg
[i
].msg_style
= PAM_PROMPT_ECHO_OFF
;
2256 msg
[i
++].msg
= discard_const_p(char, prompt2
);
2259 /* so call the conversation expecting i responses */
2261 retval
= converse(ctx
->pamh
, i
, pmsg
, &resp
);
2263 if (retval
== PAM_SUCCESS
) {
2264 retval
= PAM_AUTHTOK_RECOVER_ERR
;
2268 if (retval
!= PAM_SUCCESS
) {
2269 _pam_drop_reply(resp
, i
);
2273 /* interpret the response */
2275 token
= x_strdup(resp
[i
- replies
].resp
);
2277 _pam_log(ctx
, LOG_NOTICE
,
2278 "could not recover "
2279 "authentication token");
2280 retval
= PAM_AUTHTOK_RECOVER_ERR
;
2285 /* verify that password entered correctly */
2286 if (!resp
[i
- 1].resp
||
2287 strcmp(token
, resp
[i
- 1].resp
)) {
2288 _pam_delete(token
); /* mistyped */
2289 retval
= PAM_AUTHTOK_RECOVER_ERR
;
2290 _make_remark(ctx
, PAM_ERROR_MSG
,
2296 * tidy up the conversation (resp_retcode) is ignored
2297 * -- what is it for anyway? AGM
2299 _pam_drop_reply(resp
, i
);
2303 if (retval
!= PAM_SUCCESS
) {
2304 _pam_log_debug(ctx
, LOG_DEBUG
,
2305 "unable to obtain a password");
2308 /* 'token' is the entered password */
2310 /* we store this password as an item */
2312 retval
= pam_set_item(ctx
->pamh
, authtok_flag
, token
);
2313 _pam_delete(token
); /* clean it up */
2314 if (retval
!= PAM_SUCCESS
||
2315 (retval
= pam_get_item(ctx
->pamh
, authtok_flag
, (const void **) &item
)) != PAM_SUCCESS
) {
2317 _pam_log(ctx
, LOG_CRIT
, "error manipulating password");
2323 item
= NULL
; /* break link to password */
2328 static const char *get_conf_item_string(struct pwb_context
*ctx
,
2333 const char *parm_opt
= NULL
;
2335 if (!(ctx
->ctrl
& config_flag
)) {
2339 /* let the pam opt take precedence over the pam_winbind.conf option */
2340 for (i
=0; i
<ctx
->argc
; i
++) {
2342 if ((strncmp(ctx
->argv
[i
], item
, strlen(item
)) == 0)) {
2345 if ((p
= strchr(ctx
->argv
[i
], '=')) == NULL
) {
2346 _pam_log(ctx
, LOG_INFO
,
2347 "no \"=\" delimiter for \"%s\" found\n",
2351 _pam_log_debug(ctx
, LOG_INFO
,
2352 "PAM config: %s '%s'\n", item
, p
+1);
2360 key
= talloc_asprintf(ctx
, "global:%s", item
);
2365 parm_opt
= tiniparser_getstring_nonempty(ctx
->dict
, key
, NULL
);
2368 _pam_log_debug(ctx
, LOG_INFO
, "CONFIG file: %s '%s'\n",
2375 static int get_config_item_int(struct pwb_context
*ctx
,
2379 int i
, parm_opt
= -1;
2381 if (!(ctx
->ctrl
& config_flag
)) {
2385 /* let the pam opt take precedence over the pam_winbind.conf option */
2386 for (i
= 0; i
< ctx
->argc
; i
++) {
2388 if ((strncmp(ctx
->argv
[i
], item
, strlen(item
)) == 0)) {
2391 if ((p
= strchr(ctx
->argv
[i
], '=')) == NULL
) {
2392 _pam_log(ctx
, LOG_INFO
,
2393 "no \"=\" delimiter for \"%s\" found\n",
2397 parm_opt
= atoi(p
+ 1);
2398 _pam_log_debug(ctx
, LOG_INFO
,
2399 "PAM config: %s '%d'\n",
2408 key
= talloc_asprintf(ctx
, "global:%s", item
);
2413 parm_opt
= tiniparser_getint(ctx
->dict
, key
, -1);
2416 _pam_log_debug(ctx
, LOG_INFO
,
2417 "CONFIG file: %s '%d'\n",
2424 static const char *get_krb5_cc_type_from_config(struct pwb_context
*ctx
)
2426 return get_conf_item_string(ctx
, "krb5_ccache_type",
2427 WINBIND_KRB5_CCACHE_TYPE
);
2430 static const char *get_member_from_config(struct pwb_context
*ctx
)
2432 const char *ret
= NULL
;
2433 ret
= get_conf_item_string(ctx
, "require_membership_of",
2434 WINBIND_REQUIRED_MEMBERSHIP
);
2438 return get_conf_item_string(ctx
, "require-membership-of",
2439 WINBIND_REQUIRED_MEMBERSHIP
);
2442 static int get_warn_pwd_expire_from_config(struct pwb_context
*ctx
)
2445 ret
= get_config_item_int(ctx
, "warn_pwd_expire",
2446 WINBIND_WARN_PWD_EXPIRE
);
2447 /* no or broken setting */
2449 return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES
;
2455 * Retrieve the winbind separator.
2457 * @param ctx PAM winbind context.
2459 * @return string separator character. NULL on failure.
2462 static char winbind_get_separator(struct pwb_context
*ctx
)
2465 static struct wbcInterfaceDetails
*details
= NULL
;
2468 wbc_status
= wbcCtxInterfaceDetails(ctx
->wbc_ctx
, &details
);
2469 if (!WBC_ERROR_IS_OK(wbc_status
)) {
2470 _pam_log(ctx
, LOG_ERR
,
2471 "Could not retrieve winbind interface details: %s",
2472 wbcErrorString(wbc_status
));
2480 result
= details
->winbind_separator
;
2481 wbcFreeMemory(details
);
2487 * Convert a upn to a name.
2489 * @param ctx PAM winbind context.
2490 * @param upn User UPN to be translated.
2492 * @return converted name. NULL pointer on failure. Caller needs to free.
2495 static char* winbind_upn_to_username(struct pwb_context
*ctx
,
2499 wbcErr wbc_status
= WBC_ERR_UNKNOWN_FAILURE
;
2500 struct wbcDomainSid sid
;
2501 enum wbcSidType type
;
2502 char *domain
= NULL
;
2507 /* This cannot work when the winbind separator = @ */
2509 sep
= winbind_get_separator(ctx
);
2510 if (!sep
|| sep
== '@') {
2514 name
= talloc_strdup(ctx
, upn
);
2519 p
= strchr(name
, '@');
2527 /* Convert the UPN to a SID */
2529 wbc_status
= wbcCtxLookupName(ctx
->wbc_ctx
, domain
, name
, &sid
, &type
);
2530 if (!WBC_ERROR_IS_OK(wbc_status
)) {
2534 /* Convert the the SID back to the sAMAccountName */
2536 wbc_status
= wbcCtxLookupSid(ctx
->wbc_ctx
, &sid
, &domain
, &name
, &type
);
2537 if (!WBC_ERROR_IS_OK(wbc_status
)) {
2541 result
= talloc_asprintf(ctx
, "%s%c%s", domain
, sep
, name
);
2542 wbcFreeMemory(domain
);
2543 wbcFreeMemory(name
);
2547 static int _pam_delete_cred(pam_handle_t
*pamh
, int flags
,
2548 int argc
, enum pam_winbind_request_type type
,
2551 int retval
= PAM_SUCCESS
;
2552 struct pwb_context
*ctx
= NULL
;
2553 struct wbcLogoffUserParams logoff
;
2554 struct wbcAuthErrorInfo
*error
= NULL
;
2556 wbcErr wbc_status
= WBC_ERR_SUCCESS
;
2558 ZERO_STRUCT(logoff
);
2560 retval
= _pam_winbind_init_context(pamh
, flags
, argc
, argv
, type
, &ctx
);
2561 if (retval
!= PAM_SUCCESS
) {
2565 _PAM_LOG_FUNCTION_ENTER("_pam_delete_cred", ctx
);
2567 if (ctx
->ctrl
& WINBIND_KRB5_AUTH
) {
2569 /* destroy the ccache here */
2571 uint32_t wbc_flags
= 0;
2572 const char *ccname
= NULL
;
2573 struct passwd
*pwd
= NULL
;
2575 retval
= pam_get_user(pamh
, &user
, _("Username: "));
2576 if (retval
!= PAM_SUCCESS
) {
2577 _pam_log(ctx
, LOG_ERR
,
2578 "could not identify user");
2583 _pam_log(ctx
, LOG_ERR
,
2584 "username was NULL!");
2585 retval
= PAM_USER_UNKNOWN
;
2589 _pam_log_debug(ctx
, LOG_DEBUG
,
2590 "username [%s] obtained", user
);
2592 ccname
= pam_getenv(pamh
, "KRB5CCNAME");
2593 if (ccname
== NULL
) {
2594 _pam_log_debug(ctx
, LOG_DEBUG
,
2595 "user has no KRB5CCNAME environment");
2598 pwd
= getpwnam(user
);
2600 retval
= PAM_USER_UNKNOWN
;
2604 wbc_flags
= WBFLAG_PAM_KRB5
|
2605 WBFLAG_PAM_CONTACT_TRUSTDOM
;
2607 logoff
.username
= user
;
2610 wbc_status
= wbcAddNamedBlob(&logoff
.num_blobs
,
2614 discard_const_p(uint8_t, ccname
),
2616 if (!WBC_ERROR_IS_OK(wbc_status
)) {
2621 wbc_status
= wbcAddNamedBlob(&logoff
.num_blobs
,
2625 (uint8_t *)&wbc_flags
,
2627 if (!WBC_ERROR_IS_OK(wbc_status
)) {
2631 wbc_status
= wbcAddNamedBlob(&logoff
.num_blobs
,
2635 (uint8_t *)&pwd
->pw_uid
,
2636 sizeof(pwd
->pw_uid
));
2637 if (!WBC_ERROR_IS_OK(wbc_status
)) {
2641 wbc_status
= wbcCtxLogoffUserEx(ctx
->wbc_ctx
, &logoff
, &error
);
2642 retval
= wbc_auth_error_to_pam_error(ctx
, error
, wbc_status
,
2643 user
, "wbcLogoffUser");
2644 wbcFreeMemory(logoff
.blobs
);
2645 logoff
.blobs
= NULL
;
2647 if (!WBC_ERROR_IS_OK(wbc_status
)) {
2648 _pam_log(ctx
, LOG_INFO
,
2649 "failed to logoff user %s: %s\n",
2650 user
, wbcErrorString(wbc_status
));
2656 wbcFreeMemory(logoff
.blobs
);
2659 if (!WBC_ERROR_IS_OK(wbc_status
)) {
2660 retval
= wbc_auth_error_to_pam_error(ctx
, error
, wbc_status
,
2661 user
, "wbcLogoffUser");
2663 wbcFreeMemory(error
);
2666 * Delete the krb5 ccname variable from the PAM environment
2667 * if it was set by winbind.
2669 if ((ctx
->ctrl
& WINBIND_KRB5_AUTH
) && pam_getenv(pamh
, "KRB5CCNAME")) {
2670 pam_putenv(pamh
, "KRB5CCNAME");
2673 _PAM_LOG_FUNCTION_LEAVE("_pam_delete_cred", ctx
, retval
);
2680 #ifdef SECURITY_OPENPAM_H_INCLUDED
2682 * Logic below is copied from openpam_check_error_code() in
2683 *./contrib/openpam/lib/libpam/openpam_dispatch.c on FreeBSD.
2685 static int openpam_convert_error_code(struct pwb_context
*ctx
,
2686 enum pam_winbind_request_type req
,
2689 if (r
== PAM_SUCCESS
||
2690 r
== PAM_SYSTEM_ERR
||
2691 r
== PAM_SERVICE_ERR
||
2693 r
== PAM_CONV_ERR
||
2694 r
== PAM_PERM_DENIED
||
2699 /* specific winbind request types */
2701 case PAM_WINBIND_AUTHENTICATE
:
2702 if (r
== PAM_AUTH_ERR
||
2703 r
== PAM_CRED_INSUFFICIENT
||
2704 r
== PAM_AUTHINFO_UNAVAIL
||
2705 r
== PAM_USER_UNKNOWN
||
2706 r
== PAM_MAXTRIES
) {
2710 case PAM_WINBIND_SETCRED
:
2711 if (r
== PAM_CRED_UNAVAIL
||
2712 r
== PAM_CRED_EXPIRED
||
2713 r
== PAM_USER_UNKNOWN
||
2714 r
== PAM_CRED_ERR
) {
2718 case PAM_WINBIND_ACCT_MGMT
:
2719 if (r
== PAM_USER_UNKNOWN
||
2720 r
== PAM_AUTH_ERR
||
2721 r
== PAM_NEW_AUTHTOK_REQD
||
2722 r
== PAM_ACCT_EXPIRED
) {
2726 case PAM_WINBIND_OPEN_SESSION
:
2727 case PAM_WINBIND_CLOSE_SESSION
:
2728 if (r
== PAM_SESSION_ERR
) {
2732 case PAM_WINBIND_CHAUTHTOK
:
2733 if (r
== PAM_PERM_DENIED
||
2734 r
== PAM_AUTHTOK_ERR
||
2735 r
== PAM_AUTHTOK_RECOVERY_ERR
||
2736 r
== PAM_AUTHTOK_LOCK_BUSY
||
2737 r
== PAM_AUTHTOK_DISABLE_AGING
||
2738 r
== PAM_TRY_AGAIN
) {
2745 _pam_log(ctx
, LOG_INFO
,
2746 "Converting PAM error [%d] to PAM_SERVICE_ERR.\n", r
);
2747 return PAM_SERVICE_ERR
;
2749 #define pam_error_code(a, b, c) openpam_convert_error_code(a, b, c)
2751 #define pam_error_code(a, b, c) (c)
2755 int pam_sm_authenticate(pam_handle_t
*pamh
, int flags
,
2756 int argc
, const char **argv
)
2758 const char *username
;
2759 const char *password
;
2760 const char *member
= NULL
;
2761 const char *cctype
= NULL
;
2762 int warn_pwd_expire
;
2763 int retval
= PAM_AUTH_ERR
;
2764 char *username_ret
= NULL
;
2765 char *new_authtok_required
= NULL
;
2766 char *real_username
= NULL
;
2767 struct pwb_context
*ctx
= NULL
;
2769 retval
= _pam_winbind_init_context(pamh
, flags
, argc
, argv
,
2770 PAM_WINBIND_AUTHENTICATE
, &ctx
);
2771 if (retval
!= PAM_SUCCESS
) {
2775 _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", ctx
);
2777 /* Get the username */
2778 retval
= pam_get_user(pamh
, &username
, NULL
);
2779 if ((retval
!= PAM_SUCCESS
) || (!username
)) {
2780 _pam_log_debug(ctx
, LOG_DEBUG
,
2781 "can not get the username");
2782 retval
= PAM_SERVICE_ERR
;
2788 /* Decode the user name since AIX does not support logn user
2789 names by default. The name is encoded as _#uid. */
2791 if (username
[0] == '_') {
2792 uid_t id
= atoi(&username
[1]);
2793 struct passwd
*pw
= NULL
;
2795 if ((id
!=0) && ((pw
= getpwuid(id
)) != NULL
)) {
2796 real_username
= strdup(pw
->pw_name
);
2801 if (!real_username
) {
2802 /* Just making a copy of the username we got from PAM */
2803 if ((real_username
= strdup(username
)) == NULL
) {
2804 _pam_log_debug(ctx
, LOG_DEBUG
,
2805 "memory allocation failure when copying "
2807 retval
= PAM_SERVICE_ERR
;
2812 /* Maybe this was a UPN */
2814 if (strchr(real_username
, '@') != NULL
) {
2815 char *samaccountname
= NULL
;
2817 samaccountname
= winbind_upn_to_username(ctx
,
2819 if (samaccountname
) {
2820 free(real_username
);
2821 real_username
= strdup(samaccountname
);
2825 retval
= _winbind_read_password(ctx
, ctx
->ctrl
, NULL
,
2826 _("Password: "), NULL
,
2829 if (retval
!= PAM_SUCCESS
) {
2830 _pam_log(ctx
, LOG_ERR
,
2831 "Could not retrieve user's password");
2832 retval
= PAM_AUTHTOK_ERR
;
2836 /* Let's not give too much away in the log file */
2838 #ifdef DEBUG_PASSWORD
2839 _pam_log_debug(ctx
, LOG_INFO
,
2840 "Verify user '%s' with password '%s'",
2841 real_username
, password
);
2843 _pam_log_debug(ctx
, LOG_INFO
,
2844 "Verify user '%s'", real_username
);
2847 member
= get_member_from_config(ctx
);
2848 cctype
= get_krb5_cc_type_from_config(ctx
);
2849 warn_pwd_expire
= get_warn_pwd_expire_from_config(ctx
);
2851 /* Now use the username to look up password */
2852 retval
= winbind_auth_request(ctx
, real_username
, password
,
2853 member
, cctype
, warn_pwd_expire
,
2854 NULL
, NULL
, NULL
, &username_ret
);
2856 if (retval
== PAM_NEW_AUTHTOK_REQD
||
2857 retval
== PAM_AUTHTOK_EXPIRED
) {
2859 char *new_authtok_required_during_auth
= NULL
;
2861 new_authtok_required
= talloc_asprintf(NULL
, "%d", retval
);
2862 if (!new_authtok_required
) {
2863 retval
= PAM_BUF_ERR
;
2867 pam_set_data(pamh
, PAM_WINBIND_NEW_AUTHTOK_REQD
,
2868 new_authtok_required
,
2869 _pam_winbind_cleanup_func
);
2871 retval
= PAM_SUCCESS
;
2873 new_authtok_required_during_auth
= talloc_asprintf(NULL
, "%d", true);
2874 if (!new_authtok_required_during_auth
) {
2875 retval
= PAM_BUF_ERR
;
2879 pam_set_data(pamh
, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH
,
2880 new_authtok_required_during_auth
,
2881 _pam_winbind_cleanup_func
);
2888 pam_set_item (pamh
, PAM_USER
, username_ret
);
2889 _pam_log_debug(ctx
, LOG_INFO
,
2890 "Returned user was '%s'", username_ret
);
2894 if (real_username
) {
2895 free(real_username
);
2898 if (!new_authtok_required
) {
2899 pam_set_data(pamh
, PAM_WINBIND_NEW_AUTHTOK_REQD
, NULL
, NULL
);
2902 if (retval
!= PAM_SUCCESS
) {
2903 _pam_free_data_info3(pamh
);
2906 _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx
, retval
);
2914 int pam_sm_setcred(pam_handle_t
*pamh
, int flags
,
2915 int argc
, const char **argv
)
2917 int ret
= PAM_SYSTEM_ERR
;
2918 struct pwb_context
*ctx
= NULL
;
2920 ret
= _pam_winbind_init_context(pamh
, flags
, argc
, argv
,
2921 PAM_WINBIND_SETCRED
, &ctx
);
2922 if (ret
!= PAM_SUCCESS
) {
2926 _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", ctx
);
2928 switch (flags
& ~PAM_SILENT
) {
2930 case PAM_DELETE_CRED
:
2931 ret
= _pam_delete_cred(pamh
, flags
, argc
,
2932 PAM_WINBIND_SETCRED
, argv
);
2934 case PAM_REFRESH_CRED
:
2935 _pam_log_debug(ctx
, LOG_WARNING
,
2936 "PAM_REFRESH_CRED not implemented");
2939 case PAM_REINITIALIZE_CRED
:
2940 _pam_log_debug(ctx
, LOG_WARNING
,
2941 "PAM_REINITIALIZE_CRED not implemented");
2944 case PAM_ESTABLISH_CRED
:
2945 _pam_log_debug(ctx
, LOG_WARNING
,
2946 "PAM_ESTABLISH_CRED not implemented");
2950 ret
= PAM_SYSTEM_ERR
;
2954 _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx
, ret
);
2958 return pam_error_code(ctx
, PAM_WINBIND_SETCRED
, ret
);
2962 * Account management. We want to verify that the account exists
2963 * before returning PAM_SUCCESS
2966 int pam_sm_acct_mgmt(pam_handle_t
*pamh
, int flags
,
2967 int argc
, const char **argv
)
2969 const char *username
;
2970 int ret
= PAM_USER_UNKNOWN
;
2971 const char *tmp
= NULL
;
2972 struct pwb_context
*ctx
= NULL
;
2974 ret
= _pam_winbind_init_context(pamh
, flags
, argc
, argv
,
2975 PAM_WINBIND_ACCT_MGMT
, &ctx
);
2976 if (ret
!= PAM_SUCCESS
) {
2980 _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", ctx
);
2983 /* Get the username */
2984 ret
= pam_get_user(pamh
, &username
, NULL
);
2985 if ((ret
!= PAM_SUCCESS
) || (!username
)) {
2986 _pam_log_debug(ctx
, LOG_DEBUG
,
2987 "can not get the username");
2988 ret
= PAM_SERVICE_ERR
;
2992 /* Verify the username */
2993 ret
= valid_user(ctx
, username
);
2996 /* some sort of system error. The log was already printed */
2997 ret
= PAM_SERVICE_ERR
;
3000 /* the user does not exist */
3001 _pam_log_debug(ctx
, LOG_NOTICE
, "user '%s' not found",
3003 if (ctx
->ctrl
& WINBIND_UNKNOWN_OK_ARG
) {
3007 ret
= PAM_USER_UNKNOWN
;
3010 pam_get_data(pamh
, PAM_WINBIND_NEW_AUTHTOK_REQD
,
3011 (const void **)&tmp
);
3015 case PAM_AUTHTOK_EXPIRED
:
3016 /* Since new token is required in this case */
3018 case PAM_NEW_AUTHTOK_REQD
:
3019 _pam_log(ctx
, LOG_WARNING
,
3020 "pam_sm_acct_mgmt success but %s is set",
3021 PAM_WINBIND_NEW_AUTHTOK_REQD
);
3022 _pam_log(ctx
, LOG_NOTICE
,
3023 "user '%s' needs new password",
3025 /* PAM_AUTHTOKEN_REQD does not exist, but is documented in the manpage */
3026 ret
= PAM_NEW_AUTHTOK_REQD
;
3029 _pam_log(ctx
, LOG_WARNING
,
3030 "pam_sm_acct_mgmt success");
3031 _pam_log(ctx
, LOG_NOTICE
,
3032 "user '%s' granted access", username
);
3038 /* Otherwise, the authentication looked good */
3039 _pam_log(ctx
, LOG_NOTICE
,
3040 "user '%s' granted access", username
);
3044 /* we don't know anything about this return value */
3045 _pam_log(ctx
, LOG_ERR
,
3046 "internal module error (ret = %d, user = '%s')",
3048 ret
= PAM_SERVICE_ERR
;
3052 /* should not be reached */
3057 _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx
, ret
);
3061 return pam_error_code(ctx
, PAM_WINBIND_ACCT_MGMT
, ret
);
3065 int pam_sm_open_session(pam_handle_t
*pamh
, int flags
,
3066 int argc
, const char **argv
)
3068 int ret
= PAM_SUCCESS
;
3069 struct pwb_context
*ctx
= NULL
;
3071 ret
= _pam_winbind_init_context(pamh
, flags
, argc
, argv
,
3072 PAM_WINBIND_OPEN_SESSION
, &ctx
);
3073 if (ret
!= PAM_SUCCESS
) {
3077 _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx
);
3079 if (ctx
->ctrl
& WINBIND_MKHOMEDIR
) {
3080 /* check and create homedir */
3081 ret
= _pam_mkhomedir(ctx
);
3084 _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx
, ret
);
3088 return pam_error_code(ctx
, PAM_WINBIND_OPEN_SESSION
, ret
);
3092 int pam_sm_close_session(pam_handle_t
*pamh
, int flags
,
3093 int argc
, const char **argv
)
3095 int ret
= PAM_SUCCESS
;
3096 struct pwb_context
*ctx
= NULL
;
3098 ret
= _pam_winbind_init_context(pamh
, flags
, argc
, argv
,
3099 PAM_WINBIND_CLOSE_SESSION
, &ctx
);
3100 if (ret
!= PAM_SUCCESS
) {
3104 _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", ctx
);
3106 _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx
, ret
);
3110 return pam_error_code(ctx
, PAM_WINBIND_CLOSE_SESSION
, ret
);
3114 * evaluate whether we need to re-authenticate with kerberos after a
3117 * @param ctx PAM winbind context.
3118 * @param user The username
3120 * @return boolean Returns true if required, false if not.
3123 static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context
*ctx
,
3127 /* Make sure that we only do this if a) the chauthtok got initiated
3128 * during a logon attempt (authenticate->acct_mgmt->chauthtok) b) any
3129 * later password change via the "passwd" command if done by the user
3131 * NB. If we login from gdm or xdm and the password expires,
3132 * we change the password, but there is no memory cache.
3133 * Thus, even for passthrough login, we should do the
3134 * authentication again to update memory cache.
3138 const char *new_authtok_reqd_during_auth
= NULL
;
3139 struct passwd
*pwd
= NULL
;
3141 pam_get_data(ctx
->pamh
, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH
,
3142 (const void **) &new_authtok_reqd_during_auth
);
3143 pam_set_data(ctx
->pamh
, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH
,
3146 if (new_authtok_reqd_during_auth
) {
3150 pwd
= getpwnam(user
);
3155 if (getuid() == pwd
->pw_uid
) {
3164 int pam_sm_chauthtok(pam_handle_t
* pamh
, int flags
,
3165 int argc
, const char **argv
)
3169 bool cached_login
= false;
3171 /* <DO NOT free() THESE> */
3173 const char *pass_old
;
3174 const char *pass_new
;
3175 /* </DO NOT free() THESE> */
3180 char *username_ret
= NULL
;
3181 struct wbcAuthErrorInfo
*error
= NULL
;
3182 struct pwb_context
*ctx
= NULL
;
3184 ret
= _pam_winbind_init_context(pamh
, flags
, argc
, argv
,
3185 PAM_WINBIND_CHAUTHTOK
, &ctx
);
3186 if (ret
!= PAM_SUCCESS
) {
3190 _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", ctx
);
3192 cached_login
= (ctx
->ctrl
& WINBIND_CACHED_LOGIN
);
3194 /* clearing offline bit for auth */
3195 ctx
->ctrl
&= ~WINBIND_CACHED_LOGIN
;
3198 * First get the name of a user
3200 ret
= pam_get_user(pamh
, &user
, _("Username: "));
3201 if (ret
!= PAM_SUCCESS
) {
3202 _pam_log(ctx
, LOG_ERR
,
3203 "password - could not identify user");
3208 _pam_log(ctx
, LOG_ERR
, "username was NULL!");
3209 ret
= PAM_USER_UNKNOWN
;
3213 _pam_log_debug(ctx
, LOG_DEBUG
, "username [%s] obtained", user
);
3215 /* check if this is really a user in winbindd, not only in NSS */
3216 ret
= valid_user(ctx
, user
);
3219 ret
= PAM_USER_UNKNOWN
;
3222 ret
= PAM_SYSTEM_ERR
;
3229 * obtain and verify the current password (OLDAUTHTOK) for
3233 if (flags
& PAM_PRELIM_CHECK
) {
3234 time_t *pwdlastset_prelim
= NULL
;
3236 pwdlastset_prelim
= talloc_zero(NULL
, time_t);
3237 if (pwdlastset_prelim
== NULL
) {
3238 _pam_log(ctx
, LOG_CRIT
,
3239 "password - out of memory");
3244 /* instruct user what is happening */
3246 #define greeting _("Changing password for")
3247 Announce
= talloc_asprintf(ctx
, "%s %s", greeting
, user
);
3249 _pam_log(ctx
, LOG_CRIT
,
3250 "password - out of memory");
3256 lctrl
= ctx
->ctrl
| WINBIND__OLD_PASSWORD
;
3257 ret
= _winbind_read_password(ctx
, lctrl
,
3259 _("(current) NT password: "),
3261 (const char **) &pass_old
);
3262 TALLOC_FREE(Announce
);
3263 if (ret
!= PAM_SUCCESS
) {
3264 _pam_log(ctx
, LOG_NOTICE
,
3265 "password - (old) token not obtained");
3269 /* verify that this is the password for this user */
3271 ret
= winbind_auth_request(ctx
, user
, pass_old
,
3274 pwdlastset_prelim
, NULL
);
3276 if (ret
!= PAM_ACCT_EXPIRED
&&
3277 ret
!= PAM_AUTHTOK_EXPIRED
&&
3278 ret
!= PAM_NEW_AUTHTOK_REQD
&&
3279 ret
!= PAM_SUCCESS
) {
3284 pam_set_data(pamh
, PAM_WINBIND_PWD_LAST_SET
,
3286 _pam_winbind_cleanup_func
);
3288 ret
= pam_set_item(pamh
, PAM_OLDAUTHTOK
,
3289 (const void *) pass_old
);
3291 if (ret
!= PAM_SUCCESS
) {
3292 _pam_log(ctx
, LOG_CRIT
,
3293 "failed to set PAM_OLDAUTHTOK");
3295 } else if (flags
& PAM_UPDATE_AUTHTOK
) {
3296 const time_t *pwdlastset_update
= NULL
;
3299 * obtain the proposed password
3303 * get the old token back.
3306 ret
= pam_get_item(pamh
, PAM_OLDAUTHTOK
, (const void **) &pass_old
);
3308 if (ret
!= PAM_SUCCESS
) {
3309 _pam_log(ctx
, LOG_NOTICE
,
3310 "user not authenticated");
3314 lctrl
= ctx
->ctrl
& ~WINBIND_TRY_FIRST_PASS_ARG
;
3316 if (on(WINBIND_USE_AUTHTOK_ARG
, lctrl
)) {
3317 lctrl
|= WINBIND_USE_FIRST_PASS_ARG
;
3319 if (on(WINBIND_TRY_AUTHTOK_ARG
, lctrl
)) {
3320 lctrl
|= WINBIND_TRY_FIRST_PASS_ARG
;
3323 ret
= PAM_AUTHTOK_ERR
;
3324 while ((ret
!= PAM_SUCCESS
) && (retry
++ < MAX_PASSWD_TRIES
)) {
3326 * use_authtok is to force the use of a previously entered
3327 * password -- needed for pluggable password strength checking
3330 ret
= _winbind_read_password(ctx
, lctrl
,
3332 _("Enter new NT password: "),
3333 _("Retype new NT password: "),
3334 (const char **)&pass_new
);
3336 if (ret
!= PAM_SUCCESS
) {
3337 _pam_log_debug(ctx
, LOG_ALERT
,
3339 "new password not obtained");
3340 pass_old
= NULL
;/* tidy up */
3345 * At this point we know who the user is and what they
3346 * propose as their new password. Verify that the new
3347 * password is acceptable.
3350 if (pass_new
[0] == '\0') {/* "\0" password = NULL */
3356 * By reaching here we have approved the passwords and must now
3357 * rebuild the password database file.
3360 PAM_WINBIND_PWD_LAST_SET
,
3361 (const void **)(&pwdlastset_update
));
3364 * if cached creds were enabled, make sure to set the
3365 * WINBIND_CACHED_LOGIN bit here in order to have winbindd
3366 * update the cached creds storage - gd
3369 ctx
->ctrl
|= WINBIND_CACHED_LOGIN
;
3372 ret
= winbind_chauthtok_request(ctx
, user
, pass_old
,
3373 pass_new
, *pwdlastset_update
);
3374 if (ret
!= PAM_SUCCESS
) {
3375 pass_old
= pass_new
= NULL
;
3379 if (_pam_require_krb5_auth_after_chauthtok(ctx
, user
)) {
3381 const char *member
= NULL
;
3382 const char *cctype
= NULL
;
3383 int warn_pwd_expire
;
3384 struct wbcLogonUserInfo
*info
= NULL
;
3386 member
= get_member_from_config(ctx
);
3387 cctype
= get_krb5_cc_type_from_config(ctx
);
3388 warn_pwd_expire
= get_warn_pwd_expire_from_config(ctx
);
3390 /* Keep WINBIND_CACHED_LOGIN bit for
3391 * authentication after changing the password.
3392 * This will update the cached credentials in case
3393 * that winbindd_dual_pam_chauthtok() fails
3398 ret
= winbind_auth_request(ctx
, user
, pass_new
,
3401 NULL
, &username_ret
);
3402 pass_old
= pass_new
= NULL
;
3404 if (ret
== PAM_SUCCESS
) {
3406 struct wbcAuthUserInfo
*user_info
= NULL
;
3408 if (info
&& info
->info
) {
3409 user_info
= info
->info
;
3412 /* warn a user if the password is about to
3414 _pam_warn_password_expiry(ctx
, user_info
,
3418 /* set some info3 info for other modules in the
3420 _pam_set_data_info3(ctx
, user_info
);
3422 /* put krb5ccname into env */
3423 _pam_setup_krb5_env(ctx
, info
);
3426 pam_set_item(pamh
, PAM_USER
,
3428 _pam_log_debug(ctx
, LOG_INFO
,
3429 "Returned user was '%s'",
3436 if (info
&& info
->blobs
) {
3437 wbcFreeMemory(info
->blobs
);
3439 wbcFreeMemory(info
);
3444 ret
= PAM_SERVICE_ERR
;
3449 /* Deal with offline errors. */
3451 const char *codes
[] = {
3452 "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND",
3453 "NT_STATUS_NO_LOGON_SERVERS",
3454 "NT_STATUS_ACCESS_DENIED"
3457 for (i
=0; i
<ARRAY_SIZE(codes
); i
++) {
3459 if (_pam_check_remark_auth_err(ctx
, error
, codes
[i
], &_ret
)) {
3465 wbcFreeMemory(error
);
3467 _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx
, ret
);
3471 return pam_error_code(ctx
, PAM_WINBIND_CHAUTHTOK
, ret
);
3476 /* static module data */
3478 struct pam_module _pam_winbind_modstruct
= {
3480 pam_sm_authenticate
,
3483 pam_sm_open_session
,
3484 pam_sm_close_session
,
3491 * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000
3492 * Copyright (c) Tim Potter <tpot@samba.org> 2000
3493 * Copyright (c) Andrew Bartlettt <abartlet@samba.org> 2002
3494 * Copyright (c) Guenther Deschner <gd@samba.org> 2005-2008
3495 * Copyright (c) Jan Rêkorajski 1999.
3496 * Copyright (c) Andrew G. Morgan 1996-8.
3497 * Copyright (c) Alex O. Yuriev, 1996.
3498 * Copyright (c) Cristian Gafton 1996.
3499 * Copyright (C) Elliot Lee <sopwith@redhat.com> 1996, Red Hat Software.
3501 * Redistribution and use in source and binary forms, with or without
3502 * modification, are permitted provided that the following conditions
3504 * 1. Redistributions of source code must retain the above copyright
3505 * notice, and the entire permission notice in its entirety,
3506 * including the disclaimer of warranties.
3507 * 2. Redistributions in binary form must reproduce the above copyright
3508 * notice, this list of conditions and the following disclaimer in the
3509 * documentation and/or other materials provided with the distribution.
3510 * 3. The name of the author may not be used to endorse or promote
3511 * products derived from this software without specific prior
3512 * written permission.
3514 * ALTERNATIVELY, this product may be distributed under the terms of
3515 * the GNU Public License, in which case the provisions of the GPL are
3516 * required INSTEAD OF the above restrictions. (This clause is
3517 * necessary due to a potential bad interaction between the GPL and
3518 * the restrictions contained in a BSD-style copyright.)
3520 * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
3521 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
3522 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
3523 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
3524 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3525 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
3526 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3527 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3528 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3529 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
3530 * OF THE POSSIBILITY OF SUCH DAMAGE.