diff options
-rw-r--r-- | nsswitch/libwbclient/wbc_pam.c | 16 | ||||
-rw-r--r-- | nsswitch/libwbclient/wbclient.h | 42 | ||||
-rw-r--r-- | nsswitch/winbind_struct_protocol.h | 1 | ||||
-rw-r--r-- | source3/winbindd/winbindd_pam.c | 128 | ||||
-rw-r--r-- | source3/winbindd/winbindd_pam_auth_crap.c | 23 | ||||
-rw-r--r-- | source3/winbindd/winbindd_proto.h | 8 |
6 files changed, 191 insertions, 27 deletions
diff --git a/nsswitch/libwbclient/wbc_pam.c b/nsswitch/libwbclient/wbc_pam.c index f7fb9f23f6..f183cc61b1 100644 --- a/nsswitch/libwbclient/wbc_pam.c +++ b/nsswitch/libwbclient/wbc_pam.c @@ -364,7 +364,7 @@ wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params, BAIL_ON_WBC_ERROR(wbc_status); } - if (!params->account_name) { + if (params->level != WBC_AUTH_USER_LEVEL_PAC && !params->account_name) { wbc_status = WBC_ERR_INVALID_PARAM; BAIL_ON_WBC_ERROR(wbc_status); } @@ -491,6 +491,20 @@ wbcErr wbcAuthenticateUserEx(const struct wbcAuthUserParams *params, request.data.auth_crap.nt_resp_len); } break; + + case WBC_AUTH_USER_LEVEL_PAC: + cmd = WINBINDD_PAM_AUTH_CRAP; + request.flags = WBFLAG_PAM_AUTH_PAC | WBFLAG_PAM_INFO3_TEXT; + request.extra_data.data = malloc(params->password.pac.length); + if (request.extra_data.data == NULL) { + wbc_status = WBC_ERR_NO_MEMORY; + BAIL_ON_WBC_ERROR(wbc_status); + } + memcpy(request.extra_data.data, params->password.pac.data, + params->password.pac.length); + request.extra_len = params->password.pac.length; + break; + default: break; } diff --git a/nsswitch/libwbclient/wbclient.h b/nsswitch/libwbclient/wbclient.h index cb70cbd513..473c9019d6 100644 --- a/nsswitch/libwbclient/wbclient.h +++ b/nsswitch/libwbclient/wbclient.h @@ -197,6 +197,25 @@ struct wbcDomainInfo { #define WBC_DOMINFO_TRUSTTYPE_EXTERNAL 0x00000003 /** + * @brief Generic Blob + **/ + +struct wbcBlob { + uint8_t *data; + size_t length; +}; + +/** + * @brief Named Blob + **/ + +struct wbcNamedBlob { + const char *name; + uint32_t flags; + struct wbcBlob blob; +}; + +/** * @brief Auth User Parameters **/ @@ -212,7 +231,8 @@ struct wbcAuthUserParams { enum wbcAuthUserLevel { WBC_AUTH_USER_LEVEL_PLAIN = 1, WBC_AUTH_USER_LEVEL_HASH = 2, - WBC_AUTH_USER_LEVEL_RESPONSE = 3 + WBC_AUTH_USER_LEVEL_RESPONSE = 3, + WBC_AUTH_USER_LEVEL_PAC = 4 } level; union { const char *plaintext; @@ -227,29 +247,11 @@ struct wbcAuthUserParams { uint32_t lm_length; uint8_t *lm_data; } response; + struct wbcBlob pac; } password; }; /** - * @brief Generic Blob - **/ - -struct wbcBlob { - uint8_t *data; - size_t length; -}; - -/** - * @brief Named Blob - **/ - -struct wbcNamedBlob { - const char *name; - uint32_t flags; - struct wbcBlob blob; -}; - -/** * @brief Logon User Parameters **/ diff --git a/nsswitch/winbind_struct_protocol.h b/nsswitch/winbind_struct_protocol.h index e5ed8e1b3a..c1704c8e0b 100644 --- a/nsswitch/winbind_struct_protocol.h +++ b/nsswitch/winbind_struct_protocol.h @@ -218,6 +218,7 @@ typedef struct winbindd_gr { #define WBFLAG_PAM_FALLBACK_AFTER_KRB5 0x00002000 #define WBFLAG_PAM_CACHED_LOGIN 0x00004000 #define WBFLAG_PAM_GET_PWD_POLICY 0x00008000 +#define WBFLAG_PAM_AUTH_PAC 0x00010000 /* generic request flags */ #define WBFLAG_QUERY_ONLY 0x00000020 /* not used */ diff --git a/source3/winbindd/winbindd_pam.c b/source3/winbindd/winbindd_pam.c index 6ad0baf196..5b6b77b48c 100644 --- a/source3/winbindd/winbindd_pam.c +++ b/source3/winbindd/winbindd_pam.c @@ -38,6 +38,9 @@ #include "passdb/machine_sid.h" #include "auth.h" #include "../lib/tsocket/tsocket.h" +#include "auth/kerberos/pac_utils.h" +#include "auth/gensec/gensec.h" +#include "librpc/crypto/gse_krb5.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND @@ -724,12 +727,12 @@ bool check_request_flags(uint32_t flags) /**************************************************************** ****************************************************************/ -static NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx, - struct winbindd_response *resp, - uint32_t request_flags, - struct netr_SamInfo3 *info3, - const char *name_domain, - const char *name_user) +NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx, + struct winbindd_response *resp, + uint32_t request_flags, + struct netr_SamInfo3 *info3, + const char *name_domain, + const char *name_user) { NTSTATUS result; @@ -2270,3 +2273,116 @@ enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domai return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; } + +#ifdef HAVE_KRB5 +static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob, + struct PAC_LOGON_INFO **logon_info) +{ + krb5_context krbctx = NULL; + krb5_error_code k5ret; + krb5_keytab keytab; + krb5_kt_cursor cursor; + krb5_keytab_entry entry; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(entry); + ZERO_STRUCT(cursor); + + k5ret = krb5_init_context(&krbctx); + if (k5ret) { + DEBUG(1, ("Failed to initialize kerberos context: %s\n", + error_message(k5ret))); + status = krb5_to_nt_status(k5ret); + goto out; + } + + k5ret = gse_krb5_get_server_keytab(krbctx, &keytab); + if (k5ret) { + DEBUG(1, ("Failed to get keytab: %s\n", + error_message(k5ret))); + status = krb5_to_nt_status(k5ret); + goto out_free; + } + + k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor); + if (k5ret) { + DEBUG(1, ("Failed to start seq: %s\n", + error_message(k5ret))); + status = krb5_to_nt_status(k5ret); + goto out_keytab; + } + + k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor); + while (k5ret == 0) { + status = kerberos_pac_logon_info(mem_ctx, pac_blob, + krbctx, NULL, + KRB5_KT_KEY(&entry), NULL, 0, + logon_info); + if (NT_STATUS_IS_OK(status)) { + break; + } + k5ret = smb_krb5_kt_free_entry(krbctx, &entry); + k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor); + } + + k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor); + if (k5ret) { + DEBUG(1, ("Failed to end seq: %s\n", + error_message(k5ret))); + } +out_keytab: + k5ret = krb5_kt_close(krbctx, keytab); + if (k5ret) { + DEBUG(1, ("Failed to close keytab: %s\n", + error_message(k5ret))); + } +out_free: + krb5_free_context(krbctx); +out: + return status; +} + +NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state, + struct netr_SamInfo3 **info3) +{ + struct winbindd_request *req = state->request; + DATA_BLOB pac_blob; + struct PAC_LOGON_INFO *logon_info = NULL; + NTSTATUS result; + + pac_blob = data_blob_const(req->extra_data.data, req->extra_len); + result = extract_pac_vrfy_sigs(state->mem_ctx, pac_blob, &logon_info); + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) { + DEBUG(1, ("Error during PAC signature verification: %s\n", + nt_errstr(result))); + return result; + } + + if (logon_info) { + /* Signature verification succeeded, trust the PAC */ + netsamlogon_cache_store(NULL, &logon_info->info3); + + } else { + /* Try without signature verification */ + result = kerberos_pac_logon_info(state->mem_ctx, pac_blob, NULL, + NULL, NULL, NULL, 0, + &logon_info); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10, ("Could not extract PAC: %s\n", + nt_errstr(result))); + return result; + } + } + + *info3 = &logon_info->info3; + + return NT_STATUS_OK; +} +#else /* HAVE_KRB5 */ +NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state, + struct netr_SamInfo3 **info3) +{ + return NT_STATUS_NO_SUCH_USER; +} +#endif /* HAVE_KRB5 */ diff --git a/source3/winbindd/winbindd_pam_auth_crap.c b/source3/winbindd/winbindd_pam_auth_crap.c index 2fb5111510..ffbc322fc0 100644 --- a/source3/winbindd/winbindd_pam_auth_crap.c +++ b/source3/winbindd/winbindd_pam_auth_crap.c @@ -22,6 +22,8 @@ struct winbindd_pam_auth_crap_state { struct winbindd_response *response; + struct netr_SamInfo3 *info3; + uint32_t flags; }; static void winbindd_pam_auth_crap_done(struct tevent_req *subreq); @@ -42,6 +44,21 @@ struct tevent_req *winbindd_pam_auth_crap_send( return NULL; } + if (request->flags & WBFLAG_PAM_AUTH_PAC) { + NTSTATUS status; + + state->flags = request->flags; + status = winbindd_pam_auth_pac_send(cli, &state->info3); + if (NT_STATUS_IS_OK(status)) { + /* Defer filling out response to recv */ + tevent_req_done(req); + } else { + tevent_req_nterror(req, status); + } + + return tevent_req_post(req, ev); + } + /* Ensure null termination */ request->data.auth_crap.user[ sizeof(request->data.auth_crap.user)-1] = '\0'; @@ -114,6 +131,12 @@ NTSTATUS winbindd_pam_auth_crap_recv(struct tevent_req *req, set_auth_errors(response, status); return status; } + + if (state->flags & WBFLAG_PAM_AUTH_PAC) { + return append_auth_data(response, response, state->flags, + state->info3, NULL, NULL); + } + *response = *state->response; response->result = WINBINDD_PENDING; state->response = talloc_move(response, &state->response); diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h index ec5ec372d1..5cc90f2ab0 100644 --- a/source3/winbindd/winbindd_proto.h +++ b/source3/winbindd/winbindd_proto.h @@ -353,6 +353,12 @@ void ndr_print_winbindd_domain(struct ndr_print *ndr, /* The following definitions come from winbindd/winbindd_pam.c */ bool check_request_flags(uint32_t flags); +NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx, + struct winbindd_response *resp, + uint32_t request_flags, + struct netr_SamInfo3 *info3, + const char *name_domain, + const char *name_user); uid_t get_uid_from_request(struct winbindd_request *request); struct winbindd_domain *find_auth_domain(uint8_t flags, const char *domain_name); @@ -365,6 +371,8 @@ enum winbindd_result winbindd_dual_pam_chauthtok(struct winbindd_domain *contact enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain, struct winbindd_cli_state *state) ; enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state); +NTSTATUS winbindd_pam_auth_pac_send(struct winbindd_cli_state *state, + struct netr_SamInfo3 **info3); /* The following definitions come from winbindd/winbindd_util.c */ |