s3-printing: Restrict printing=cups to systems with cups development headers at build...
[gd/samba-autobuild/.git] / nsswitch / pam_winbind.c
index d049bdb1e7d538bce21ac1628be6282c0502f337..e3e9af19a3220691d431b6078cc1da033d60ba52 100644 (file)
@@ -10,6 +10,8 @@
    <sopwith@redhat.com> (see copyright below for full details)
 */
 
+#define UID_WRAPPER_NOT_REPLACE
+
 #include "pam_winbind.h"
 
 static int wbc_error_to_pam_error(wbcErr status)
@@ -154,7 +156,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;
@@ -410,49 +412,51 @@ static int _pam_parse(const pam_handle_t *pamh,
                config_file = PAM_WINBIND_CONFIG_FILE;
        }
 
-       d = iniparser_load(config_file);
+       d = iniparser_load(discard_const_p(char, config_file));
        if (d == NULL) {
                goto config_from_pam;
        }
 
-       if (iniparser_getboolean(d, "global:debug", false)) {
+       if (iniparser_getboolean(d, discard_const_p(char, "global:debug"), false)) {
                ctrl |= WINBIND_DEBUG_ARG;
        }
 
-       if (iniparser_getboolean(d, "global:debug_state", false)) {
+       if (iniparser_getboolean(d, discard_const_p(char, "global:debug_state"), false)) {
                ctrl |= WINBIND_DEBUG_STATE;
        }
 
-       if (iniparser_getboolean(d, "global:cached_login", false)) {
+       if (iniparser_getboolean(d, discard_const_p(char, "global:cached_login"), false)) {
                ctrl |= WINBIND_CACHED_LOGIN;
        }
 
-       if (iniparser_getboolean(d, "global:krb5_auth", false)) {
+       if (iniparser_getboolean(d, discard_const_p(char, "global:krb5_auth"), false)) {
                ctrl |= WINBIND_KRB5_AUTH;
        }
 
-       if (iniparser_getboolean(d, "global:silent", false)) {
+       if (iniparser_getboolean(d, discard_const_p(char, "global:silent"), false)) {
                ctrl |= WINBIND_SILENT;
        }
 
-       if (iniparser_getstr(d, "global:krb5_ccache_type") != NULL) {
+       if (iniparser_getstring(d, discard_const_p(char, "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 ((iniparser_getstring(d, discard_const_p(char, "global:require-membership-of"), NULL)
+            != NULL) ||
+           (iniparser_getstring(d, discard_const_p(char, "global:require_membership_of"), NULL)
+            != NULL)) {
                ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
        }
 
-       if (iniparser_getboolean(d, "global:try_first_pass", false)) {
+       if (iniparser_getboolean(d, discard_const_p(char, "global:try_first_pass"), false)) {
                ctrl |= WINBIND_TRY_FIRST_PASS_ARG;
        }
 
-       if (iniparser_getint(d, "global:warn_pwd_expire", 0)) {
+       if (iniparser_getint(d, discard_const_p(char, "global:warn_pwd_expire"), 0)) {
                ctrl |= WINBIND_WARN_PWD_EXPIRE;
        }
 
-       if (iniparser_getboolean(d, "global:mkhomedir", false)) {
+       if (iniparser_getboolean(d, discard_const_p(char, "global:mkhomedir"), false)) {
                ctrl |= WINBIND_MKHOMEDIR;
        }
 
@@ -534,7 +538,7 @@ static int _pam_winbind_init_context(pam_handle_t *pamh,
        textdomain_init();
 #endif
 
-       r = TALLOC_ZERO_P(NULL, struct pwb_context);
+       r = talloc_zero(NULL, struct pwb_context);
        if (!r) {
                return PAM_BUF_ERR;
        }
@@ -803,6 +807,43 @@ 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, *pmsg;
+       struct pam_response *resp = NULL;
+       const char *prompt;
+       int ret;
+       bool retval = false;
+       prompt = _("Do you want to change your password now?");
+       pmsg = &msg;
+       msg.msg_style = PAM_RADIO_TYPE;
+       msg.msg = prompt;
+       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 (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 +860,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 +898,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;
        }
 
@@ -879,7 +973,8 @@ 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;
@@ -892,6 +987,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,14 +1006,16 @@ 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)) {
+                                             already_expired,
+                                             change_pwd)) {
                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) {
+       if ((policy->expire == (int64_t)-1) ||
+           (policy->expire == 0)) {
                return;
        }
 
@@ -922,7 +1023,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)) {
+                                             already_expired,
+                                             change_pwd)) {
                return;
        }
 
@@ -975,16 +1077,15 @@ 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);
@@ -996,20 +1097,13 @@ static bool winbind_name_to_sid_string(struct pwb_context *ctx,
                        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 +1128,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 +1146,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 +1178,21 @@ 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);
+               /*
+                * 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 +1214,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,7 +1241,7 @@ 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;
        }
 
@@ -1126,6 +1251,7 @@ static void _pam_setup_krb5_env(struct pwb_context *ctx,
                         "failed to set KRB5CCNAME to %s: %s",
                         var, pam_strerror(ctx->pamh, ret));
        }
+       free(var);
 }
 
 /**
@@ -1298,12 +1424,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 +1443,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;
        }
@@ -1639,7 +1765,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;
@@ -1717,11 +1843,13 @@ static int winbind_auth_request(struct pwb_context *ctx,
        if ((ret == PAM_SUCCESS) && user_info && policy && 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,
                                          warn_pwd_expire,
-                                         &already_expired);
+                                         &already_expired,
+                                         &change_pwd);
 
                if (already_expired == true) {
 
@@ -1731,15 +1859,20 @@ static int winbind_auth_request(struct pwb_context *ctx,
                                       "Password has expired "
                                       "(Password was last set: %lld, "
                                       "the policy says it should expire here "
-                                      "%lld (now it's: %lu))\n",
+                                      "%lld (now it's: %ld))\n",
                                       (long long int)last_set,
                                       (long long int)last_set +
                                       policy->expire,
-                                      time(NULL));
+                                      (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 +1891,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) {
@@ -1815,7 +1946,7 @@ 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;
@@ -1857,22 +1988,22 @@ static int winbind_chauthtok_request(struct pwb_context *ctx,
                switch (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"));
@@ -2147,7 +2278,7 @@ static const char *get_conf_item_string(struct pwb_context *ctx,
                        goto out;
                }
 
-               parm_opt = iniparser_getstr(ctx->dict, key);
+               parm_opt = iniparser_getstring(ctx->dict, key, NULL);
                TALLOC_FREE(key);
 
                _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n",
@@ -2281,8 +2412,9 @@ 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;
 
        /* This cannot work when the winbind separator = @ */
 
@@ -2291,9 +2423,18 @@ static char* winbind_upn_to_username(struct pwb_context *ctx,
                return NULL;
        }
 
+       name = talloc_strdup(ctx, upn);
+       if (!name) {
+               return NULL;
+       }
+       if ((p = strchr(name, '@')) != NULL) {
+               *p = 0;
+               domain = p + 1;
+       }
+
        /* Convert the UPN to a SID */
 
-       wbc_status = wbcLookupName("", upn, &sid, &type);
+       wbc_status = wbcLookupName(domain, name, &sid, &type);
        if (!WBC_ERROR_IS_OK(wbc_status)) {
                return NULL;
        }
@@ -2318,6 +2459,8 @@ static int _pam_delete_cred(pam_handle_t *pamh, int flags,
        const char *user;
        wbcErr wbc_status = WBC_ERR_SUCCESS;
 
+       ZERO_STRUCT(logoff);
+
        retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx);
        if (retval) {
                goto out;
@@ -2365,8 +2508,6 @@ static int _pam_delete_cred(pam_handle_t *pamh, int flags,
                wbc_flags = WBFLAG_PAM_KRB5 |
                        WBFLAG_PAM_CONTACT_TRUSTDOM;
 
-               ZERO_STRUCT(logoff);
-
                logoff.username         = user;
 
                if (ccname) {
@@ -2374,7 +2515,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;
@@ -2406,6 +2547,7 @@ static int _pam_delete_cred(pam_handle_t *pamh, int flags,
                                                     user, "wbcLogoffUser");
                wbcFreeMemory(error);
                wbcFreeMemory(logoff.blobs);
+               logoff.blobs = NULL;
 
                if (!WBC_ERROR_IS_OK(wbc_status)) {
                        _pam_log(ctx, LOG_INFO,
@@ -2428,7 +2570,7 @@ out:
         * Delete the krb5 ccname variable from the PAM environment
         * if it was set by winbind.
         */
-       if (ctx->ctrl & WINBIND_KRB5_AUTH) {
+       if ((ctx->ctrl & WINBIND_KRB5_AUTH) && pam_getenv(pamh, "KRB5CCNAME")) {
                pam_putenv(pamh, "KRB5CCNAME");
        }
 
@@ -3043,8 +3185,6 @@ 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);
                        pass_old = pass_new = NULL;
                        goto out;
                }
@@ -3073,8 +3213,6 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                                                   member, cctype, 0,
                                                   &error, &info, &policy,
                                                   NULL, &username_ret);
-                       _pam_overwrite(pass_new);
-                       _pam_overwrite(pass_old);
                        pass_old = pass_new = NULL;
 
                        if (ret == PAM_SUCCESS) {
@@ -3089,7 +3227,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
                                 * expire soon */
                                _pam_warn_password_expiry(ctx, user_info, policy,
                                                          warn_pwd_expire,
-                                                         NULL);
+                                                         NULL, NULL);
 
                                /* set some info3 info for other modules in the
                                 * stack */
@@ -3107,10 +3245,14 @@ 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);
+                       wbcFreeMemory(policy);
+
                        goto out;
                }
        } else {