diff options
author | Jelmer Vernooij <jelmer@samba.org> | 2008-10-11 14:44:10 +0200 |
---|---|---|
committer | Jelmer Vernooij <jelmer@samba.org> | 2008-10-11 14:44:10 +0200 |
commit | 6a78e56277799672b7ac187c57e546836e136f79 (patch) | |
tree | 87f0336cb1908d01690b74c56a44f4713559b5bc /source3/nsswitch/pam_winbind.c | |
parent | ddbddbd80c80b872cdd36a01f9a3a6bc2eca1b1f (diff) | |
parent | f0a27064869871806343648de3b5a0667118872f (diff) | |
download | samba-6a78e56277799672b7ac187c57e546836e136f79.tar.gz samba-6a78e56277799672b7ac187c57e546836e136f79.tar.bz2 samba-6a78e56277799672b7ac187c57e546836e136f79.zip |
Merge branch 'master' of ssh://git.samba.org/data/git/samba into arc4
Diffstat (limited to 'source3/nsswitch/pam_winbind.c')
-rw-r--r-- | source3/nsswitch/pam_winbind.c | 1132 |
1 files changed, 729 insertions, 403 deletions
diff --git a/source3/nsswitch/pam_winbind.c b/source3/nsswitch/pam_winbind.c index beb0d47baa..8d8868d0ef 100644 --- a/source3/nsswitch/pam_winbind.c +++ b/source3/nsswitch/pam_winbind.c @@ -12,6 +12,42 @@ #include "pam_winbind.h" +static int wbc_error_to_pam_error(wbcErr status) +{ + switch (status) { + case WBC_ERR_SUCCESS: + return PAM_SUCCESS; + case WBC_ERR_NOT_IMPLEMENTED: + return PAM_SERVICE_ERR; + case WBC_ERR_UNKNOWN_FAILURE: + break; + case WBC_ERR_NO_MEMORY: + return PAM_BUF_ERR; + case WBC_ERR_INVALID_SID: + case WBC_ERR_INVALID_PARAM: + break; + case WBC_ERR_WINBIND_NOT_AVAILABLE: + return PAM_AUTHINFO_UNAVAIL; + case WBC_ERR_DOMAIN_NOT_FOUND: + return PAM_AUTHINFO_UNAVAIL; + case WBC_ERR_INVALID_RESPONSE: + return PAM_BUF_ERR; + case WBC_ERR_NSS_ERROR: + return PAM_USER_UNKNOWN; + case WBC_ERR_AUTH_ERROR: + return PAM_AUTH_ERR; + case WBC_ERR_UNKNOWN_USER: + return PAM_USER_UNKNOWN; + case WBC_ERR_UNKNOWN_GROUP: + return PAM_USER_UNKNOWN; + case WBC_ERR_PWD_CHANGE_FAILED: + break; + } + + /* be paranoid */ + return PAM_AUTH_ERR; +} + static const char *_pam_error_code_str(int err) { switch (err) { @@ -401,6 +437,10 @@ static int _pam_parse(const pam_handle_t *pamh, ctrl |= WINBIND_WARN_PWD_EXPIRE; } + if (iniparser_getboolean(d, "global:mkhomedir", false)) { + ctrl |= WINBIND_MKHOMEDIR; + } + config_from_pam: /* step through arguments */ for (i=argc,v=argv; i-- > 0; ++v) { @@ -433,6 +473,8 @@ config_from_pam: ctrl |= WINBIND_KRB5_CCACHE_TYPE; else if (!strcasecmp(*v, "cached_login")) ctrl |= WINBIND_CACHED_LOGIN; + else if (!strcasecmp(*v, "mkhomedir")) + ctrl |= WINBIND_MKHOMEDIR; else { __pam_log(pamh, ctrl, LOG_ERR, "pam_parse: unknown option: %s", *v); @@ -452,13 +494,17 @@ config_from_pam: return ctrl; }; -static void _pam_winbind_free_context(struct pwb_context *ctx) +static int _pam_winbind_free_context(struct pwb_context *ctx) { + if (!ctx) { + return 0; + } + if (ctx->dict) { iniparser_freedict(ctx->dict); } - SAFE_FREE(ctx); + return 0; } static int _pam_winbind_init_context(pam_handle_t *pamh, @@ -469,12 +515,12 @@ static int _pam_winbind_init_context(pam_handle_t *pamh, { struct pwb_context *r = NULL; - r = (struct pwb_context *)malloc(sizeof(struct pwb_context)); + r = TALLOC_ZERO_P(NULL, struct pwb_context); if (!r) { return PAM_BUF_ERR; } - ZERO_STRUCTP(r); + talloc_set_destructor(r, _pam_winbind_free_context); r->pamh = pamh; r->flags = flags; @@ -482,7 +528,7 @@ static int _pam_winbind_init_context(pam_handle_t *pamh, r->argv = argv; r->ctrl = _pam_parse(pamh, flags, argc, argv, &r->dict); if (r->ctrl == -1) { - _pam_winbind_free_context(r); + TALLOC_FREE(r); return PAM_SYSTEM_ERR; } @@ -502,7 +548,7 @@ static void _pam_winbind_cleanup_func(pam_handle_t *pamh, "(error_status = %d)", pamh, data, error_status); } - SAFE_FREE(data); + TALLOC_FREE(data); } @@ -644,80 +690,11 @@ static int _make_remark_format(struct pwb_context *ctx, int type, const char *fo return ret; } -static int pam_winbind_request(struct pwb_context *ctx, - enum winbindd_cmd req_type, - struct winbindd_request *request, - struct winbindd_response *response) -{ - /* Fill in request and send down pipe */ - winbindd_init_request(request, req_type); - - if (winbind_write_sock(request, sizeof(*request), 0, 0) == -1) { - _pam_log(ctx, LOG_ERR, - "pam_winbind_request: write to socket failed!"); - winbind_close_sock(); - return PAM_SERVICE_ERR; - } - - /* Wait for reply */ - if (winbindd_read_reply(response) == -1) { - _pam_log(ctx, LOG_ERR, - "pam_winbind_request: read from socket failed!"); - winbind_close_sock(); - return PAM_SERVICE_ERR; - } - - /* We are done with the socket - close it and avoid mischeif */ - winbind_close_sock(); - - /* Copy reply data from socket */ - if (response->result == WINBINDD_OK) { - return PAM_SUCCESS; - } - - /* no need to check for pam_error codes for getpwnam() */ - switch (req_type) { - - case WINBINDD_GETPWNAM: - case WINBINDD_LOOKUPNAME: - if (strlen(response->data.auth.nt_status_string) > 0) { - _pam_log(ctx, LOG_ERR, - "request failed, NT error was %s", - response->data.auth.nt_status_string); - } else { - _pam_log(ctx, LOG_ERR, "request failed"); - } - return PAM_USER_UNKNOWN; - default: - break; - } - - if (response->data.auth.pam_error != PAM_SUCCESS) { - _pam_log(ctx, LOG_ERR, - "request failed: %s, " - "PAM error was %s (%d), NT error was %s", - response->data.auth.error_string, - pam_strerror(ctx->pamh, response->data.auth.pam_error), - response->data.auth.pam_error, - response->data.auth.nt_status_string); - return response->data.auth.pam_error; - } - - _pam_log(ctx, LOG_ERR, "request failed, but PAM error 0!"); - - return PAM_SERVICE_ERR; -} - static int pam_winbind_request_log(struct pwb_context *ctx, - enum winbindd_cmd req_type, - struct winbindd_request *request, - struct winbindd_response *response, - const char *user) + int retval, + const char *user, + const char *fn) { - int retval; - - retval = pam_winbind_request(ctx, req_type, request, response); - switch (retval) { case PAM_AUTH_ERR: /* incorrect password */ @@ -749,33 +726,65 @@ static int pam_winbind_request_log(struct pwb_context *ctx, return retval; case PAM_SUCCESS: /* Otherwise, the authentication looked good */ - switch (req_type) { - case WINBINDD_INFO: - break; - case WINBINDD_PAM_AUTH: - _pam_log(ctx, LOG_NOTICE, - "user '%s' granted access", user); - break; - case WINBINDD_PAM_CHAUTHTOK: - _pam_log(ctx, LOG_NOTICE, - "user '%s' password changed", user); - break; - default: - _pam_log(ctx, LOG_NOTICE, - "user '%s' OK", user); - break; + if (strcmp(fn, "wbcLogonUser") == 0) { + _pam_log(ctx, LOG_NOTICE, + "user '%s' granted access", user); + } else { + _pam_log(ctx, LOG_NOTICE, + "user '%s' OK", user); } - return retval; default: /* we don't know anything about this return value */ _pam_log(ctx, LOG_ERR, - "internal module error (retval = %d, user = '%s')", - retval, user); + "internal module error (retval = %s(%d), user = '%s')", + _pam_error_code_str(retval), retval, user); return retval; } } +static int wbc_auth_error_to_pam_error(struct pwb_context *ctx, + struct wbcAuthErrorInfo *e, + wbcErr status, + const char *username, + const char *fn) +{ + int ret = PAM_AUTH_ERR; + + if (WBC_ERROR_IS_OK(status)) { + _pam_log_debug(ctx, LOG_DEBUG, "request %s succeeded", + fn); + ret = PAM_SUCCESS; + return pam_winbind_request_log(ctx, ret, username, fn); + } + + if (e) { + if (e->pam_error != PAM_SUCCESS) { + _pam_log(ctx, LOG_ERR, + "request %s failed: %s, " + "PAM error: %s (%d), NTSTATUS: %s, " + "Error message was: %s", + fn, + wbcErrorString(status), + _pam_error_code_str(e->pam_error), + e->pam_error, + e->nt_string, + e->display_string); + ret = e->pam_error; + return pam_winbind_request_log(ctx, ret, username, fn); + } + + _pam_log(ctx, LOG_ERR, "request %s failed, but PAM error 0!", fn); + + ret = PAM_SERVICE_ERR; + return pam_winbind_request_log(ctx, ret, username, fn); + } + + ret = wbc_error_to_pam_error(status); + return pam_winbind_request_log(ctx, ret, username, fn); +} + + /** * send a password expiry message if required * @@ -848,29 +857,34 @@ static bool _pam_send_password_expiry_message(struct pwb_context *ctx, */ static void _pam_warn_password_expiry(struct pwb_context *ctx, - const struct winbindd_response *response, + const struct wbcAuthUserInfo *info, + const struct wbcUserPasswordPolicyInfo *policy, int warn_pwd_expire, bool *already_expired) { time_t now = time(NULL); time_t next_change = 0; + if (!info || !policy) { + return; + } + if (already_expired) { *already_expired = false; } - /* accounts with ACB_PWNOEXP set never receive a warning */ - if (response->data.auth.info3.acct_flags & ACB_PWNOEXP) { + /* accounts with WBC_ACB_PWNOEXP set never receive a warning */ + if (info->acct_flags & WBC_ACB_PWNOEXP) { return; } /* no point in sending a warning if this is a grace logon */ - if (PAM_WB_GRACE_LOGON(response->data.auth.info3.user_flgs)) { + if (PAM_WB_GRACE_LOGON(info->user_flags)) { return; } /* check if the info3 must change timestamp has been set */ - next_change = response->data.auth.info3.pass_must_change_time; + next_change = info->pass_must_change_time; if (_pam_send_password_expiry_message(ctx, next_change, now, warn_pwd_expire, @@ -881,12 +895,11 @@ static void _pam_warn_password_expiry(struct pwb_context *ctx, /* now check for the global password policy */ /* good catch from Ralf Haferkamp: an expiry of "never" is translated * to -1 */ - if (response->data.auth.policy.expire <= 0) { + if (policy->expire <= 0) { return; } - next_change = response->data.auth.info3.pass_last_set_time + - response->data.auth.policy.expire; + next_change = info->pass_last_set_time + policy->expire; if (_pam_send_password_expiry_message(ctx, next_change, now, warn_pwd_expire, @@ -944,33 +957,33 @@ static bool winbind_name_to_sid_string(struct pwb_context *ctx, int sid_list_buffer_size) { const char* sid_string; - struct winbindd_response sid_response; /* lookup name? */ if (IS_SID_STRING(name)) { sid_string = name; } else { - struct winbindd_request sid_request; - - ZERO_STRUCT(sid_request); - ZERO_STRUCT(sid_response); + 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); - /* fortunatly winbindd can handle non-separated names */ - strncpy(sid_request.data.name.name, name, - sizeof(sid_request.data.name.name) - 1); - - if (pam_winbind_request_log(ctx, WINBINDD_LOOKUPNAME, - &sid_request, &sid_response, - user)) { + wbc_status = wbcLookupName("", name, &sid, &type); + if (!WBC_ERROR_IS_OK(wbc_status)) { _pam_log(ctx, LOG_INFO, "could not lookup name: %s\n", name); return false; } - sid_string = sid_response.data.sid.sid; + wbc_status = wbcSidToString(&sid, &sid_str); + if (!WBC_ERROR_IS_OK(wbc_status)) { + return false; + } + + wbcFreeMemory(sid_str); + sid_string = sid_str; } if (!safe_append_string(sid_list_buffer, sid_string, @@ -1055,15 +1068,28 @@ out: */ static void _pam_setup_krb5_env(struct pwb_context *ctx, - const char *krb5ccname) + struct wbcLogonUserInfo *info) { char var[PATH_MAX]; int ret; + uint32_t i; + const char *krb5ccname = NULL; if (off(ctx->ctrl, WINBIND_KRB5_AUTH)) { return; } + if (!info) { + return; + } + + for (i=0; i < info->num_blobs; i++) { + if (strcasecmp(info->blobs[i].name, "krb5ccname") == 0) { + krb5ccname = (const char *)info->blobs[i].blob.data; + break; + } + } + if (!krb5ccname || (strlen(krb5ccname) == 0)) { return; } @@ -1084,6 +1110,41 @@ static void _pam_setup_krb5_env(struct pwb_context *ctx, } /** + * Copy unix username if available (further processed in PAM). + * + * @param ctx PAM winbind context + * @param user_ret A pointer that holds a pointer to a string + * @param unix_username A username + * + * @return void. + */ + +static void _pam_setup_unix_username(struct pwb_context *ctx, + char **user_ret, + struct wbcLogonUserInfo *info) +{ + const char *unix_username = NULL; + uint32_t i; + + if (!user_ret || !info) { + return; + } + + for (i=0; i < info->num_blobs; i++) { + if (strcasecmp(info->blobs[i].name, "unix_username") == 0) { + unix_username = (const char *)info->blobs[i].blob.data; + break; + } + } + + if (!unix_username || !unix_username[0]) { + return; + } + + *user_ret = strdup(unix_username); +} + +/** * Set string into the PAM stack. * * @param ctx PAM winbind context. @@ -1104,14 +1165,13 @@ static void _pam_set_data_string(struct pwb_context *ctx, return; } - ret = pam_set_data(ctx->pamh, data_name, (void *)strdup(value), + ret = pam_set_data(ctx->pamh, data_name, talloc_strdup(NULL, value), _pam_winbind_cleanup_func); if (ret) { _pam_log_debug(ctx, LOG_DEBUG, "Could not set data %s: %s\n", data_name, pam_strerror(ctx->pamh, ret)); } - } /** @@ -1125,16 +1185,16 @@ static void _pam_set_data_string(struct pwb_context *ctx, */ static void _pam_set_data_info3(struct pwb_context *ctx, - struct winbindd_response *response) + const struct wbcAuthUserInfo *info) { _pam_set_data_string(ctx, PAM_WINBIND_HOMEDIR, - response->data.auth.info3.home_dir); + info->home_directory); _pam_set_data_string(ctx, PAM_WINBIND_LOGONSCRIPT, - response->data.auth.info3.logon_script); + info->logon_script); _pam_set_data_string(ctx, PAM_WINBIND_LOGONSERVER, - response->data.auth.info3.logon_srv); + info->logon_server); _pam_set_data_string(ctx, PAM_WINBIND_PROFILEPATH, - response->data.auth.info3.profile_path); + info->profile_path); } /** @@ -1216,80 +1276,235 @@ 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) +{ + const char *ntstatus = NULL; + const char *error_string = NULL; + + if (!e || !pam_error) { + return false; + } + + ntstatus = e->nt_string; + if (!ntstatus) { + return false; + } + + if (strcasecmp(ntstatus, nt_status_string) == 0) { + + 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; + return true; + } + + if (e->display_string) { + _make_remark(ctx, PAM_ERROR_MSG, e->display_string); + *pam_error = e->pam_error; + return true; + } + + _make_remark(ctx, PAM_ERROR_MSG, nt_status_string); + *pam_error = e->pam_error; + + return true; + } + + return false; +}; + /** * Compose Password Restriction String for a PAM_ERROR_MSG conversation. * - * @param response The struct winbindd_response. + * @param i The wbcUserPasswordPolicyInfo struct. * - * @return string (caller needs to free). + * @return string (caller needs to talloc_free). */ -static char *_pam_compose_pwd_restriction_string(struct winbindd_response *response) +static char *_pam_compose_pwd_restriction_string(struct pwb_context *ctx, + struct wbcUserPasswordPolicyInfo *i) { char *str = NULL; - size_t offset = 0, ret = 0, str_size = 1024; - str = (char *)malloc(str_size); - if (!str) { - return NULL; + if (!i) { + goto failed; } - memset(str, '\0', str_size); - - offset = snprintf(str, str_size, "Your password "); - if (offset == -1) { + str = talloc_asprintf(ctx, "Your password "); + if (!str) { goto failed; } - if (response->data.auth.policy.min_length_password > 0) { - ret = snprintf(str+offset, str_size-offset, + if (i->min_length_password > 0) { + str = talloc_asprintf_append(str, "must be at least %d characters; ", - response->data.auth.policy.min_length_password); - if (ret == -1) { + i->min_length_password); + if (!str) { goto failed; } - offset += ret; } - if (response->data.auth.policy.password_history > 0) { - ret = snprintf(str+offset, str_size-offset, + if (i->password_history > 0) { + str = talloc_asprintf_append(str, "cannot repeat any of your previous %d " "passwords; ", - response->data.auth.policy.password_history); - if (ret == -1) { + i->password_history); + if (!str) { goto failed; } - offset += ret; } - if (response->data.auth.policy.password_properties & - DOMAIN_PASSWORD_COMPLEX) { - ret = snprintf(str+offset, str_size-offset, + if (i->password_properties & WBC_DOMAIN_PASSWORD_COMPLEX) { + str = talloc_asprintf_append(str, "must contain capitals, numerals " "or punctuation; " "and cannot contain your account " "or full name; "); - if (ret == -1) { + if (!str) { goto failed; } - offset += ret; } - ret = snprintf(str+offset, str_size-offset, + str = talloc_asprintf_append(str, "Please type a different password. " "Type a password which meets these requirements in " "both text boxes."); - if (ret == -1) { + if (!str) { goto failed; } return str; failed: - SAFE_FREE(str); + TALLOC_FREE(str); return NULL; } +static int _pam_create_homedir(struct pwb_context *ctx, + const char *dirname, + mode_t mode) +{ + struct stat sbuf; + + if (stat(dirname, &sbuf) == 0) { + return PAM_SUCCESS; + } + + if (mkdir(dirname, mode) != 0) { + + _make_remark_format(ctx, PAM_TEXT_INFO, + "Creating directory: %s failed: %s", + dirname, strerror(errno)); + _pam_log(ctx, LOG_ERR, "could not create dir: %s (%s)", + dirname, strerror(errno)); + return PAM_PERM_DENIED; + } + + return PAM_SUCCESS; +} + +static int _pam_chown_homedir(struct pwb_context *ctx, + const char *dirname, + uid_t uid, + gid_t gid) +{ + if (chown(dirname, uid, gid) != 0) { + _pam_log(ctx, LOG_ERR, "failed to chown user homedir: %s (%s)", + dirname, strerror(errno)); + return PAM_PERM_DENIED; + } + + return PAM_SUCCESS; +} + +static int _pam_mkhomedir(struct pwb_context *ctx) +{ + struct passwd *pwd = NULL; + char *token = NULL; + char *create_dir = NULL; + char *user_dir = NULL; + int ret; + const char *username; + mode_t mode = 0700; + char *safe_ptr = NULL; + char *p = NULL; + + /* Get the username */ + ret = pam_get_user(ctx->pamh, &username, NULL); + if ((ret != PAM_SUCCESS) || (!username)) { + _pam_log_debug(ctx, LOG_DEBUG, "can not get the username"); + return PAM_SERVICE_ERR; + } + + pwd = getpwnam(username); + if (pwd == NULL) { + _pam_log_debug(ctx, LOG_DEBUG, "can not get the username"); + return PAM_USER_UNKNOWN; + } + _pam_log_debug(ctx, LOG_DEBUG, "homedir is: %s", pwd->pw_dir); + + ret = _pam_create_homedir(ctx, pwd->pw_dir, 0700); + if (ret == PAM_SUCCESS) { + ret = _pam_chown_homedir(ctx, pwd->pw_dir, + pwd->pw_uid, + pwd->pw_gid); + } + + if (ret == PAM_SUCCESS) { + return ret; + } + + /* maybe we need to create parent dirs */ + create_dir = talloc_strdup(ctx, "/"); + if (!create_dir) { + return PAM_BUF_ERR; + } + + /* find final directory */ + user_dir = strrchr(pwd->pw_dir, '/'); + if (!user_dir) { + return PAM_BUF_ERR; + } + user_dir++; + + _pam_log(ctx, LOG_DEBUG, "final directory: %s", user_dir); + + p = pwd->pw_dir; + + while ((token = strtok_r(p, "/", &safe_ptr)) != NULL) { + + mode = 0755; + + p = NULL; + + _pam_log_debug(ctx, LOG_DEBUG, "token is %s", token); + + create_dir = talloc_asprintf_append(create_dir, "%s/", token); + if (!create_dir) { + return PAM_BUF_ERR; + } + _pam_log_debug(ctx, LOG_DEBUG, "current_dir is %s", create_dir); + + if (strcmp(token, user_dir) == 0) { + _pam_log_debug(ctx, LOG_DEBUG, "assuming last directory: %s", token); + mode = 0700; + } + + ret = _pam_create_homedir(ctx, create_dir, mode); + if (ret) { + return ret; + } + } + + return _pam_chown_homedir(ctx, create_dir, + pwd->pw_uid, + pwd->pw_gid); +} + /* talk to winbindd */ static int winbind_auth_request(struct pwb_context *ctx, const char *user, @@ -1297,37 +1512,52 @@ static int winbind_auth_request(struct pwb_context *ctx, const char *member, const char *cctype, const int warn_pwd_expire, - struct winbindd_response *p_response, + struct wbcAuthErrorInfo **p_error, + struct wbcLogonUserInfo **p_info, + struct wbcUserPasswordPolicyInfo **p_policy, time_t *pwd_last_set, char **user_ret) { - struct winbindd_request request; - struct winbindd_response response; - int ret; - bool already_expired = false; + 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; - ZERO_STRUCT(request); - ZERO_STRUCT(response); + 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[] = { + "NT_STATUS_PASSWORD_EXPIRED", + "NT_STATUS_PASSWORD_MUST_CHANGE", + "NT_STATUS_INVALID_WORKSTATION", + "NT_STATUS_INVALID_LOGON_HOURS", + "NT_STATUS_ACCOUNT_EXPIRED", + "NT_STATUS_ACCOUNT_DISABLED", + "NT_STATUS_ACCOUNT_LOCKED_OUT", + "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", + "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", + "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", + "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", + "NT_STATUS_NO_LOGON_SERVERS", + "NT_STATUS_WRONG_PASSWORD", + "NT_STATUS_ACCESS_DENIED" + }; if (pwd_last_set) { *pwd_last_set = 0; } - strncpy(request.data.auth.user, user, - sizeof(request.data.auth.user)-1); - - strncpy(request.data.auth.pass, pass, - sizeof(request.data.auth.pass)-1); - - request.data.auth.krb5_cc_type[0] = '\0'; - request.data.auth.uid = -1; - - request.flags = WBFLAG_PAM_INFO3_TEXT | WBFLAG_PAM_GET_PWD_POLICY; - /* Krb5 auth always has to go against the KDC of the user's realm */ if (ctx->ctrl & WINBIND_KRB5_AUTH) { - request.flags |= WBFLAG_PAM_CONTACT_TRUSTDOM; + flags |= WBFLAG_PAM_CONTACT_TRUSTDOM; } if (ctx->ctrl & (WINBIND_KRB5_AUTH|WINBIND_CACHED_LOGIN)) { @@ -1337,7 +1567,7 @@ static int winbind_auth_request(struct pwb_context *ctx, if (pwd == NULL) { return PAM_USER_UNKNOWN; } - request.data.auth.uid = pwd->pw_uid; + user_uid = pwd->pw_uid; } if (ctx->ctrl & WINBIND_KRB5_AUTH) { @@ -1345,38 +1575,34 @@ static int winbind_auth_request(struct pwb_context *ctx, _pam_log_debug(ctx, LOG_DEBUG, "enabling krb5 login flag\n"); - request.flags |= WBFLAG_PAM_KRB5 | - WBFLAG_PAM_FALLBACK_AFTER_KRB5; + flags |= WBFLAG_PAM_KRB5 | + WBFLAG_PAM_FALLBACK_AFTER_KRB5; } if (ctx->ctrl & WINBIND_CACHED_LOGIN) { _pam_log_debug(ctx, LOG_DEBUG, "enabling cached login flag\n"); - request.flags |= WBFLAG_PAM_CACHED_LOGIN; + flags |= WBFLAG_PAM_CACHED_LOGIN; } if (user_ret) { *user_ret = NULL; - request.flags |= WBFLAG_PAM_UNIX_NAME; + flags |= WBFLAG_PAM_UNIX_NAME; } if (cctype != NULL) { - strncpy(request.data.auth.krb5_cc_type, cctype, - sizeof(request.data.auth.krb5_cc_type) - 1); _pam_log_debug(ctx, LOG_DEBUG, "enabling request for a %s krb5 ccache\n", cctype); } - request.data.auth.require_membership_of_sid[0] = '\0'; - if (member != NULL) { - if (!winbind_name_list_to_sid_string_list(ctx, user, - member, - request.data.auth.require_membership_of_sid, - sizeof(request.data.auth.require_membership_of_sid))) { + ZERO_STRUCT(membership_of); + if (!winbind_name_list_to_sid_string_list(ctx, user, member, + membership_of, + sizeof(membership_of))) { _pam_log_debug(ctx, LOG_ERR, "failed to serialize membership of sid " "\"%s\"\n", member); @@ -1384,60 +1610,100 @@ static int winbind_auth_request(struct pwb_context *ctx, } } - ret = pam_winbind_request_log(ctx, WINBINDD_PAM_AUTH, - &request, &response, user); + ZERO_STRUCT(logon); - if (pwd_last_set) { - *pwd_last_set = response.data.auth.info3.pass_last_set_time; + logon.username = user; + logon.password = pass; + + wbc_status = wbcAddNamedBlob(&logon.num_blobs, + &logon.blobs, + "krb5_cc_type", + 0, + (uint8_t *)cctype, + strlen(cctype)+1); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto done; + } + + wbc_status = wbcAddNamedBlob(&logon.num_blobs, + &logon.blobs, + "flags", + 0, + (uint8_t *)&flags, + sizeof(flags)); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto done; + } + + wbc_status = wbcAddNamedBlob(&logon.num_blobs, + &logon.blobs, + "user_uid", + 0, + (uint8_t *)&user_uid, + sizeof(user_uid)); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto done; + } + + wbc_status = wbcAddNamedBlob(&logon.num_blobs, + &logon.blobs, + "membership_of", + 0, + (uint8_t *)membership_of, + sizeof(membership_of)); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto done; } - if (p_response) { - /* We want to process the response in the caller. */ - *p_response = response; + wbc_status = wbcLogonUser(&logon, &info, &error, &policy); + ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status, + user, "wbcLogonUser"); + wbcFreeMemory(logon.blobs); + logon.blobs = NULL; + + if (info && info->info) { + user_info = info->info; + } + + if (pwd_last_set && user_info) { + *pwd_last_set = user_info->pass_last_set_time; + } + + if (p_info && info) { + *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; return ret; } - if (ret) { - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_PASSWORD_EXPIRED"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_PASSWORD_MUST_CHANGE"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_INVALID_WORKSTATION"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_INVALID_LOGON_HOURS"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_ACCOUNT_EXPIRED"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_ACCOUNT_DISABLED"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_ACCOUNT_LOCKED_OUT"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_NO_LOGON_SERVERS"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_WRONG_PASSWORD"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_ACCESS_DENIED"); + for (i=0; i<ARRAY_SIZE(codes); i++) { + int _ret = ret; + if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) { + ret = _ret; + goto done; + } } - if (ret == PAM_SUCCESS) { + if ((ret == PAM_SUCCESS) && user_info && policy && info) { + + bool already_expired = false; /* warn a user if the password is about to expire soon */ - _pam_warn_password_expiry(ctx, &response, + _pam_warn_password_expiry(ctx, user_info, policy, warn_pwd_expire, &already_expired); if (already_expired == true) { - SMB_TIME_T last_set; - last_set = response.data.auth.info3.pass_last_set_time; + + SMB_TIME_T last_set = user_info->pass_last_set_time; + _pam_log_debug(ctx, LOG_DEBUG, "Password has expired " "(Password was last set: %lld, " @@ -1445,33 +1711,44 @@ static int winbind_auth_request(struct pwb_context *ctx, "%lld (now it's: %lu))\n", (long long int)last_set, (long long int)last_set + - response.data.auth.policy.expire, + policy->expire, time(NULL)); return PAM_AUTHTOK_EXPIRED; } /* inform about logon type */ - _pam_warn_logon_type(ctx, user, - response.data.auth.info3.user_flgs); + _pam_warn_logon_type(ctx, user, user_info->user_flags); /* inform about krb5 failures */ - _pam_warn_krb5_failure(ctx, user, - response.data.auth.info3.user_flgs); + _pam_warn_krb5_failure(ctx, user, user_info->user_flags); /* set some info3 info for other modules in the stack */ - _pam_set_data_info3(ctx, &response); + _pam_set_data_info3(ctx, user_info); /* put krb5ccname into env */ - _pam_setup_krb5_env(ctx, response.data.auth.krb5ccname); + _pam_setup_krb5_env(ctx, info); /* If winbindd returned a username, return the pointer to it * here. */ - if (user_ret && response.data.auth.unix_username[0]) { - /* We have to trust it's a null terminated string. */ - *user_ret = strndup(response.data.auth.unix_username, - sizeof(response.data.auth.unix_username) - 1); - } + _pam_setup_unix_username(ctx, user_ret, info); + } + + done: + if (logon.blobs) { + wbcFreeMemory(logon.blobs); + } + if (info && info->blobs) { + wbcFreeMemory(info->blobs); + } + if (error && !p_error) { + wbcFreeMemory(error); + } + if (info && !p_info) { + wbcFreeMemory(info); + } + if (policy && !p_policy) { + wbcFreeMemory(policy); } return ret; @@ -1484,99 +1761,95 @@ static int winbind_chauthtok_request(struct pwb_context *ctx, const char *newpass, time_t pwd_last_set) { - struct winbindd_request request; - struct winbindd_response response; - int ret; + wbcErr wbc_status; + struct wbcChangePasswordParams params; + struct wbcAuthErrorInfo *error = NULL; + struct wbcUserPasswordPolicyInfo *policy = NULL; + enum wbcPasswordChangeRejectReason reject_reason = -1; + uint32_t flags = 0; - ZERO_STRUCT(request); - ZERO_STRUCT(response); + int i; + const char *codes[] = { + "NT_STATUS_BACKUP_CONTROLLER", + "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", + "NT_STATUS_NO_LOGON_SERVERS", + "NT_STATUS_ACCESS_DENIED", + "NT_STATUS_PWD_TOO_SHORT", /* TODO: tell the min pwd length ? */ + "NT_STATUS_PWD_TOO_RECENT", /* TODO: tell the minage ? */ + "NT_STATUS_PWD_HISTORY_CONFLICT" /* TODO: tell the history length ? */ + }; + int ret = PAM_AUTH_ERR; + + ZERO_STRUCT(params); - if (request.data.chauthtok.user == NULL) { - return -2; + if (ctx->ctrl & WINBIND_KRB5_AUTH) { + flags |= WBFLAG_PAM_KRB5 | + WBFLAG_PAM_CONTACT_TRUSTDOM; } - strncpy(request.data.chauthtok.user, user, - sizeof(request.data.chauthtok.user) - 1); - - if (oldpass != NULL) { - strncpy(request.data.chauthtok.oldpass, oldpass, - sizeof(request.data.chauthtok.oldpass) - 1); - } else { - request.data.chauthtok.oldpass[0] = '\0'; + if (ctx->ctrl & WINBIND_CACHED_LOGIN) { + flags |= WBFLAG_PAM_CACHED_LOGIN; } - if (newpass != NULL) { - strncpy(request.data.chauthtok.newpass, newpass, - sizeof(request.data.chauthtok.newpass) - 1); - } else { - request.data.chauthtok.newpass[0] = '\0'; - } + params.account_name = user; + params.level = WBC_AUTH_USER_LEVEL_PLAIN; + params.old_password.plaintext = oldpass; + params.new_password.plaintext = newpass; + params.flags = flags; - if (ctx->ctrl & WINBIND_KRB5_AUTH) { - request.flags = WBFLAG_PAM_KRB5 | - WBFLAG_PAM_CONTACT_TRUSTDOM; - } + wbc_status = wbcChangeUserPasswordEx(¶ms, &error, &reject_reason, &policy); + ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status, + user, "wbcChangeUserPasswordEx"); - if (ctx->ctrl & WINBIND_CACHED_LOGIN) { - request.flags |= WBFLAG_PAM_CACHED_LOGIN; + if (WBC_ERROR_IS_OK(wbc_status)) { + _pam_log(ctx, LOG_NOTICE, + "user '%s' password changed", user); + return PAM_SUCCESS; } - ret = pam_winbind_request_log(ctx, WINBINDD_PAM_CHAUTHTOK, - &request, &response, user); - - if (ret == PAM_SUCCESS) { + if (!error) { + wbcFreeMemory(policy); return ret; } - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_BACKUP_CONTROLLER"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_NO_LOGON_SERVERS"); - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_ACCESS_DENIED"); - - /* TODO: tell the min pwd length ? */ - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_PWD_TOO_SHORT"); - - /* TODO: tell the minage ? */ - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_PWD_TOO_RECENT"); - - /* TODO: tell the history length ? */ - PAM_WB_REMARK_CHECK_RESPONSE_RET(ctx, response, - "NT_STATUS_PWD_HISTORY_CONFLICT"); + for (i=0; i<ARRAY_SIZE(codes); i++) { + int _ret = ret; + if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) { + ret = _ret; + goto done; + } + } - if (!strcasecmp(response.data.auth.nt_status_string, + if (!strcasecmp(error->nt_string, "NT_STATUS_PASSWORD_RESTRICTION")) { char *pwd_restriction_string = NULL; - SMB_TIME_T min_pwd_age; - uint32_t reject_reason = response.data.auth.reject_reason; - min_pwd_age = response.data.auth.policy.min_passwordage; + SMB_TIME_T min_pwd_age = 0; + + if (policy) { + min_pwd_age = policy->min_passwordage; + } /* FIXME: avoid to send multiple PAM messages after another */ switch (reject_reason) { case -1: break; - case SAMR_REJECT_OTHER: + case WBC_PWD_CHANGE_REJECT_OTHER: 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 SAMR_REJECT_TOO_SHORT: + case WBC_PWD_CHANGE_REJECT_TOO_SHORT: PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PWD_TOO_SHORT"); break; - case SAMR_REJECT_IN_HISTORY: + case WBC_PWD_CHANGE_REJECT_IN_HISTORY: PAM_WB_REMARK_DIRECT(ctx, "NT_STATUS_PWD_HISTORY_CONFLICT"); break; - case SAMR_REJECT_COMPLEXITY: + case WBC_PWD_CHANGE_REJECT_COMPLEXITY: _make_remark(ctx, PAM_ERROR_MSG, "Password does not meet " "complexity requirements"); @@ -1590,13 +1863,16 @@ static int winbind_chauthtok_request(struct pwb_context *ctx, } pwd_restriction_string = - _pam_compose_pwd_restriction_string(&response); + _pam_compose_pwd_restriction_string(ctx, policy); if (pwd_restriction_string) { _make_remark(ctx, PAM_ERROR_MSG, pwd_restriction_string); - SAFE_FREE(pwd_restriction_string); + TALLOC_FREE(pwd_restriction_string); } } + done: + wbcFreeMemory(error); + wbcFreeMemory(policy); return ret; } @@ -1616,29 +1892,26 @@ static int valid_user(struct pwb_context *ctx, * sure it's really a winbind user, this is important when stacking PAM * modules in the 'account' or 'password' facility. */ + wbcErr wbc_status; struct passwd *pwd = NULL; - struct winbindd_request request; - struct winbindd_response response; - int ret; - - ZERO_STRUCT(request); - ZERO_STRUCT(response); + struct passwd *wb_pwd = NULL; pwd = getpwnam(user); if (pwd == NULL) { return 1; } - strncpy(request.data.username, user, - sizeof(request.data.username) - 1); - - ret = pam_winbind_request_log(ctx, WINBINDD_GETPWNAM, - &request, &response, user); + wbc_status = wbcGetpwnam(user, &wb_pwd); + wbcFreeMemory(wb_pwd); + if (!WBC_ERROR_IS_OK(wbc_status)) { + _pam_log(ctx, LOG_DEBUG, "valid_user: wbcGetpwnam gave %s\n", + wbcErrorString(wbc_status)); + } - switch (ret) { - case PAM_USER_UNKNOWN: + switch (wbc_status) { + case WBC_ERR_UNKNOWN_USER: return 1; - case PAM_SUCCESS: + case WBC_ERR_SUCCESS: return 0; default: break; @@ -1846,12 +2119,13 @@ static const char *get_conf_item_string(struct pwb_context *ctx, if (ctx->dict) { char *key = NULL; - if (!asprintf(&key, "global:%s", item)) { + key = talloc_asprintf(ctx, "global:%s", item); + if (!key) { goto out; } parm_opt = iniparser_getstr(ctx->dict, key); - SAFE_FREE(key); + TALLOC_FREE(key); _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%s'\n", item, parm_opt); @@ -1893,12 +2167,13 @@ static int get_config_item_int(struct pwb_context *ctx, if (ctx->dict) { char *key = NULL; - if (!asprintf(&key, "global:%s", item)) { + key = talloc_asprintf(ctx, "global:%s", item); + if (!key) { goto out; } parm_opt = iniparser_getint(ctx->dict, key, -1); - SAFE_FREE(key); + TALLOC_FREE(key); _pam_log_debug(ctx, LOG_INFO, "CONFIG file: %s '%d'\n", @@ -1948,20 +2223,25 @@ static int get_warn_pwd_expire_from_config(struct pwb_context *ctx) static char winbind_get_separator(struct pwb_context *ctx) { - struct winbindd_request request; - struct winbindd_response response; + wbcErr wbc_status; + static struct wbcInterfaceDetails *details = NULL; - ZERO_STRUCT(request); - ZERO_STRUCT(response); + wbc_status = wbcInterfaceDetails(&details); + if (!WBC_ERROR_IS_OK(wbc_status)) { + _pam_log(ctx, LOG_ERR, + "Could not retrieve winbind interface details: %s", + wbcErrorString(wbc_status)); + return '\0'; + } - if (pam_winbind_request_log(ctx, WINBINDD_INFO, - &request, &response, NULL)) { + if (!details) { return '\0'; } - return response.data.info.winbind_separator; + return details->winbind_separator; } + /** * Convert a upn to a name. * @@ -1974,12 +2254,12 @@ static char winbind_get_separator(struct pwb_context *ctx) static char* winbind_upn_to_username(struct pwb_context *ctx, const char *upn) { - struct winbindd_request req; - struct winbindd_response resp; - int retval; - char *account_name; - int account_name_len; char sep; + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + struct wbcDomainSid sid; + enum wbcSidType type; + char *domain; + char *name; /* This cannot work when the winbind separator = @ */ @@ -1990,35 +2270,19 @@ static char* winbind_upn_to_username(struct pwb_context *ctx, /* Convert the UPN to a SID */ - ZERO_STRUCT(req); - ZERO_STRUCT(resp); - - strncpy(req.data.name.dom_name, "", - sizeof(req.data.name.dom_name) - 1); - strncpy(req.data.name.name, upn, - sizeof(req.data.name.name) - 1); - retval = pam_winbind_request_log(ctx, WINBINDD_LOOKUPNAME, - &req, &resp, upn); - if (retval != PAM_SUCCESS) { + wbc_status = wbcLookupName("", upn, &sid, &type); + if (!WBC_ERROR_IS_OK(wbc_status)) { return NULL; } /* Convert the the SID back to the sAMAccountName */ - ZERO_STRUCT(req); - strncpy(req.data.sid, resp.data.sid.sid, sizeof(req.data.sid)-1); - ZERO_STRUCT(resp); - retval = pam_winbind_request_log(ctx, WINBINDD_LOOKUPSID, - &req, &resp, upn); - if (retval != PAM_SUCCESS) { + wbc_status = wbcLookupSid(&sid, &domain, &name, &type); + if (!WBC_ERROR_IS_OK(wbc_status)) { return NULL; } - account_name_len = asprintf(&account_name, "%s\\%s", - resp.data.name.dom_name, - resp.data.name.name); - - return account_name; + return talloc_asprintf(ctx, "%s\\%s", domain, name); } PAM_EXTERN @@ -2087,7 +2351,7 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags, real_username); if (samaccountname) { free(real_username); - real_username = samaccountname; + real_username = strdup(samaccountname); } } @@ -2119,7 +2383,8 @@ 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, + member, cctype, warn_pwd_expire, + NULL, NULL, NULL, NULL, &username_ret); if (retval == PAM_NEW_AUTHTOK_REQD || @@ -2127,7 +2392,8 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags, char *new_authtok_required_during_auth = NULL; - if (!asprintf(&new_authtok_required, "%d", retval)) { + new_authtok_required = talloc_asprintf(NULL, "%d", retval); + if (!new_authtok_required) { retval = PAM_BUF_ERR; goto out; } @@ -2138,7 +2404,8 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags, retval = PAM_SUCCESS; - if (!asprintf(&new_authtok_required_during_auth, "%d", true)) { + new_authtok_required_during_auth = talloc_asprintf(NULL, "%d", true); + if (!new_authtok_required_during_auth) { retval = PAM_BUF_ERR; goto out; } @@ -2172,7 +2439,7 @@ out: _PAM_LOG_FUNCTION_LEAVE("pam_sm_authenticate", ctx, retval); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return retval; } @@ -2220,7 +2487,7 @@ int pam_sm_setcred(pam_handle_t *pamh, int flags, _PAM_LOG_FUNCTION_LEAVE("pam_sm_setcred", ctx, ret); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return ret; } @@ -2321,7 +2588,7 @@ int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, _PAM_LOG_FUNCTION_LEAVE("pam_sm_acct_mgmt", ctx, ret); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return ret; } @@ -2330,7 +2597,7 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { - int ret = PAM_SYSTEM_ERR; + int ret = PAM_SUCCESS; struct pwb_context *ctx = NULL; ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); @@ -2340,12 +2607,14 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags, _PAM_LOG_FUNCTION_ENTER("pam_sm_open_session", ctx); - ret = PAM_SUCCESS; - + if (ctx->ctrl & WINBIND_MKHOMEDIR) { + /* check and create homedir */ + ret = _pam_mkhomedir(ctx); + } out: _PAM_LOG_FUNCTION_LEAVE("pam_sm_open_session", ctx, ret); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return ret; } @@ -2356,6 +2625,7 @@ int pam_sm_close_session(pam_handle_t *pamh, int flags, { int retval = PAM_SUCCESS; struct pwb_context *ctx = NULL; + struct wbcLogoffUserParams logoff; retval = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); if (retval) { @@ -2372,15 +2642,15 @@ int pam_sm_close_session(pam_handle_t *pamh, int flags, if (ctx->ctrl & WINBIND_KRB5_AUTH) { /* destroy the ccache here */ - struct winbindd_request request; - struct winbindd_response response; + + wbcErr wbc_status; + struct wbcAuthErrorInfo *error = NULL; + + uint32_t flags = 0; const char *user; const char *ccname = NULL; struct passwd *pwd = NULL; - ZERO_STRUCT(request); - ZERO_STRUCT(response); - retval = pam_get_user(pamh, &user, "Username: "); if (retval) { _pam_log(ctx, LOG_ERR, @@ -2404,34 +2674,70 @@ int pam_sm_close_session(pam_handle_t *pamh, int flags, "user has no KRB5CCNAME environment"); } - strncpy(request.data.logoff.user, user, - sizeof(request.data.logoff.user) - 1); - - if (ccname) { - strncpy(request.data.logoff.krb5ccname, ccname, - sizeof(request.data.logoff.krb5ccname) - 1); - } - pwd = getpwnam(user); if (pwd == NULL) { retval = PAM_USER_UNKNOWN; goto out; } - request.data.logoff.uid = pwd->pw_uid; - request.flags = WBFLAG_PAM_KRB5 | - WBFLAG_PAM_CONTACT_TRUSTDOM; + flags = WBFLAG_PAM_KRB5 | + WBFLAG_PAM_CONTACT_TRUSTDOM; + + ZERO_STRUCT(logoff); + + logoff.username = user; - retval = pam_winbind_request_log(ctx, - WINBINDD_PAM_LOGOFF, - &request, &response, user); + wbc_status = wbcAddNamedBlob(&logoff.num_blobs, + &logoff.blobs, + "ccfilename", + 0, + (uint8_t *)ccname, + strlen(ccname)+1); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto out; + } + + wbc_status = wbcAddNamedBlob(&logoff.num_blobs, + &logoff.blobs, + "flags", + 0, + (uint8_t *)&flags, + sizeof(flags)); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto out; + } + + wbc_status = wbcAddNamedBlob(&logoff.num_blobs, + &logoff.blobs, + "user_uid", + 0, + (uint8_t *)&pwd->pw_uid, + sizeof(pwd->pw_uid)); + if (!WBC_ERROR_IS_OK(wbc_status)) { + goto out; + } + + wbc_status = wbcLogoffUserEx(&logoff, &error); + retval = wbc_auth_error_to_pam_error(ctx, error, wbc_status, + user, "wbcLogoffUser"); + wbcFreeMemory(error); + wbcFreeMemory(logoff.blobs); + + if (!WBC_ERROR_IS_OK(wbc_status)) { + _pam_log(ctx, LOG_INFO, + "failed to logoff user %s: %s\n", + user, wbcErrorString(wbc_status)); + } } out: + if (logoff.blobs) { + wbcFreeMemory(logoff.blobs); + } _PAM_LOG_FUNCTION_LEAVE("pam_sm_close_session", ctx, retval); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return retval; } @@ -2503,11 +2809,9 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int retry = 0; char *username_ret = NULL; - struct winbindd_response response; + struct wbcAuthErrorInfo *error = NULL; struct pwb_context *ctx = NULL; - ZERO_STRUCT(response); - ret = _pam_winbind_init_context(pamh, flags, argc, argv, &ctx); if (ret) { goto out; @@ -2560,16 +2864,15 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, time_t pwdlastset_prelim = 0; /* instruct user what is happening */ -#define greeting "Changing password for " - Announce = (char *) malloc(sizeof(greeting) + strlen(user)); - if (Announce == NULL) { + +#define greeting "Changing password for" + Announce = talloc_asprintf(ctx, "%s %s", greeting, user); + if (!Announce) { _pam_log(ctx, LOG_CRIT, "password - out of memory"); ret = PAM_BUF_ERR; goto out; } - (void) strcpy(Announce, greeting); - (void) strcpy(Announce + sizeof(greeting) - 1, user); #undef greeting lctrl = ctx->ctrl | WINBIND__OLD_PASSWORD; @@ -2578,6 +2881,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, "(current) NT password: ", NULL, (const char **) &pass_old); + TALLOC_FREE(Announce); if (ret != PAM_SUCCESS) { _pam_log(ctx, LOG_NOTICE, "password - (old) token not obtained"); @@ -2587,7 +2891,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, /* verify that this is the password for this user */ ret = winbind_auth_request(ctx, user, pass_old, - NULL, NULL, 0, &response, + NULL, NULL, 0, + &error, NULL, NULL, &pwdlastset_prelim, NULL); if (ret != PAM_ACCT_EXPIRED && @@ -2696,6 +3001,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, const char *member = NULL; 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); @@ -2710,7 +3017,8 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, * */ ret = winbind_auth_request(ctx, user, pass_new, - member, cctype, 0, &response, + member, cctype, 0, + &error, &info, &policy, NULL, &username_ret); _pam_overwrite(pass_new); _pam_overwrite(pass_old); @@ -2718,19 +3026,24 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, if (ret == PAM_SUCCESS) { + struct wbcAuthUserInfo *user_info = NULL; + + if (info && info->info) { + user_info = info->info; + } + /* warn a user if the password is about to * expire soon */ - _pam_warn_password_expiry(ctx, &response, + _pam_warn_password_expiry(ctx, user_info, policy, warn_pwd_expire, NULL); /* set some info3 info for other modules in the * stack */ - _pam_set_data_info3(ctx, &response); + _pam_set_data_info3(ctx, user_info); /* put krb5ccname into env */ - _pam_setup_krb5_env(ctx, - response.data.auth.krb5ccname); + _pam_setup_krb5_env(ctx, info); if (username_ret) { pam_set_item(pamh, PAM_USER, @@ -2740,6 +3053,9 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, username_ret); free(username_ret); } + + wbcFreeMemory(info); + wbcFreeMemory(policy); } goto out; @@ -2749,18 +3065,28 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, } out: + { + /* Deal with offline errors. */ + int i; + const char *codes[] = { + "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", + "NT_STATUS_NO_LOGON_SERVERS", + "NT_STATUS_ACCESS_DENIED" + }; + + for (i=0; i<ARRAY_SIZE(codes); i++) { + int _ret; + if (_pam_check_remark_auth_err(ctx, error, codes[i], &_ret)) { + break; + } + } + } - /* Deal with offline errors. */ - PAM_WB_REMARK_CHECK_RESPONSE(ctx, response, - "NT_STATUS_NO_LOGON_SERVERS"); - PAM_WB_REMARK_CHECK_RESPONSE(ctx, response, - "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND"); - PAM_WB_REMARK_CHECK_RESPONSE(ctx, response, - "NT_STATUS_ACCESS_DENIED"); + wbcFreeMemory(error); _PAM_LOG_FUNCTION_LEAVE("pam_sm_chauthtok", ctx, ret); - _pam_winbind_free_context(ctx); + TALLOC_FREE(ctx); return ret; } |