X-Git-Url: http://git.samba.org/?p=metze%2Fsamba%2Fwip.git;a=blobdiff_plain;f=nsswitch%2Fpam_winbind.c;h=4ae646442f8af15b368a4ea1857d778036cda09e;hp=0d33f59a23c62b50e53b3bdddd5d29e4fe8f4aa1;hb=5b7bc0ae13ccc8e0ec1ba0c4b57c72a7d1dec1be;hpb=43372b27403f617897bab564c42eead4d64532e4 diff --git a/nsswitch/pam_winbind.c b/nsswitch/pam_winbind.c index 0d33f59a23c6..4ae646442f8a 100644 --- a/nsswitch/pam_winbind.c +++ b/nsswitch/pam_winbind.c @@ -12,6 +12,17 @@ #include "pam_winbind.h" +enum pam_winbind_request_type +{ + PAM_WINBIND_AUTHENTICATE, + PAM_WINBIND_SETCRED, + PAM_WINBIND_ACCT_MGMT, + PAM_WINBIND_OPEN_SESSION, + PAM_WINBIND_CLOSE_SESSION, + PAM_WINBIND_CHAUTHTOK, + PAM_WINBIND_CLEANUP +}; + static int wbc_error_to_pam_error(wbcErr status) { switch (status) { @@ -138,7 +149,7 @@ static const char *_pam_error_code_str(int err) #define _PAM_LOG_FUNCTION_LEAVE(function, ctx, retval) \ do { \ _pam_log_debug(ctx, LOG_DEBUG, "[pamh: %p] LEAVE: " \ - function " returning %d (%s)", ctx->pamh, retval, \ + function " returning %d (%s)", ctx ? ctx->pamh : NULL, retval, \ _pam_error_code_str(retval)); \ _pam_log_state(ctx); \ } while (0) @@ -154,7 +165,7 @@ static inline void textdomain_init(void); static inline void textdomain_init(void) { if (!initialized) { - bindtextdomain(MODULE_NAME, dyn_LOCALEDIR); + bindtextdomain(MODULE_NAME, LOCALEDIR); initialized = 1; } return; @@ -162,25 +173,6 @@ static inline void textdomain_init(void) #endif -/* - * Work around the pam API that has functions with void ** as parameters - * These lead to strict aliasing warnings with gcc. - */ -static int _pam_get_item(const pam_handle_t *pamh, - int item_type, - const void *_item) -{ - const void **item = (const void **)_item; - return pam_get_item(pamh, item_type, item); -} -static int _pam_get_data(const pam_handle_t *pamh, - const char *module_data_name, - const void *_data) -{ - const void **data = (const void **)_data; - return pam_get_data(pamh, module_data_name, data); -} - /* some syslogging */ #ifdef HAVE_PAM_VSYSLOG @@ -199,17 +191,17 @@ static void _pam_log_int(const pam_handle_t *pamh, { char *format2 = NULL; const char *service; + int ret; - _pam_get_item(pamh, PAM_SERVICE, &service); + pam_get_item(pamh, PAM_SERVICE, (const void **) &service); - format2 = (char *)malloc(strlen(MODULE_NAME)+strlen(format)+strlen(service)+5); - if (format2 == NULL) { + ret = asprintf(&format2, "%s(%s): %s", MODULE_NAME, service, format); + if (ret == -1) { /* what else todo ? */ vsyslog(err, format, args); return; } - sprintf(format2, "%s(%s): %s", MODULE_NAME, service, format); vsyslog(err, format2, args); SAFE_FREE(format2); } @@ -278,7 +270,7 @@ static void _pam_log_debug(struct pwb_context *r, int err, const char *format, . { va_list args; - if (!_pam_log_is_debug_enabled(r->ctrl)) { + if (!r || !_pam_log_is_debug_enabled(r->ctrl)) { return; } @@ -347,10 +339,29 @@ static void _pam_log_state_datum(struct pwb_context *ctx, #define _PAM_LOG_STATE_ITEM_PASSWORD(ctx, item_type) \ _pam_log_state_datum(ctx, item_type, #item_type, \ _LOG_PASSWORD_AS_STRING) +/* + * wrapper to preserve old behaviour of iniparser which ignored + * key values that had no value assigned like + * key = + * for a key like above newer iniparser will return a zero-length + * string, previously iniparser would return NULL + * + * JRA: For compatibility, tiniparser behaves like iniparser. + */ +static const char *tiniparser_getstring_nonempty(struct tiniparser_dictionary *d, + const char *key, + const char *def) +{ + const char *ret = tiniparser_getstring(d, key, def); + if (ret && strlen(ret) == 0) { + ret = NULL; + } + return ret; +} static void _pam_log_state(struct pwb_context *ctx) { - if (!_pam_log_is_debug_state_enabled(ctx->ctrl)) { + if (!ctx || !_pam_log_is_debug_state_enabled(ctx->ctrl)) { return; } @@ -386,13 +397,14 @@ static int _pam_parse(const pam_handle_t *pamh, int flags, int argc, const char **argv, - dictionary **result_d) + enum pam_winbind_request_type type, + struct tiniparser_dictionary **result_d) { int ctrl = 0; const char *config_file = NULL; int i; const char **v; - dictionary *d = NULL; + struct tiniparser_dictionary *d = NULL; if (flags & PAM_SILENT) { ctrl |= WINBIND_SILENT; @@ -410,49 +422,51 @@ static int _pam_parse(const pam_handle_t *pamh, config_file = PAM_WINBIND_CONFIG_FILE; } - d = iniparser_load(config_file); + d = tiniparser_load(config_file); if (d == NULL) { goto config_from_pam; } - if (iniparser_getboolean(d, "global:debug", false)) { + if (tiniparser_getboolean(d, "global:debug", false)) { ctrl |= WINBIND_DEBUG_ARG; } - if (iniparser_getboolean(d, "global:debug_state", false)) { + if (tiniparser_getboolean(d, "global:debug_state", false)) { ctrl |= WINBIND_DEBUG_STATE; } - if (iniparser_getboolean(d, "global:cached_login", false)) { + if (tiniparser_getboolean(d, "global:cached_login", false)) { ctrl |= WINBIND_CACHED_LOGIN; } - if (iniparser_getboolean(d, "global:krb5_auth", false)) { + if (tiniparser_getboolean(d, "global:krb5_auth", false)) { ctrl |= WINBIND_KRB5_AUTH; } - if (iniparser_getboolean(d, "global:silent", false)) { + if (tiniparser_getboolean(d, "global:silent", false)) { ctrl |= WINBIND_SILENT; } - if (iniparser_getstr(d, "global:krb5_ccache_type") != NULL) { + if (tiniparser_getstring_nonempty(d, "global:krb5_ccache_type", NULL) != NULL) { ctrl |= WINBIND_KRB5_CCACHE_TYPE; } - if ((iniparser_getstr(d, "global:require-membership-of") != NULL) || - (iniparser_getstr(d, "global:require_membership_of") != NULL)) { + if ((tiniparser_getstring_nonempty(d, "global:require-membership-of", NULL) + != NULL) || + (tiniparser_getstring_nonempty(d, "global:require_membership_of", NULL) + != NULL)) { ctrl |= WINBIND_REQUIRED_MEMBERSHIP; } - if (iniparser_getboolean(d, "global:try_first_pass", false)) { + if (tiniparser_getboolean(d, "global:try_first_pass", false)) { ctrl |= WINBIND_TRY_FIRST_PASS_ARG; } - if (iniparser_getint(d, "global:warn_pwd_expire", 0)) { + if (tiniparser_getint(d, "global:warn_pwd_expire", 0)) { ctrl |= WINBIND_WARN_PWD_EXPIRE; } - if (iniparser_getboolean(d, "global:mkhomedir", false)) { + if (tiniparser_getboolean(d, "global:mkhomedir", false)) { ctrl |= WINBIND_MKHOMEDIR; } @@ -475,11 +489,15 @@ config_from_pam: ctrl |= WINBIND_TRY_FIRST_PASS_ARG; else if (!strcasecmp(*v, "unknown_ok")) ctrl |= WINBIND_UNKNOWN_OK_ARG; - else if (!strncasecmp(*v, "require_membership_of", - strlen("require_membership_of"))) + else if ((type == PAM_WINBIND_AUTHENTICATE + || type == PAM_WINBIND_SETCRED) + && !strncasecmp(*v, "require_membership_of", + strlen("require_membership_of"))) ctrl |= WINBIND_REQUIRED_MEMBERSHIP; - else if (!strncasecmp(*v, "require-membership-of", - strlen("require-membership-of"))) + else if ((type == PAM_WINBIND_AUTHENTICATE + || type == PAM_WINBIND_SETCRED) + && !strncasecmp(*v, "require-membership-of", + strlen("require-membership-of"))) ctrl |= WINBIND_REQUIRED_MEMBERSHIP; else if (!strcasecmp(*v, "krb5_auth")) ctrl |= WINBIND_KRB5_AUTH; @@ -490,7 +508,10 @@ config_from_pam: ctrl |= WINBIND_CACHED_LOGIN; else if (!strcasecmp(*v, "mkhomedir")) ctrl |= WINBIND_MKHOMEDIR; - else { + else if (!strncasecmp(*v, "warn_pwd_expire", + strlen("warn_pwd_expire"))) + ctrl |= WINBIND_WARN_PWD_EXPIRE; + else if (type != PAM_WINBIND_CLEANUP) { __pam_log(pamh, ctrl, LOG_ERR, "pam_parse: unknown option: %s", *v); return -1; @@ -502,7 +523,7 @@ config_from_pam: *result_d = d; } else { if (d) { - iniparser_freedict(d); + tiniparser_freedict(d); } } @@ -516,9 +537,11 @@ static int _pam_winbind_free_context(struct pwb_context *ctx) } if (ctx->dict) { - iniparser_freedict(ctx->dict); + tiniparser_freedict(ctx->dict); } + wbcCtxFree(ctx->wbc_ctx); + return 0; } @@ -526,15 +549,17 @@ static int _pam_winbind_init_context(pam_handle_t *pamh, int flags, int argc, const char **argv, + enum pam_winbind_request_type type, struct pwb_context **ctx_p) { struct pwb_context *r = NULL; + int ctrl_code; #ifdef HAVE_GETTEXT textdomain_init(); #endif - r = TALLOC_ZERO_P(NULL, struct pwb_context); + r = talloc_zero(NULL, struct pwb_context); if (!r) { return PAM_BUF_ERR; } @@ -545,8 +570,15 @@ static int _pam_winbind_init_context(pam_handle_t *pamh, r->flags = flags; r->argc = argc; r->argv = argv; - r->ctrl = _pam_parse(pamh, flags, argc, argv, &r->dict); - if (r->ctrl == -1) { + ctrl_code = _pam_parse(pamh, flags, argc, argv, type, &r->dict); + if (ctrl_code == -1) { + TALLOC_FREE(r); + return PAM_SYSTEM_ERR; + } + r->ctrl = ctrl_code; + + r->wbc_ctx = wbcCtxCreate(); + if (r->wbc_ctx == NULL) { TALLOC_FREE(r); return PAM_SYSTEM_ERR; } @@ -560,7 +592,7 @@ static void _pam_winbind_cleanup_func(pam_handle_t *pamh, void *data, int error_status) { - int ctrl = _pam_parse(pamh, 0, 0, NULL, NULL); + int ctrl = _pam_parse(pamh, 0, 0, NULL, PAM_WINBIND_CLEANUP, NULL); if (_pam_log_is_debug_state_enabled(ctrl)) { __pam_log_debug(pamh, ctrl, LOG_DEBUG, "[pamh: %p] CLEAN: cleaning up PAM data %p " @@ -578,7 +610,7 @@ static const struct ntstatus_errors { {"NT_STATUS_OK", N_("Success")}, {"NT_STATUS_BACKUP_CONTROLLER", - N_("No primary Domain Controler available")}, + N_("No primary Domain Controller available")}, {"NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", N_("No domain controllers found")}, {"NT_STATUS_NO_LOGON_SERVERS", @@ -635,16 +667,16 @@ static const char *_get_ntstatus_error_string(const char *nt_status_string) static int converse(const pam_handle_t *pamh, int nargs, - struct pam_message **message, + const struct pam_message **message, struct pam_response **response) { int retval; struct pam_conv *conv; - retval = _pam_get_item(pamh, PAM_CONV, &conv); + retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv); if (retval == PAM_SUCCESS) { retval = conv->conv(nargs, - (const struct pam_message **)message, + discard_const_p(const struct pam_message *, message), response, conv->appdata_ptr); } @@ -658,7 +690,8 @@ static int _make_remark(struct pwb_context *ctx, { int retval = PAM_SUCCESS; - struct pam_message *pmsg[1], msg[1]; + const struct pam_message *pmsg[1]; + struct pam_message msg[1]; struct pam_response *resp; if (ctx->flags & WINBIND_SILENT) { @@ -743,6 +776,11 @@ static int pam_winbind_request_log(struct pwb_context *ctx, return PAM_IGNORE; } return retval; + case PAM_AUTHTOK_ERR: + /* Authentication token manipulation error */ + _pam_log(ctx, LOG_WARNING, "user `%s' authentication token change failed " + "(pwd complexity/history/min_age not met?)", user); + return retval; case PAM_SUCCESS: /* Otherwise, the authentication looked good */ if (strcmp(fn, "wbcLogonUser") == 0) { @@ -803,6 +841,42 @@ static int wbc_auth_error_to_pam_error(struct pwb_context *ctx, return pam_winbind_request_log(ctx, ret, username, fn); } +#if defined(HAVE_PAM_RADIO_TYPE) +static bool _pam_winbind_change_pwd(struct pwb_context *ctx) +{ + struct pam_message msg; + const struct pam_message *pmsg; + struct pam_response *resp = NULL; + int ret; + bool retval = false; + pmsg = &msg; + msg.msg_style = PAM_RADIO_TYPE; + msg.msg = _("Do you want to change your password now?"); + ret = converse(ctx->pamh, 1, &pmsg, &resp); + if (resp == NULL) { + if (ret == PAM_SUCCESS) { + _pam_log(ctx, LOG_CRIT, "pam_winbind: system error!\n"); + return false; + } + } + if (ret != PAM_SUCCESS) { + return false; + } + _pam_log(ctx, LOG_CRIT, "Received [%s] reply from application.\n", resp->resp); + + if ((resp->resp != NULL) && (strcasecmp(resp->resp, "yes") == 0)) { + retval = true; + } + + _pam_drop_reply(resp, 1); + return retval; +} +#else +static bool _pam_winbind_change_pwd(struct pwb_context *ctx) +{ + return false; +} +#endif /** * send a password expiry message if required @@ -819,15 +893,22 @@ static bool _pam_send_password_expiry_message(struct pwb_context *ctx, time_t next_change, time_t now, int warn_pwd_expire, - bool *already_expired) + bool *already_expired, + bool *change_pwd) { int days = 0; struct tm tm_now, tm_next_change; + bool retval = false; + int ret; if (already_expired) { *already_expired = false; } + if (change_pwd) { + *change_pwd = false; + } + if (next_change <= now) { PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PASSWORD_EXPIRED"); if (already_expired) { @@ -850,15 +931,61 @@ static bool _pam_send_password_expiry_message(struct pwb_context *ctx, (tm_now.tm_yday+tm_now.tm_year*365); if (days == 0) { - _make_remark(ctx, PAM_TEXT_INFO, - _("Your password expires today")); + ret = _make_remark(ctx, PAM_TEXT_INFO, + _("Your password expires today.\n")); + + /* + * If change_pwd and already_expired is null. + * We are just sending a notification message. + * We don't expect any response in this case. + */ + + if (!change_pwd && !already_expired) { + return true; + } + + /* + * successfully sent the warning message. + * Give the user a chance to change pwd. + */ + if (ret == PAM_SUCCESS) { + if (change_pwd) { + retval = _pam_winbind_change_pwd(ctx); + if (retval) { + *change_pwd = true; + } + } + } return true; } if (days > 0 && days < warn_pwd_expire) { - _make_remark_format(ctx, PAM_TEXT_INFO, - _("Your password will expire in %d %s"), - days, (days > 1) ? _("days"):_("day")); + + ret = _make_remark_format(ctx, PAM_TEXT_INFO, + _("Your password will expire in %d %s.\n"), + days, (days > 1) ? _("days"):_("day")); + /* + * If change_pwd and already_expired is null. + * We are just sending a notification message. + * We don't expect any response in this case. + */ + + if (!change_pwd && !already_expired) { + return true; + } + + /* + * successfully sent the warning message. + * Give the user a chance to change pwd. + */ + if (ret == PAM_SUCCESS) { + if (change_pwd) { + retval = _pam_winbind_change_pwd(ctx); + if (retval) { + *change_pwd = true; + } + } + } return true; } @@ -877,14 +1004,14 @@ static bool _pam_send_password_expiry_message(struct pwb_context *ctx, static void _pam_warn_password_expiry(struct pwb_context *ctx, const struct wbcAuthUserInfo *info, - const struct wbcUserPasswordPolicyInfo *policy, int warn_pwd_expire, - bool *already_expired) + bool *already_expired, + bool *change_pwd) { time_t now = time(NULL); time_t next_change = 0; - if (!info || !policy) { + if (info == NULL) { return; } @@ -892,6 +1019,10 @@ static void _pam_warn_password_expiry(struct pwb_context *ctx, *already_expired = false; } + if (change_pwd) { + *change_pwd = false; + } + /* accounts with WBC_ACB_PWNOEXP set never receive a warning */ if (info->acct_flags & WBC_ACB_PWNOEXP) { return; @@ -907,22 +1038,8 @@ static void _pam_warn_password_expiry(struct pwb_context *ctx, if (_pam_send_password_expiry_message(ctx, next_change, now, warn_pwd_expire, - already_expired)) { - return; - } - - /* now check for the global password policy */ - /* good catch from Ralf Haferkamp: an expiry of "never" is translated - * to -1 */ - if (policy->expire <= 0) { - return; - } - - next_change = info->pass_last_set_time + policy->expire; - - if (_pam_send_password_expiry_message(ctx, next_change, now, - warn_pwd_expire, - already_expired)) { + already_expired, + change_pwd)) { return; } @@ -947,15 +1064,9 @@ static bool safe_append_string(char *dest, const char *src, int dest_buffer_size) { - int dest_length = strlen(dest); - int src_length = strlen(src); - - if (dest_length + src_length + 1 > dest_buffer_size) { - return false; - } - - memcpy(dest + dest_length, src, src_length + 1); - return true; + size_t len; + len = strlcat(dest, src, dest_buffer_size); + return (len < dest_buffer_size); } /** @@ -975,41 +1086,37 @@ static bool winbind_name_to_sid_string(struct pwb_context *ctx, char *sid_list_buffer, int sid_list_buffer_size) { - const char* sid_string; + char sid_string[WBC_SID_STRING_BUFLEN]; /* lookup name? */ if (IS_SID_STRING(name)) { - sid_string = name; + strlcpy(sid_string, name, sizeof(sid_string)); } else { wbcErr wbc_status; struct wbcDomainSid sid; enum wbcSidType type; - char *sid_str; _pam_log_debug(ctx, LOG_DEBUG, "no sid given, looking up: %s\n", name); - wbc_status = wbcLookupName("", name, &sid, &type); + wbc_status = wbcCtxLookupName(ctx->wbc_ctx, + "", + name, + &sid, + &type); if (!WBC_ERROR_IS_OK(wbc_status)) { _pam_log(ctx, LOG_INFO, "could not lookup name: %s\n", name); return false; } - wbc_status = wbcSidToString(&sid, &sid_str); - if (!WBC_ERROR_IS_OK(wbc_status)) { - return false; - } - - wbcFreeMemory(sid_str); - sid_string = sid_str; + wbcSidToStringBuf(&sid, sid_string, sizeof(sid_string)); } if (!safe_append_string(sid_list_buffer, sid_string, sid_list_buffer_size)) { return false; } - return true; } @@ -1034,13 +1141,14 @@ static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx, char *current_name = NULL; const char *search_location; const char *comma; + int len; if (sid_list_buffer_size > 0) { sid_list_buffer[0] = 0; } search_location = name_list; - while ((comma = strstr(search_location, ",")) != NULL) { + while ((comma = strchr(search_location, ',')) != NULL) { current_name = strndup(search_location, comma - search_location); if (NULL == current_name) { @@ -1051,7 +1159,23 @@ static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx, current_name, sid_list_buffer, sid_list_buffer_size)) { - goto out; + /* + * If one group name failed, we must not fail + * the authentication totally, continue with + * the following group names. If user belongs to + * one of the valid groups, we must allow it + * login. -- BoYang + */ + + _pam_log(ctx, LOG_INFO, "cannot convert group %s to sid, " + "check if group %s is valid group.", current_name, + current_name); + _make_remark_format(ctx, PAM_TEXT_INFO, _("Cannot convert group %s " + "to sid, please contact your administrator to see " + "if group %s is valid."), current_name, current_name); + SAFE_FREE(current_name); + search_location = comma + 1; + continue; } SAFE_FREE(current_name); @@ -1067,7 +1191,27 @@ static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx, if (!winbind_name_to_sid_string(ctx, user, search_location, sid_list_buffer, sid_list_buffer_size)) { - goto out; + _pam_log(ctx, LOG_INFO, "cannot convert group %s to sid, " + "check if group %s is valid group.", search_location, + search_location); + _make_remark_format(ctx, PAM_TEXT_INFO, _("Cannot convert group %s " + "to sid, please contact your administrator to see " + "if group %s is valid."), search_location, search_location); + + /* If no valid groups were converted we should fail outright */ + if (name_list != NULL && strlen(sid_list_buffer) == 0) { + result = false; + goto out; + } + /* + * The lookup of the last name failed.. + * It results in require_member_of_sid ends with ',' + * It is malformated parameter here, overwrite the last ','. + */ + len = strlen(sid_list_buffer); + if ((len != 0) && (sid_list_buffer[len - 1] == ',')) { + sid_list_buffer[len - 1] = '\0'; + } } result = true; @@ -1089,7 +1233,7 @@ out: static void _pam_setup_krb5_env(struct pwb_context *ctx, struct wbcLogonUserInfo *info) { - char var[PATH_MAX]; + char *var = NULL; int ret; uint32_t i; const char *krb5ccname = NULL; @@ -1116,16 +1260,17 @@ static void _pam_setup_krb5_env(struct pwb_context *ctx, _pam_log_debug(ctx, LOG_DEBUG, "request returned KRB5CCNAME: %s", krb5ccname); - if (snprintf(var, sizeof(var), "KRB5CCNAME=%s", krb5ccname) == -1) { + if (asprintf(&var, "KRB5CCNAME=%s", krb5ccname) == -1) { return; } ret = pam_putenv(ctx->pamh, var); - if (ret) { + if (ret != PAM_SUCCESS) { _pam_log(ctx, LOG_ERR, "failed to set KRB5CCNAME to %s: %s", var, pam_strerror(ctx->pamh, ret)); } + free(var); } /** @@ -1186,7 +1331,7 @@ static void _pam_set_data_string(struct pwb_context *ctx, ret = pam_set_data(ctx->pamh, data_name, talloc_strdup(NULL, value), _pam_winbind_cleanup_func); - if (ret) { + if (ret != PAM_SUCCESS) { _pam_log_debug(ctx, LOG_DEBUG, "Could not set data %s: %s\n", data_name, pam_strerror(ctx->pamh, ret)); @@ -1298,12 +1443,12 @@ static void _pam_warn_krb5_failure(struct pwb_context *ctx, static bool _pam_check_remark_auth_err(struct pwb_context *ctx, const struct wbcAuthErrorInfo *e, const char *nt_status_string, - int *pam_error) + int *pam_err) { const char *ntstatus = NULL; const char *error_string = NULL; - if (!e || !pam_error) { + if (!e || !pam_err) { return false; } @@ -1317,18 +1462,18 @@ static bool _pam_check_remark_auth_err(struct pwb_context *ctx, error_string = _get_ntstatus_error_string(nt_status_string); if (error_string) { _make_remark(ctx, PAM_ERROR_MSG, error_string); - *pam_error = e->pam_error; + *pam_err = e->pam_error; return true; } if (e->display_string) { - _make_remark(ctx, PAM_ERROR_MSG, e->display_string); - *pam_error = e->pam_error; + _make_remark(ctx, PAM_ERROR_MSG, _(e->display_string)); + *pam_err = e->pam_error; return true; } _make_remark(ctx, PAM_ERROR_MSG, nt_status_string); - *pam_error = e->pam_error; + *pam_err = e->pam_error; return true; } @@ -1514,7 +1659,7 @@ static int _pam_mkhomedir(struct pwb_context *ctx) } ret = _pam_create_homedir(ctx, create_dir, mode); - if (ret) { + if (ret != PAM_SUCCESS) { return ret; } } @@ -1533,23 +1678,17 @@ static int winbind_auth_request(struct pwb_context *ctx, const int warn_pwd_expire, struct wbcAuthErrorInfo **p_error, struct wbcLogonUserInfo **p_info, - struct wbcUserPasswordPolicyInfo **p_policy, time_t *pwd_last_set, char **user_ret) { wbcErr wbc_status; - struct wbcLogonUserParams logon; char membership_of[1024]; uid_t user_uid = -1; - uint32_t flags = WBFLAG_PAM_INFO3_TEXT | - WBFLAG_PAM_GET_PWD_POLICY; - + uint32_t flags = WBFLAG_PAM_INFO3_TEXT; struct wbcLogonUserInfo *info = NULL; struct wbcAuthUserInfo *user_info = NULL; struct wbcAuthErrorInfo *error = NULL; - struct wbcUserPasswordPolicyInfo *policy = NULL; - int ret = PAM_AUTH_ERR; int i; const char *codes[] = { @@ -1639,7 +1778,7 @@ static int winbind_auth_request(struct pwb_context *ctx, &logon.blobs, "krb5_cc_type", 0, - (uint8_t *)cctype, + discard_const_p(uint8_t, cctype), strlen(cctype)+1); if (!WBC_ERROR_IS_OK(wbc_status)) { goto done; @@ -1678,7 +1817,11 @@ static int winbind_auth_request(struct pwb_context *ctx, } } - wbc_status = wbcLogonUser(&logon, &info, &error, &policy); + wbc_status = wbcCtxLogonUser(ctx->wbc_ctx, + &logon, + &info, + &error, + NULL); ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status, user, "wbcLogonUser"); wbcFreeMemory(logon.blobs); @@ -1696,10 +1839,6 @@ static int winbind_auth_request(struct pwb_context *ctx, *p_info = info; } - if (p_policy && policy) { - *p_policy = policy; - } - if (p_error && error) { /* We want to process the error in the caller. */ *p_error = error; @@ -1714,32 +1853,39 @@ static int winbind_auth_request(struct pwb_context *ctx, } } - if ((ret == PAM_SUCCESS) && user_info && policy && info) { + if ((ret == PAM_SUCCESS) && user_info && info) { bool already_expired = false; + bool change_pwd = false; /* warn a user if the password is about to expire soon */ - _pam_warn_password_expiry(ctx, user_info, policy, + _pam_warn_password_expiry(ctx, user_info, warn_pwd_expire, - &already_expired); + &already_expired, + &change_pwd); if (already_expired == true) { SMB_TIME_T last_set = user_info->pass_last_set_time; + SMB_TIME_T must_set = user_info->pass_must_change_time; _pam_log_debug(ctx, LOG_DEBUG, "Password has expired " "(Password was last set: %lld, " - "the policy says it should expire here " - "%lld (now it's: %lu))\n", + "it must be changed here " + "%lld (now it's: %ld))\n", (long long int)last_set, - (long long int)last_set + - policy->expire, - time(NULL)); + (long long int)must_set, + (long)time(NULL)); return PAM_AUTHTOK_EXPIRED; } + if (change_pwd) { + ret = PAM_NEW_AUTHTOK_REQD; + goto done; + } + /* inform about logon type */ _pam_warn_logon_type(ctx, user, user_info->user_flags); @@ -1758,10 +1904,8 @@ static int winbind_auth_request(struct pwb_context *ctx, } done: - if (logon.blobs) { - wbcFreeMemory(logon.blobs); - } - if (info && info->blobs) { + wbcFreeMemory(logon.blobs); + if (info && info->blobs && !p_info) { wbcFreeMemory(info->blobs); } if (error && !p_error) { @@ -1770,9 +1914,6 @@ static int winbind_auth_request(struct pwb_context *ctx, if (info && !p_info) { wbcFreeMemory(info); } - if (policy && !p_policy) { - wbcFreeMemory(policy); - } return ret; } @@ -1815,12 +1956,16 @@ static int winbind_chauthtok_request(struct pwb_context *ctx, } params.account_name = user; - params.level = WBC_AUTH_USER_LEVEL_PLAIN; + params.level = WBC_CHANGE_PASSWORD_LEVEL_PLAIN; params.old_password.plaintext = oldpass; params.new_password.plaintext = newpass; params.flags = flags; - wbc_status = wbcChangeUserPasswordEx(¶ms, &error, &reject_reason, &policy); + wbc_status = wbcCtxChangeUserPasswordEx(ctx->wbc_ctx, + ¶ms, + &error, + &reject_reason, + &policy); ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status, user, "wbcChangeUserPasswordEx"); @@ -1854,25 +1999,25 @@ static int winbind_chauthtok_request(struct pwb_context *ctx, } /* FIXME: avoid to send multiple PAM messages after another */ - switch (reject_reason) { + switch ((int)reject_reason) { case -1: break; - case WBC_PWD_CHANGE_REJECT_OTHER: + case WBC_PWD_CHANGE_NO_ERROR: if ((min_pwd_age > 0) && (pwd_last_set + min_pwd_age > time(NULL))) { PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PWD_TOO_RECENT"); } break; - case WBC_PWD_CHANGE_REJECT_TOO_SHORT: + case WBC_PWD_CHANGE_PASSWORD_TOO_SHORT: PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PWD_TOO_SHORT"); break; - case WBC_PWD_CHANGE_REJECT_IN_HISTORY: + case WBC_PWD_CHANGE_PWD_IN_HISTORY: PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PWD_HISTORY_CONFLICT"); break; - case WBC_PWD_CHANGE_REJECT_COMPLEXITY: + case WBC_PWD_CHANGE_NOT_COMPLEX: _make_remark(ctx, PAM_ERROR_MSG, _("Password does not meet " "complexity requirements")); @@ -1924,7 +2069,7 @@ static int valid_user(struct pwb_context *ctx, return 1; } - wbc_status = wbcGetpwnam(user, &wb_pwd); + wbc_status = wbcCtxGetpwnam(ctx->wbc_ctx, user, &wb_pwd); wbcFreeMemory(wb_pwd); if (!WBC_ERROR_IS_OK(wbc_status)) { _pam_log(ctx, LOG_DEBUG, "valid_user: wbcGetpwnam gave %s\n", @@ -1933,6 +2078,9 @@ static int valid_user(struct pwb_context *ctx, switch (wbc_status) { case WBC_ERR_UNKNOWN_USER: + /* match other insane libwbclient return codes */ + case WBC_ERR_WINBIND_NOT_AVAILABLE: + case WBC_ERR_DOMAIN_NOT_FOUND: return 1; case WBC_ERR_SUCCESS: return 0; @@ -1989,7 +2137,9 @@ static int _winbind_read_password(struct pwb_context *ctx, if (on(WINBIND_TRY_FIRST_PASS_ARG, ctrl) || on(WINBIND_USE_FIRST_PASS_ARG, ctrl)) { - retval = _pam_get_item(ctx->pamh, authtok_flag, &item); + retval = pam_get_item(ctx->pamh, + authtok_flag, + (const void **) &item); if (retval != PAM_SUCCESS) { /* very strange. */ _pam_log(ctx, LOG_ALERT, @@ -2015,7 +2165,8 @@ static int _winbind_read_password(struct pwb_context *ctx, */ { - struct pam_message msg[3], *pmsg[3]; + struct pam_message msg[3]; + const struct pam_message *pmsg[3]; struct pam_response *resp; int i, replies; @@ -2097,7 +2248,7 @@ static int _winbind_read_password(struct pwb_context *ctx, retval = pam_set_item(ctx->pamh, authtok_flag, token); _pam_delete(token); /* clean it up */ if (retval != PAM_SUCCESS || - (retval = _pam_get_item(ctx->pamh, authtok_flag, &item)) != PAM_SUCCESS) { + (retval = pam_get_item(ctx->pamh, authtok_flag, (const void **) &item)) != PAM_SUCCESS) { _pam_log(ctx, LOG_CRIT, "error manipulating password"); return retval; @@ -2147,7 +2298,7 @@ static const char *get_conf_item_string(struct pwb_context *ctx, goto out; } - parm_opt = iniparser_getstr(ctx->dict, key); + parm_opt = tiniparser_getstring_nonempty(ctx->dict, key, NULL); TALLOC_FREE(key); _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n", @@ -2195,7 +2346,7 @@ static int get_config_item_int(struct pwb_context *ctx, goto out; } - parm_opt = iniparser_getint(ctx->dict, key, -1); + parm_opt = tiniparser_getint(ctx->dict, key, -1); TALLOC_FREE(key); _pam_log_debug(ctx, LOG_INFO, @@ -2217,7 +2368,7 @@ static const char *get_member_from_config(struct pwb_context *ctx) const char *ret = NULL; ret = get_conf_item_string(ctx, "require_membership_of", WINBIND_REQUIRED_MEMBERSHIP); - if (ret) { + if (ret != NULL) { return ret; } return get_conf_item_string(ctx, "require-membership-of", @@ -2230,7 +2381,7 @@ static int get_warn_pwd_expire_from_config(struct pwb_context *ctx) ret = get_config_item_int(ctx, "warn_pwd_expire", WINBIND_WARN_PWD_EXPIRE); /* no or broken setting */ - if (ret <= 0) { + if (ret < 0) { return DEFAULT_DAYS_TO_WARN_BEFORE_PWD_EXPIRES; } return ret; @@ -2249,7 +2400,7 @@ static char winbind_get_separator(struct pwb_context *ctx) wbcErr wbc_status; static struct wbcInterfaceDetails *details = NULL; - wbc_status = wbcInterfaceDetails(&details); + wbc_status = wbcCtxInterfaceDetails(ctx->wbc_ctx, &details); if (!WBC_ERROR_IS_OK(wbc_status)) { _pam_log(ctx, LOG_ERR, "Could not retrieve winbind interface details: %s", @@ -2269,7 +2420,7 @@ static char winbind_get_separator(struct pwb_context *ctx) * Convert a upn to a name. * * @param ctx PAM winbind context. - * @param upn USer UPN to be trabslated. + * @param upn User UPN to be translated. * * @return converted name. NULL pointer on failure. Caller needs to free. */ @@ -2281,8 +2432,10 @@ static char* winbind_upn_to_username(struct pwb_context *ctx, wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; struct wbcDomainSid sid; enum wbcSidType type; - char *domain; + char *domain = NULL; char *name; + char *p; + char *result; /* This cannot work when the winbind separator = @ */ @@ -2291,25 +2444,42 @@ static char* winbind_upn_to_username(struct pwb_context *ctx, return NULL; } + name = talloc_strdup(ctx, upn); + if (!name) { + return NULL; + } + + p = strchr(name, '@'); + if (p == NULL) { + TALLOC_FREE(name); + return NULL; + } + *p = '\0'; + domain = p + 1; + /* Convert the UPN to a SID */ - wbc_status = wbcLookupName("", upn, &sid, &type); + wbc_status = wbcCtxLookupName(ctx->wbc_ctx, domain, name, &sid, &type); if (!WBC_ERROR_IS_OK(wbc_status)) { return NULL; } /* Convert the the SID back to the sAMAccountName */ - wbc_status = wbcLookupSid(&sid, &domain, &name, &type); + wbc_status = wbcCtxLookupSid(ctx->wbc_ctx, &sid, &domain, &name, &type); if (!WBC_ERROR_IS_OK(wbc_status)) { return NULL; } - return talloc_asprintf(ctx, "%s\\%s", domain, name); + result = talloc_asprintf(ctx, "%s%c%s", domain, sep, name); + wbcFreeMemory(domain); + wbcFreeMemory(name); + return result; } static int _pam_delete_cred(pam_handle_t *pamh, int flags, - int argc, const char **argv) + int argc, enum pam_winbind_request_type type, + const char **argv) { int retval = PAM_SUCCESS; struct pwb_context *ctx = NULL; @@ -2320,9 +2490,9 @@ static int _pam_delete_cred(pam_handle_t *pamh, int flags, ZERO_STRUCT(logoff); - retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); - if (retval) { - goto out; + retval = _pam_winbind_init_context(pamh, flags, argc, argv, type, &ctx); + if (retval != PAM_SUCCESS) { + return retval; } _PAM_LOG_FUNCTION_ENTER("_pam_delete_cred", ctx); @@ -2336,7 +2506,7 @@ static int _pam_delete_cred(pam_handle_t *pamh, int flags, struct passwd *pwd = NULL; retval = pam_get_user(pamh, &user, _("Username: ")); - if (retval) { + if (retval != PAM_SUCCESS) { _pam_log(ctx, LOG_ERR, "could not identify user"); goto out; @@ -2374,7 +2544,7 @@ static int _pam_delete_cred(pam_handle_t *pamh, int flags, &logoff.blobs, "ccfilename", 0, - (uint8_t *)ccname, + discard_const_p(uint8_t, ccname), strlen(ccname)+1); if (!WBC_ERROR_IS_OK(wbc_status)) { goto out; @@ -2401,7 +2571,7 @@ static int _pam_delete_cred(pam_handle_t *pamh, int flags, goto out; } - wbc_status = wbcLogoffUserEx(&logoff, &error); + wbc_status = wbcCtxLogoffUserEx(ctx->wbc_ctx, &logoff, &error); retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status, user, "wbcLogoffUser"); wbcFreeMemory(error); @@ -2455,9 +2625,10 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags, char *real_username = NULL; struct pwb_context *ctx = NULL; - retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); - if (retval) { - goto out; + retval = _pam_winbind_init_context(pamh, flags, argc, argv, + PAM_WINBIND_AUTHENTICATE, &ctx); + if (retval != PAM_SUCCESS) { + return retval; } _PAM_LOG_FUNCTION_ENTER("pam_sm_authenticate", ctx); @@ -2539,8 +2710,7 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags, /* Now use the username to look up password */ retval = winbind_auth_request(ctx, real_username, password, member, cctype, warn_pwd_expire, - NULL, NULL, NULL, - NULL, &username_ret); + NULL, NULL, NULL, &username_ret); if (retval == PAM_NEW_AUTHTOK_REQD || retval == PAM_AUTHTOK_EXPIRED) { @@ -2606,9 +2776,10 @@ int pam_sm_setcred(pam_handle_t *pamh, int flags, int ret = PAM_SYSTEM_ERR; struct pwb_context *ctx = NULL; - ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); - if (ret) { - goto out; + ret = _pam_winbind_init_context(pamh, flags, argc, argv, + PAM_WINBIND_SETCRED, &ctx); + if (ret != PAM_SUCCESS) { + return ret; } _PAM_LOG_FUNCTION_ENTER("pam_sm_setcred", ctx); @@ -2616,7 +2787,8 @@ int pam_sm_setcred(pam_handle_t *pamh, int flags, switch (flags & ~PAM_SILENT) { case PAM_DELETE_CRED: - ret = _pam_delete_cred(pamh, flags, argc, argv); + ret = _pam_delete_cred(pamh, flags, argc, + PAM_WINBIND_SETCRED, argv); break; case PAM_REFRESH_CRED: _pam_log_debug(ctx, LOG_WARNING, @@ -2638,8 +2810,6 @@ int pam_sm_setcred(pam_handle_t *pamh, int flags, break; } - out: - _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret); TALLOC_FREE(ctx); @@ -2657,12 +2827,13 @@ int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, { const char *username; int ret = PAM_USER_UNKNOWN; - void *tmp = NULL; + const char *tmp = NULL; struct pwb_context *ctx = NULL; - ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); - if (ret) { - goto out; + ret = _pam_winbind_init_context(pamh, flags, argc, argv, + PAM_WINBIND_ACCT_MGMT, &ctx); + if (ret != PAM_SUCCESS) { + return ret; } _PAM_LOG_FUNCTION_ENTER("pam_sm_acct_mgmt", ctx); @@ -2698,7 +2869,7 @@ int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, pam_get_data(pamh, PAM_WINBIND_NEW_AUTHTOK_REQD, (const void **)&tmp); if (tmp != NULL) { - ret = atoi((const char *)tmp); + ret = atoi(tmp); switch (ret) { case PAM_AUTHTOK_EXPIRED: /* fall through, since new token is required in this case */ @@ -2755,9 +2926,10 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags, int ret = PAM_SUCCESS; struct pwb_context *ctx = NULL; - ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); - if (ret) { - goto out; + ret = _pam_winbind_init_context(pamh, flags, argc, argv, + PAM_WINBIND_OPEN_SESSION, &ctx); + if (ret != PAM_SUCCESS) { + return ret; } _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx); @@ -2766,7 +2938,7 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags, /* check and create homedir */ ret = _pam_mkhomedir(ctx); } - out: + _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret); TALLOC_FREE(ctx); @@ -2781,14 +2953,14 @@ int pam_sm_close_session(pam_handle_t *pamh, int flags, int ret = PAM_SUCCESS; struct pwb_context *ctx = NULL; - ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); - if (ret) { - goto out; + ret = _pam_winbind_init_context(pamh, flags, argc, argv, + PAM_WINBIND_CLOSE_SESSION, &ctx); + if (ret != PAM_SUCCESS) { + return ret; } _PAM_LOG_FUNCTION_ENTER("pam_sm_close_session", ctx); -out: _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, ret); TALLOC_FREE(ctx); @@ -2824,8 +2996,8 @@ static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx, char *new_authtok_reqd_during_auth = NULL; struct passwd *pwd = NULL; - _pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, - &new_authtok_reqd_during_auth); + pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, + (const void **) &new_authtok_reqd_during_auth); pam_set_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH, NULL, NULL); @@ -2856,7 +3028,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, /* */ const char *user; - char *pass_old, *pass_new; + const char *pass_old; + const char *pass_new; /* */ char *Announce; @@ -2866,9 +3039,10 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, struct wbcAuthErrorInfo *error = NULL; struct pwb_context *ctx = NULL; - ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); - if (ret) { - goto out; + ret = _pam_winbind_init_context(pamh, flags, argc, argv, + PAM_WINBIND_CHAUTHTOK, &ctx); + if (ret != PAM_SUCCESS) { + return ret; } _PAM_LOG_FUNCTION_ENTER("pam_sm_chauthtok", ctx); @@ -2882,7 +3056,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, * First get the name of a user */ ret = pam_get_user(pamh, &user, _("Username: ")); - if (ret) { + if (ret != PAM_SUCCESS) { _pam_log(ctx, LOG_ERR, "password - could not identify user"); goto out; @@ -2946,7 +3120,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, ret = winbind_auth_request(ctx, user, pass_old, NULL, NULL, 0, - &error, NULL, NULL, + &error, NULL, &pwdlastset_prelim, NULL); if (ret != PAM_ACCT_EXPIRED && @@ -2979,7 +3153,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, * get the old token back. */ - ret = _pam_get_item(pamh, PAM_OLDAUTHTOK, &pass_old); + ret = pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **) &pass_old); if (ret != PAM_SUCCESS) { _pam_log(ctx, LOG_NOTICE, @@ -3029,8 +3203,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, * By reaching here we have approved the passwords and must now * rebuild the password database file. */ - _pam_get_data(pamh, PAM_WINBIND_PWD_LAST_SET, - &pwdlastset_update); + pam_get_data(pamh, PAM_WINBIND_PWD_LAST_SET, + (const void **) &pwdlastset_update); /* * if cached creds were enabled, make sure to set the @@ -3043,9 +3217,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, ret = winbind_chauthtok_request(ctx, user, pass_old, pass_new, pwdlastset_update); - if (ret) { - _pam_overwrite(pass_new); - _pam_overwrite(pass_old); + if (ret != PAM_SUCCESS) { pass_old = pass_new = NULL; goto out; } @@ -3056,7 +3228,6 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, const char *cctype = NULL; int warn_pwd_expire; struct wbcLogonUserInfo *info = NULL; - struct wbcUserPasswordPolicyInfo *policy = NULL; member = get_member_from_config(ctx); cctype = get_krb5_cc_type_from_config(ctx); @@ -3072,10 +3243,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, ret = winbind_auth_request(ctx, user, pass_new, member, cctype, 0, - &error, &info, &policy, + &error, &info, NULL, &username_ret); - _pam_overwrite(pass_new); - _pam_overwrite(pass_old); pass_old = pass_new = NULL; if (ret == PAM_SUCCESS) { @@ -3088,9 +3257,9 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, /* warn a user if the password is about to * expire soon */ - _pam_warn_password_expiry(ctx, user_info, policy, + _pam_warn_password_expiry(ctx, user_info, warn_pwd_expire, - NULL); + NULL, NULL); /* set some info3 info for other modules in the * stack */ @@ -3108,10 +3277,13 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, free(username_ret); } - wbcFreeMemory(info); - wbcFreeMemory(policy); } + if (info && info->blobs) { + wbcFreeMemory(info->blobs); + } + wbcFreeMemory(info); + goto out; } } else {