Revert "nsswitch: libwbclient has vnum 1 now."
[samba.git] / nsswitch / pam_winbind.c
index d049bdb1e7d538bce21ac1628be6282c0502f337..e651ce0e498796a4c78c2b640c316f8fcad53634 100644 (file)
@@ -11,6 +11,8 @@
 */
 
 #include "pam_winbind.h"
+#define CONST_DISCARD(type,ptr) ((type)(void *)ptr)
+
 
 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(CONST_DISCARD(char *, config_file));
        if (d == NULL) {
                goto config_from_pam;
        }
 
-       if (iniparser_getboolean(d, "global:debug", false)) {
+       if (iniparser_getboolean(d, CONST_DISCARD(char *, "global:debug"), false)) {
                ctrl |= WINBIND_DEBUG_ARG;
        }
 
-       if (iniparser_getboolean(d, "global:debug_state", false)) {
+       if (iniparser_getboolean(d, CONST_DISCARD(char *, "global:debug_state"), false)) {
                ctrl |= WINBIND_DEBUG_STATE;
        }
 
-       if (iniparser_getboolean(d, "global:cached_login", false)) {
+       if (iniparser_getboolean(d, CONST_DISCARD(char *, "global:cached_login"), false)) {
                ctrl |= WINBIND_CACHED_LOGIN;
        }
 
-       if (iniparser_getboolean(d, "global:krb5_auth", false)) {
+       if (iniparser_getboolean(d, CONST_DISCARD(char *, "global:krb5_auth"), false)) {
                ctrl |= WINBIND_KRB5_AUTH;
        }
 
-       if (iniparser_getboolean(d, "global:silent", false)) {
+       if (iniparser_getboolean(d, CONST_DISCARD(char *, "global:silent"), false)) {
                ctrl |= WINBIND_SILENT;
        }
 
-       if (iniparser_getstr(d, "global:krb5_ccache_type") != NULL) {
+       if (iniparser_getstr(d, CONST_DISCARD(char *, "global:krb5_ccache_type")) != 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_getstr(d, CONST_DISCARD(char *, "global:require-membership-of"))
+            != NULL) ||
+           (iniparser_getstr(d, CONST_DISCARD(char *, "global:require_membership_of"))
+            != NULL)) {
                ctrl |= WINBIND_REQUIRED_MEMBERSHIP;
        }
 
-       if (iniparser_getboolean(d, "global:try_first_pass", false)) {
+       if (iniparser_getboolean(d, CONST_DISCARD(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, CONST_DISCARD(char *, "global:warn_pwd_expire"), 0)) {
                ctrl |= WINBIND_WARN_PWD_EXPIRE;
        }
 
-       if (iniparser_getboolean(d, "global:mkhomedir", false)) {
+       if (iniparser_getboolean(d, CONST_DISCARD(char *, "global:mkhomedir"), false)) {
                ctrl |= WINBIND_MKHOMEDIR;
        }
 
@@ -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,7 +1077,8 @@ static bool winbind_name_to_sid_string(struct pwb_context *ctx,
                                       char *sid_list_buffer,
                                       int sid_list_buffer_size)
 {
-       const char* sid_string;
+       const char* sid_string = NULL;
+       char *sid_str = NULL;
 
        /* lookup name? */
        if (IS_SID_STRING(name)) {
@@ -984,7 +1087,6 @@ static bool winbind_name_to_sid_string(struct pwb_context *ctx,
                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);
@@ -1001,15 +1103,16 @@ static bool winbind_name_to_sid_string(struct pwb_context *ctx,
                        return false;
                }
 
-               wbcFreeMemory(sid_str);
                sid_string = sid_str;
        }
 
        if (!safe_append_string(sid_list_buffer, sid_string,
                                sid_list_buffer_size)) {
+               wbcFreeMemory(sid_str);
                return false;
        }
 
+       wbcFreeMemory(sid_str);
        return true;
 }
 
@@ -1034,13 +1137,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 +1155,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 +1187,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;
@@ -1298,12 +1432,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 +1451,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;
        }
@@ -1717,11 +1851,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 +1867,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);
 
@@ -1761,7 +1902,7 @@ static int winbind_auth_request(struct pwb_context *ctx,
        if (logon.blobs) {
                wbcFreeMemory(logon.blobs);
        }
-       if (info && info->blobs) {
+       if (info && info->blobs && !p_info) {
                wbcFreeMemory(info->blobs);
        }
        if (error && !p_error) {
@@ -1857,22 +1998,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"));
@@ -2281,8 +2422,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 +2433,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 +2469,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 +2518,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) {
@@ -2406,6 +2557,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 +2580,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 +3195,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 +3223,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 +3237,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 +3255,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 {