docs: fix a typo in history file
[bbaumbach/samba-autobuild/.git] / nsswitch / pam_winbind.c
index 9f855564bcca4af13ea8872e999b38eb2a47b4b8..5e5cf0af8b89158d8bfe391f514667929861d885 100644 (file)
    <sopwith@redhat.com> (see copyright below for full details)
 */
 
-#define UID_WRAPPER_NOT_REPLACE
-
 #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) {
@@ -140,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)
@@ -165,6 +174,10 @@ static inline void textdomain_init(void)
 
 
 /* some syslogging */
+static void _pam_log_int(const pam_handle_t *pamh,
+                        int err,
+                        const char *format,
+                        va_list args) PRINTF_ATTRIBUTE(3, 0);
 
 #ifdef HAVE_PAM_VSYSLOG
 static void _pam_log_int(const pam_handle_t *pamh,
@@ -180,21 +193,26 @@ static void _pam_log_int(const pam_handle_t *pamh,
                         const char *format,
                         va_list args)
 {
-       char *format2 = NULL;
+       char *base = NULL;
+       va_list args2;
        const char *service;
+       int ret;
+
+       va_copy(args2, args);
 
        pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
 
-       format2 = (char *)malloc(strlen(MODULE_NAME)+strlen(format)+strlen(service)+5);
-       if (format2 == NULL) {
+       ret = vasprintf(&base, format, args);
+       if (ret == -1) {
                /* what else todo ? */
-               vsyslog(err, format, args);
+               vsyslog(err, format, args2);
+               va_end(args2);
                return;
        }
 
-       sprintf(format2, "%s(%s): %s", MODULE_NAME, service, format);
-       vsyslog(err, format2, args);
-       SAFE_FREE(format2);
+       syslog(err, "%s(%s): %s", MODULE_NAME, service, base);
+       SAFE_FREE(base);
+       va_end(args2);
 }
 #endif /* HAVE_PAM_VSYSLOG */
 
@@ -261,7 +279,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;
        }
 
@@ -330,10 +348,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;
        }
 
@@ -369,13 +406,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;
@@ -393,54 +431,58 @@ static int _pam_parse(const pam_handle_t *pamh,
                config_file = PAM_WINBIND_CONFIG_FILE;
        }
 
-       d = iniparser_load(discard_const_p(char, config_file));
+       d = tiniparser_load(config_file);
        if (d == NULL) {
                goto config_from_pam;
        }
 
-       if (iniparser_getboolean(d, discard_const_p(char, "global:debug"), false)) {
+       if (tiniparser_getboolean(d, "global:debug", false)) {
                ctrl |= WINBIND_DEBUG_ARG;
        }
 
-       if (iniparser_getboolean(d, discard_const_p(char, "global:debug_state"), false)) {
+       if (tiniparser_getboolean(d, "global:debug_state", false)) {
                ctrl |= WINBIND_DEBUG_STATE;
        }
 
-       if (iniparser_getboolean(d, discard_const_p(char, "global:cached_login"), false)) {
+       if (tiniparser_getboolean(d, "global:cached_login", false)) {
                ctrl |= WINBIND_CACHED_LOGIN;
        }
 
-       if (iniparser_getboolean(d, discard_const_p(char, "global:krb5_auth"), false)) {
+       if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
                ctrl |= WINBIND_KRB5_AUTH;
        }
 
-       if (iniparser_getboolean(d, discard_const_p(char, "global:silent"), false)) {
+       if (tiniparser_getboolean(d, "global:silent", false)) {
                ctrl |= WINBIND_SILENT;
        }
 
-       if (iniparser_getstring(d, discard_const_p(char, "global:krb5_ccache_type"), NULL) != NULL) {
+       if (tiniparser_getstring_nonempty(d, "global:krb5_ccache_type", NULL) != NULL) {
                ctrl |= WINBIND_KRB5_CCACHE_TYPE;
        }
 
-       if ((iniparser_getstring(d, discard_const_p(char, "global:require-membership-of"), NULL)
+       if ((tiniparser_getstring_nonempty(d, "global:require-membership-of", NULL)
             != NULL) ||
-           (iniparser_getstring(d, discard_const_p(char, "global:require_membership_of"), NULL)
+           (tiniparser_getstring_nonempty(d, "global:require_membership_of", NULL)
             != NULL)) {
                ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
        }
 
-       if (iniparser_getboolean(d, discard_const_p(char, "global:try_first_pass"), false)) {
+       if (tiniparser_getboolean(d, "global:try_first_pass", false)) {
                ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
        }
 
-       if (iniparser_getint(d, discard_const_p(char, "global:warn_pwd_expire"), 0)) {
+       if (tiniparser_getint(d, "global:warn_pwd_expire", 0)) {
                ctrl |= WINBIND_WARN_PWD_EXPIRE;
        }
 
-       if (iniparser_getboolean(d, discard_const_p(char, "global:mkhomedir"), false)) {
+       if (tiniparser_getboolean(d, "global:mkhomedir", false)) {
                ctrl |= WINBIND_MKHOMEDIR;
        }
 
+       if (tiniparser_getboolean(d, "global:pwd_change_prompt", false)) {
+               ctrl |= WINBIND_PWD_CHANGE_PROMPT;
+       }
+
 config_from_pam:
        /* step through arguments */
        for (i=argc,v=argv; i-- > 0; ++v) {
@@ -454,17 +496,23 @@ config_from_pam:
                        ctrl |= WINBIND_SILENT;
                else if (!strcasecmp(*v, "use_authtok"))
                        ctrl |= WINBIND_USE_AUTHTOK_ARG;
+               else if (!strcasecmp(*v, "try_authtok"))
+                       ctrl |= WINBIND_TRY_AUTHTOK_ARG;
                else if (!strcasecmp(*v, "use_first_pass"))
                        ctrl |= WINBIND_USE_FIRST_PASS_ARG;
                else if (!strcasecmp(*v, "try_first_pass"))
                        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;
@@ -475,7 +523,12 @@ 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 (!strcasecmp(*v, "pwd_change_prompt"))
+                       ctrl |= WINBIND_PWD_CHANGE_PROMPT;
+               else if (type != PAM_WINBIND_CLEANUP) {
                        __pam_log(pamh, ctrl, LOG_ERR,
                                 "pam_parse: unknown option: %s", *v);
                        return -1;
@@ -487,7 +540,7 @@ config_from_pam:
                *result_d = d;
        } else {
                if (d) {
-                       iniparser_freedict(d);
+                       tiniparser_freedict(d);
                }
        }
 
@@ -501,9 +554,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;
 }
 
@@ -511,9 +566,13 @@ 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;
+       const char *service = NULL;
+       char service_name[32] = {0};
+       int ctrl_code;
 
 #ifdef HAVE_GETTEXT
        textdomain_init();
@@ -530,12 +589,25 @@ 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;
        }
 
+       pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
+
+       snprintf(service_name, sizeof(service_name), "PAM_WINBIND[%s]", service);
+
+       wbcSetClientProcessName(service_name);
+
        *ctx_p = r;
 
        return PAM_SUCCESS;
@@ -545,7 +617,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 "
@@ -563,7 +635,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",
@@ -571,7 +643,7 @@ static const struct ntstatus_errors {
        {"NT_STATUS_PWD_TOO_SHORT",
                N_("Password too short")},
        {"NT_STATUS_PWD_TOO_RECENT",
-               N_("The password of this user is too recent to change")},
+               N_("The password was recently changed and cannot be changed again before %s")},
        {"NT_STATUS_PWD_HISTORY_CONFLICT",
                N_("Password is already in password history")},
        {"NT_STATUS_PASSWORD_EXPIRED",
@@ -620,16 +692,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;
+       const struct 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);
        }
 
@@ -643,7 +715,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) {
@@ -663,6 +736,11 @@ static int _make_remark(struct pwb_context *ctx,
        return retval;
 }
 
+static int _make_remark_v(struct pwb_context *ctx,
+                         int type,
+                         const char *format,
+                         va_list args) PRINTF_ATTRIBUTE(3, 0);
+
 static int _make_remark_v(struct pwb_context *ctx,
                          int type,
                          const char *format,
@@ -728,6 +806,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) {
@@ -785,13 +868,18 @@ static int wbc_auth_error_to_pam_error(struct pwb_context *ctx,
        }
 
        ret = wbc_error_to_pam_error(status);
+       _pam_log(ctx, LOG_ERR,
+                "request %s failed: %s, PAM error: %s (%d)!",
+                fn, wbcErrorString(status),
+                _pam_error_code_str(ret), ret);
        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, *pmsg;
+       struct pam_message msg;
+       const struct pam_message *pmsg;
        struct pam_response *resp = NULL;
        int ret;
        bool retval = false;
@@ -894,7 +982,8 @@ static bool _pam_send_password_expiry_message(struct pwb_context *ctx,
                 * successfully sent the warning message.
                 * Give the user a chance to change pwd.
                 */
-               if (ret == PAM_SUCCESS) {
+               if (ret == PAM_SUCCESS &&
+                   (ctx->ctrl & WINBIND_PWD_CHANGE_PROMPT)) {
                        if (change_pwd) {
                                retval = _pam_winbind_change_pwd(ctx);
                                if (retval) {
@@ -924,7 +1013,8 @@ static bool _pam_send_password_expiry_message(struct pwb_context *ctx,
                 * successfully sent the warning message.
                 * Give the user a chance to change pwd.
                 */
-               if (ret == PAM_SUCCESS) {
+               if (ret == PAM_SUCCESS &&
+                   (ctx->ctrl & WINBIND_PWD_CHANGE_PROMPT)) {
                        if (change_pwd) {
                                retval = _pam_winbind_change_pwd(ctx);
                                if (retval) {
@@ -950,7 +1040,6 @@ 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 *change_pwd)
@@ -958,7 +1047,7 @@ static void _pam_warn_password_expiry(struct pwb_context *ctx,
        time_t now = time(NULL);
        time_t next_change = 0;
 
-       if (!info || !policy) {
+       if (info == NULL) {
                return;
        }
 
@@ -990,23 +1079,6 @@ static void _pam_warn_password_expiry(struct pwb_context *ctx,
                return;
        }
 
-       /* now check for the global password policy */
-       /* good catch from Ralf Haferkamp: an expiry of "never" is translated
-        * to -1 */
-       if ((policy->expire == (int64_t)-1) ||
-           (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,
-                                             change_pwd)) {
-               return;
-       }
-
        /* no warning sent */
 }
 
@@ -1028,15 +1100,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);
 }
 
 /**
@@ -1069,7 +1135,11 @@ static bool winbind_name_to_sid_string(struct pwb_context *ctx,
                _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);
@@ -1163,10 +1233,16 @@ static bool winbind_name_list_to_sid_string_list(struct pwb_context *ctx,
                _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 ','.
+                * It is malformatted parameter here, overwrite the last ','.
                 */
                len = strlen(sid_list_buffer);
                if ((len != 0) && (sid_list_buffer[len - 1] == ',')) {
@@ -1225,7 +1301,7 @@ static void _pam_setup_krb5_env(struct pwb_context *ctx,
        }
 
        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));
@@ -1291,7 +1367,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));
@@ -1311,14 +1387,16 @@ static void _pam_set_data_string(struct pwb_context *ctx,
 static void _pam_set_data_info3(struct pwb_context *ctx,
                                const struct wbcAuthUserInfo *info)
 {
-       _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR,
+       if (info != NULL) {
+               _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR,
                             info->home_directory);
-       _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT,
+               _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT,
                             info->logon_script);
-       _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER,
+               _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER,
                             info->logon_server);
-       _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH,
+               _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH,
                             info->profile_path);
+       }
 }
 
 /**
@@ -1512,14 +1590,23 @@ static int _pam_create_homedir(struct pwb_context *ctx,
                               const char *dirname,
                               mode_t mode)
 {
-       struct stat sbuf;
+       int ret;
 
-       if (stat(dirname, &sbuf) == 0) {
-               return PAM_SUCCESS;
-       }
+       ret = mkdir(dirname, mode);
+       if (ret != 0 && errno == EEXIST) {
+               struct stat sbuf;
 
-       if (mkdir(dirname, mode) != 0) {
+               ret = stat(dirname, &sbuf);
+               if (ret != 0) {
+                       return PAM_PERM_DENIED;
+               }
 
+               if (!S_ISDIR(sbuf.st_mode)) {
+                       return PAM_PERM_DENIED;
+               }
+       }
+
+       if (ret != 0) {
                _make_remark_format(ctx, PAM_TEXT_INFO,
                                    _("Creating directory: %s failed: %s"),
                                    dirname, strerror(errno));
@@ -1619,7 +1706,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;
                }
        }
@@ -1638,23 +1725,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[] = {
@@ -1783,7 +1864,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);
@@ -1801,10 +1886,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;
@@ -1819,13 +1900,13 @@ 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,
                                          &change_pwd);
@@ -1833,15 +1914,15 @@ static int winbind_auth_request(struct pwb_context *ctx,
                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 "
+                                      "it must be changed here "
                                       "%lld (now it's: %ld))\n",
                                       (long long int)last_set,
-                                      (long long int)last_set +
-                                      policy->expire,
+                                      (long long int)must_set,
                                       (long)time(NULL));
 
                        return PAM_AUTHTOK_EXPIRED;
@@ -1873,6 +1954,11 @@ static int winbind_auth_request(struct pwb_context *ctx,
        wbcFreeMemory(logon.blobs);
        if (info && info->blobs && !p_info) {
                wbcFreeMemory(info->blobs);
+               /*
+                * We set blobs to NULL to prevent a use after free in the
+                * in the wbcLogonUserInfoDestructor
+                */
+               info->blobs = NULL;
        }
        if (error && !p_error) {
                wbcFreeMemory(error);
@@ -1880,9 +1966,6 @@ static int winbind_auth_request(struct pwb_context *ctx,
        if (info && !p_info) {
                wbcFreeMemory(info);
        }
-       if (policy && !p_policy) {
-               wbcFreeMemory(policy);
-       }
 
        return ret;
 }
@@ -1930,7 +2013,11 @@ static int winbind_chauthtok_request(struct pwb_context *ctx,
        params.new_password.plaintext   = newpass;
        params.flags                    = flags;
 
-       wbc_status = wbcChangeUserPasswordEx(&params, &error, &reject_reason, &policy);
+       wbc_status = wbcCtxChangeUserPasswordEx(ctx->wbc_ctx,
+                                               &params,
+                                               &error,
+                                               &reject_reason,
+                                               &policy);
        ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
                                          user, "wbcChangeUserPasswordEx");
 
@@ -1964,14 +2051,24 @@ 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_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");
+                                       time_t next_change = pwd_last_set + min_pwd_age;
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
+                                       _make_remark_format(ctx, PAM_ERROR_MSG,
+                                               _get_ntstatus_error_string("NT_STATUS_PWD_TOO_RECENT"),
+                                               ctime(&next_change));
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+                                       goto done;
                                }
                                break;
                        case WBC_PWD_CHANGE_PASSWORD_TOO_SHORT:
@@ -2034,7 +2131,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",
@@ -2130,7 +2227,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;
 
@@ -2262,7 +2360,7 @@ static const char *get_conf_item_string(struct pwb_context *ctx,
                        goto out;
                }
 
-               parm_opt = iniparser_getstring(ctx->dict, key, NULL);
+               parm_opt = tiniparser_getstring_nonempty(ctx->dict, key, NULL);
                TALLOC_FREE(key);
 
                _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n",
@@ -2310,7 +2408,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,
@@ -2332,7 +2430,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",
@@ -2345,7 +2443,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;
@@ -2363,8 +2461,9 @@ static char winbind_get_separator(struct pwb_context *ctx)
 {
        wbcErr wbc_status;
        static struct wbcInterfaceDetails *details = NULL;
+       char result;
 
-       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",
@@ -2376,7 +2475,9 @@ static char winbind_get_separator(struct pwb_context *ctx)
                return '\0';
        }
 
-       return details->winbind_separator;
+       result = details->winbind_separator;
+       wbcFreeMemory(details);
+       return result;
 }
 
 
@@ -2384,7 +2485,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.
  */
@@ -2399,6 +2500,7 @@ static char* winbind_upn_to_username(struct pwb_context *ctx,
        char *domain = NULL;
        char *name;
        char *p;
+       char *result;
 
        /* This cannot work when the winbind separator = @ */
 
@@ -2411,30 +2513,38 @@ static char* winbind_upn_to_username(struct pwb_context *ctx,
        if (!name) {
                return NULL;
        }
-       if ((p = strchr(name, '@')) != NULL) {
-               *p = 0;
-               domain = p + 1;
+
+       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(domain, name, &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%c%s", domain, sep, 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;
@@ -2445,9 +2555,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);
@@ -2461,7 +2571,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;
@@ -2526,10 +2636,9 @@ 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);
                wbcFreeMemory(logoff.blobs);
                logoff.blobs = NULL;
 
@@ -2549,6 +2658,7 @@ out:
                retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
                     user, "wbcLogoffUser");
        }
+       wbcFreeMemory(error);
 
        /*
         * Delete the krb5 ccname variable from the PAM environment
@@ -2565,7 +2675,81 @@ out:
        return retval;
 }
 
-PAM_EXTERN
+#ifdef SECURITY_OPENPAM_H_INCLUDED
+/*
+ * Logic below is copied from openpam_check_error_code() in
+ *./contrib/openpam/lib/libpam/openpam_dispatch.c on FreeBSD.
+ */
+static int openpam_convert_error_code(struct pwb_context *ctx,
+                                     enum pam_winbind_request_type req,
+                                     int r)
+{
+       if (r == PAM_SUCCESS ||
+           r == PAM_SYSTEM_ERR ||
+           r == PAM_SERVICE_ERR ||
+           r == PAM_BUF_ERR ||
+           r == PAM_CONV_ERR ||
+           r == PAM_PERM_DENIED ||
+           r == PAM_ABORT) {
+               return r;
+       }
+
+       /* specific winbind request types */
+       switch (req) {
+       case PAM_WINBIND_AUTHENTICATE:
+               if (r == PAM_AUTH_ERR ||
+                   r == PAM_CRED_INSUFFICIENT ||
+                   r == PAM_AUTHINFO_UNAVAIL ||
+                   r == PAM_USER_UNKNOWN ||
+                   r == PAM_MAXTRIES) {
+                       return r;
+               }
+               break;
+       case PAM_WINBIND_SETCRED:
+               if (r == PAM_CRED_UNAVAIL ||
+                   r == PAM_CRED_EXPIRED ||
+                   r == PAM_USER_UNKNOWN ||
+                   r == PAM_CRED_ERR) {
+                       return r;
+               }
+               break;
+       case PAM_WINBIND_ACCT_MGMT:
+               if (r == PAM_USER_UNKNOWN ||
+                   r == PAM_AUTH_ERR ||
+                   r == PAM_NEW_AUTHTOK_REQD ||
+                   r == PAM_ACCT_EXPIRED) {
+                       return r;
+               }
+               break;
+       case PAM_WINBIND_OPEN_SESSION:
+       case PAM_WINBIND_CLOSE_SESSION:
+               if (r == PAM_SESSION_ERR) {
+                       return r;
+               }
+               break;
+       case PAM_WINBIND_CHAUTHTOK:
+               if (r == PAM_PERM_DENIED ||
+                   r == PAM_AUTHTOK_ERR ||
+                   r == PAM_AUTHTOK_RECOVERY_ERR ||
+                   r == PAM_AUTHTOK_LOCK_BUSY ||
+                   r == PAM_AUTHTOK_DISABLE_AGING ||
+                   r == PAM_TRY_AGAIN) {
+                       return r;
+               }
+               break;
+       default:
+               break;
+       }
+       _pam_log(ctx, LOG_INFO,
+                "Converting PAM error [%d] to PAM_SERVICE_ERR.\n", r);
+       return PAM_SERVICE_ERR;
+};
+#define pam_error_code(a, b, c) openpam_convert_error_code(a, b, c)
+#else
+#define pam_error_code(a, b, c) (c)
+#endif
+
+_PUBLIC_ PAM_EXTERN
 int pam_sm_authenticate(pam_handle_t *pamh, int flags,
                        int argc, const char **argv)
 {
@@ -2580,9 +2764,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);
@@ -2664,8 +2849,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) {
@@ -2717,24 +2901,24 @@ out:
                _pam_free_data_info3(pamh);
        }
 
-       if (ctx != NULL) {
-               _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval);
-               TALLOC_FREE(ctx);
-       }
+       _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval);
+
+       TALLOC_FREE(ctx);
 
        return retval;
 }
 
-PAM_EXTERN
+_PUBLIC_ PAM_EXTERN
 int pam_sm_setcred(pam_handle_t *pamh, int flags,
                   int argc, const char **argv)
 {
        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);
@@ -2742,7 +2926,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,
@@ -2764,20 +2949,18 @@ int pam_sm_setcred(pam_handle_t *pamh, int flags,
                        break;
        }
 
- out:
-
        _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret);
 
        TALLOC_FREE(ctx);
 
-       return ret;
+       return pam_error_code(ctx, PAM_WINBIND_SETCRED, ret);
 }
 
 /*
  * Account management. We want to verify that the account exists
  * before returning PAM_SUCCESS
  */
-PAM_EXTERN
+_PUBLIC_ PAM_EXTERN
 int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
                   int argc, const char **argv)
 {
@@ -2786,9 +2969,10 @@ int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
        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);
@@ -2827,7 +3011,8 @@ int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
                        ret = atoi(tmp);
                        switch (ret) {
                        case PAM_AUTHTOK_EXPIRED:
-                               /* fall through, since new token is required in this case */
+                               /* Since new token is required in this case */
+                               FALL_THROUGH;
                        case PAM_NEW_AUTHTOK_REQD:
                                _pam_log(ctx, LOG_WARNING,
                                         "pam_sm_acct_mgmt success but %s is set",
@@ -2871,19 +3056,20 @@ int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
 
        TALLOC_FREE(ctx);
 
-       return ret;
+       return pam_error_code(ctx, PAM_WINBIND_ACCT_MGMT, ret);
 }
 
-PAM_EXTERN
+_PUBLIC_ PAM_EXTERN
 int pam_sm_open_session(pam_handle_t *pamh, int flags,
                        int argc, const char **argv)
 {
        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);
@@ -2892,34 +3078,34 @@ 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);
 
-       return ret;
+       return pam_error_code(ctx, PAM_WINBIND_OPEN_SESSION, ret);
 }
 
-PAM_EXTERN
+_PUBLIC_ PAM_EXTERN
 int pam_sm_close_session(pam_handle_t *pamh, int flags,
                         int argc, const char **argv)
 {
        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);
 
-       return ret;
+       return pam_error_code(ctx, PAM_WINBIND_CLOSE_SESSION, ret);
 }
 
 /**
@@ -2947,7 +3133,7 @@ static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx,
         * --- BoYang
         * */
 
-       char *new_authtok_reqd_during_auth = NULL;
+       const char *new_authtok_reqd_during_auth = NULL;
        struct passwd *pwd = NULL;
 
        pam_get_data(ctx->pamh, PAM_WINBIND_NEW_AUTHTOK_REQD_DURING_AUTH,
@@ -2972,7 +3158,7 @@ static bool _pam_require_krb5_auth_after_chauthtok(struct pwb_context *ctx,
 }
 
 
-PAM_EXTERN
+_PUBLIC_ PAM_EXTERN
 int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                     int argc, const char **argv)
 {
@@ -2993,9 +3179,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);
@@ -3009,7 +3196,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;
@@ -3042,7 +3229,15 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
         */
 
        if (flags & PAM_PRELIM_CHECK) {
-               time_t pwdlastset_prelim = 0;
+               time_t *pwdlastset_prelim = NULL;
+
+               pwdlastset_prelim = talloc_zero(NULL, time_t);
+               if (pwdlastset_prelim == NULL) {
+                       _pam_log(ctx, LOG_CRIT,
+                                "password - out of memory");
+                       ret = PAM_BUF_ERR;
+                       goto out;
+               }
 
                /* instruct user what is happening */
 
@@ -3073,8 +3268,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 
                ret = winbind_auth_request(ctx, user, pass_old,
                                           NULL, NULL, 0,
-                                          &error, NULL, NULL,
-                                          &pwdlastset_prelim, NULL);
+                                          &error, NULL,
+                                          pwdlastset_prelim, NULL);
 
                if (ret != PAM_ACCT_EXPIRED &&
                    ret != PAM_AUTHTOK_EXPIRED &&
@@ -3085,7 +3280,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                }
 
                pam_set_data(pamh, PAM_WINBIND_PWD_LAST_SET,
-                            (void *)pwdlastset_prelim, NULL);
+                            pwdlastset_prelim,
+                            _pam_winbind_cleanup_func);
 
                ret = pam_set_item(pamh, PAM_OLDAUTHTOK,
                                   (const void *) pass_old);
@@ -3095,8 +3291,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                                 "failed to set PAM_OLDAUTHTOK");
                }
        } else if (flags & PAM_UPDATE_AUTHTOK) {
-
-               time_t pwdlastset_update = 0;
+               const time_t *pwdlastset_update = NULL;
 
                /*
                 * obtain the proposed password
@@ -3119,6 +3314,9 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                if (on(WINBIND_USE_AUTHTOK_ARG, lctrl)) {
                        lctrl |= WINBIND_USE_FIRST_PASS_ARG;
                }
+               if (on(WINBIND_TRY_AUTHTOK_ARG, lctrl)) {
+                       lctrl |= WINBIND_TRY_FIRST_PASS_ARG;
+               }
                retry = 0;
                ret = PAM_AUTHTOK_ERR;
                while ((ret != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
@@ -3156,8 +3354,9 @@ 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,
-                            (const void **) &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
@@ -3169,8 +3368,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                }
 
                ret = winbind_chauthtok_request(ctx, user, pass_old,
-                                               pass_new, pwdlastset_update);
-               if (ret) {
+                                               pass_new, *pwdlastset_update);
+               if (ret != PAM_SUCCESS) {
                        pass_old = pass_new = NULL;
                        goto out;
                }
@@ -3181,7 +3380,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);
@@ -3197,7 +3395,7 @@ 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);
                        pass_old = pass_new = NULL;
 
@@ -3211,7 +3409,7 @@ 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);
 
@@ -3237,7 +3435,6 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                                wbcFreeMemory(info->blobs);
                        }
                        wbcFreeMemory(info);
-                       wbcFreeMemory(policy);
 
                        goto out;
                }
@@ -3269,7 +3466,7 @@ out:
 
        TALLOC_FREE(ctx);
 
-       return ret;
+       return pam_error_code(ctx, PAM_WINBIND_CHAUTHTOK, ret);
 }
 
 #ifdef PAM_STATIC