From 6ec4306f8c3fed7ec5b5bd164c5829b2661589b7 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sat, 16 Apr 2011 15:41:50 +1000 Subject: auth/kerberos: Create common helper to get the verified PAC from GSSAPI This only works for Heimdal and MIT Krb5 1.8, other versions will get an ACCESS_DEINED error. We no longer manually verify any details of the PAC in Samba for GSSAPI logins, as we never had the information to do it properly, and it is better to have the GSSAPI library handle it. Andrew Bartlett --- auth/kerberos/gssapi_pac.c | 123 +++++++++++++++++++++++++++++++++++++ auth/kerberos/wscript_build | 3 + libcli/auth/krb5_wrap.h | 5 ++ libcli/auth/wscript_build | 2 +- source3/Makefile.in | 1 + source3/configure.in | 1 + source3/librpc/crypto/gse.c | 47 ++------------ source3/librpc/crypto/gse.h | 3 +- source3/rpc_server/dcesrv_gssapi.c | 62 +++---------------- source3/wscript | 2 +- source3/wscript_build | 1 + wscript_build | 1 + 12 files changed, 152 insertions(+), 99 deletions(-) create mode 100644 auth/kerberos/gssapi_pac.c create mode 100644 auth/kerberos/wscript_build diff --git a/auth/kerberos/gssapi_pac.c b/auth/kerberos/gssapi_pac.c new file mode 100644 index 0000000000..dd2fb7e0a7 --- /dev/null +++ b/auth/kerberos/gssapi_pac.c @@ -0,0 +1,123 @@ +/* + Unix SMB/CIFS implementation. + kerberos authorization data (PAC) utility library + Copyright (C) Andrew Bartlett 2011 + Copyright (C) Simo Sorce 2010. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#ifdef HAVE_KRB5 + +#include "libcli/auth/krb5_wrap.h" + +/* The Heimdal OID for getting the PAC */ +#define EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH 8 +/* EXTRACTION OID AUTHZ ID */ +#define EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID "\x2a\x85\x70\x2b\x0d\x03" "\x81\x00" + +static gss_OID_desc pac_data_oid = { + EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH, + (void *)EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID +}; + +NTSTATUS gssapi_obtain_pac_blob(TALLOC_CTX *mem_ctx, + gss_ctx_id_t gssapi_context, + gss_name_t gss_client_name, + DATA_BLOB *pac_blob) +{ + OM_uint32 gss_maj, gss_min; + gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; + gss_buffer_desc pac_buffer; + gss_buffer_desc pac_display_buffer; + gss_buffer_desc pac_name = { + .value = "urn:mspac:", + .length = sizeof("urn:mspac:")-1 + }; + NTSTATUS status; + int more = -1; + int authenticated = false; + int complete = false; + +#ifdef HAVE_GSS_GET_NAME_ATTRIBUTE + gss_maj = gss_get_name_attribute( + &gss_min, gss_client_name, &pac_name, + &authenticated, &complete, + &pac_buffer, &pac_display_buffer, &more); + + if (gss_maj != 0) { + DEBUG(0, ("obtaining PAC via GSSAPI gss_get_name_attribute failed: %s\n", + gssapi_error_string(mem_ctx, gss_maj, gss_min, gss_mech_krb5))); + return NT_STATUS_ACCESS_DENIED; + } else if (authenticated && complete) { + /* The PAC blob is returned directly */ + *pac_blob = data_blob_talloc(mem_ctx, pac_buffer.value, + pac_buffer.length); + + if (!pac_blob->data) { + status = NT_STATUS_NO_MEMORY; + } else { + status = NT_STATUS_OK; + } + + gss_maj = gss_release_buffer(&gss_min, &pac_buffer); + gss_maj = gss_release_buffer(&gss_min, &pac_display_buffer); + return status; + } else { + DEBUG(0, ("obtaining PAC via GSSAPI failed: authenticated: %s, complete: %s, more: %s\n", + authenticated ? "true" : "false", + complete ? "true" : "false", + more ? "true" : "false")); + return NT_STATUS_ACCESS_DENIED; + } + +#endif + /* If we didn't have the routine to get a verified, validated + * PAC (supplied only by MIT at the time of writing), then try + * with the Heimdal OID (fetches the PAC directly and always + * validates) */ + gss_maj = gss_inquire_sec_context_by_oid( + &gss_min, gssapi_context, + &pac_data_oid, &set); + + /* First check for the error MIT gives for an unknown OID */ + if (gss_maj == GSS_S_UNAVAILABLE) { + DEBUG(1, ("unable to obtain a PAC against this GSSAPI library. " + "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n")); + } else if (gss_maj != 0) { + DEBUG(2, ("obtaining PAC via GSSAPI gss_inqiure_sec_context_by_oid (Heimdal OID) failed: %s\n", + gssapi_error_string(mem_ctx, gss_maj, gss_min, gss_mech_krb5))); + } else { + if (set == GSS_C_NO_BUFFER_SET) { + DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown " + "data in results.\n")); + return NT_STATUS_INTERNAL_ERROR; + } + + /* The PAC blob is returned directly */ + *pac_blob = data_blob_talloc(mem_ctx, set->elements[0].value, + set->elements[0].length); + if (!pac_blob->data) { + status = NT_STATUS_NO_MEMORY; + } else { + status = NT_STATUS_OK; + } + + gss_maj = gss_release_buffer_set(&gss_min, &set); + return status; + } + return NT_STATUS_ACCESS_DENIED; +} +#endif diff --git a/auth/kerberos/wscript_build b/auth/kerberos/wscript_build new file mode 100644 index 0000000000..c289aab839 --- /dev/null +++ b/auth/kerberos/wscript_build @@ -0,0 +1,3 @@ +bld.SAMBA_SUBSYSTEM('KRB5_PAC', + source='gssapi_pac.c', + deps='gssapi_krb5 krb5 ndr-krb5pac') diff --git a/libcli/auth/krb5_wrap.h b/libcli/auth/krb5_wrap.h index 31bee352ab..82769aede9 100644 --- a/libcli/auth/krb5_wrap.h +++ b/libcli/auth/krb5_wrap.h @@ -72,3 +72,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, krb5_const_principal client_principal, time_t tgs_authtime, struct PAC_DATA **pac_data_out); + +NTSTATUS gssapi_obtain_pac_blob(TALLOC_CTX *mem_ctx, + gss_ctx_id_t gssapi_context, + gss_name_t gss_client_name, + DATA_BLOB *pac_data); diff --git a/libcli/auth/wscript_build b/libcli/auth/wscript_build index 541eaf0434..262d483e06 100644 --- a/libcli/auth/wscript_build +++ b/libcli/auth/wscript_build @@ -41,4 +41,4 @@ bld.SAMBA_SUBSYSTEM('SPNEGO_PARSE', bld.SAMBA_SUBSYSTEM('KRB5_WRAP', source='krb5_wrap.c kerberos_pac.c', - deps='gssapi krb5 ndr-krb5pac com_err') + deps='gssapi_krb5 krb5 ndr-krb5pac com_err KRB5_PAC') diff --git a/source3/Makefile.in b/source3/Makefile.in index 3407dfd215..1e8b5bb994 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -598,6 +598,7 @@ LIBMSRPC_OBJ = $(SCHANNEL_OBJ) \ rpc_client/cli_pipe.o \ librpc/crypto/gse_krb5.o \ librpc/crypto/gse.o \ + ../auth/kerberos/gssapi_pac.o \ librpc/crypto/cli_spnego.o \ librpc/rpc/rpc_common.o \ rpc_client/rpc_transport_np.o \ diff --git a/source3/configure.in b/source3/configure.in index 556b8d3aee..883f0b1df0 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -3869,6 +3869,7 @@ if test x"$with_ads_support" != x"no"; then AC_CHECK_FUNC_EXT(krb5_get_host_realm, $KRB5_LIBS) AC_CHECK_FUNC_EXT(krb5_free_host_realm, $KRB5_LIBS) AC_CHECK_FUNC_EXT(gss_krb5_import_cred, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(gss_get_name_attribute, $KRB5_LIBS) # MIT krb5 1.8 does not expose this call (yet) AC_CHECK_DECLS(krb5_get_credentials_for_user, [], [], [#include ]) diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c index 0d9eead082..42e9c942a9 100644 --- a/source3/librpc/crypto/gse.c +++ b/source3/librpc/crypto/gse.c @@ -62,16 +62,6 @@ gss_OID_desc gse_authz_data_oid = { (void *)GSE_EXTRACT_RELEVANT_AUTHZ_DATA_OID }; -#ifndef GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID -#define GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH 11 -#define GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0c" -#endif - -gss_OID_desc gse_authtime_oid = { - GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH, - (void *)GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID -}; - static char *gse_errstr(TALLOC_CTX *mem_ctx, OM_uint32 maj, OM_uint32 min); struct gse_context { @@ -692,42 +682,15 @@ NTSTATUS gse_get_authz_data(struct gse_context *gse_ctx, return NT_STATUS_OK; } -NTSTATUS gse_get_authtime(struct gse_context *gse_ctx, time_t *authtime) +NTSTATUS gse_get_pac_blob(struct gse_context *gse_ctx, + TALLOC_CTX *mem_ctx, DATA_BLOB *pac_blob) { - OM_uint32 gss_min, gss_maj; - gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; - int32_t tkttime; - if (!gse_ctx->authenticated) { return NT_STATUS_ACCESS_DENIED; } - gss_maj = gss_inquire_sec_context_by_oid( - &gss_min, gse_ctx->gss_ctx, - &gse_authtime_oid, &set); - if (gss_maj) { - DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n", - gse_errstr(talloc_tos(), gss_maj, gss_min))); - return NT_STATUS_NOT_FOUND; - } - - if ((set == GSS_C_NO_BUFFER_SET) || (set->count != 1) != 0) { - DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown " - "data in results.\n")); - return NT_STATUS_INTERNAL_ERROR; - } - - if (set->elements[0].length != sizeof(int32_t)) { - DEBUG(0, ("Invalid authtime size!\n")); - return NT_STATUS_INTERNAL_ERROR; - } - - tkttime = *((int32_t *)set->elements[0].value); - - gss_maj = gss_release_buffer_set(&gss_min, &set); - - *authtime = (time_t)tkttime; - return NT_STATUS_OK; + return gssapi_obtain_pac_blob(mem_ctx, gse_ctx->gss_ctx, + gse_ctx->client_name, pac_blob); } size_t gse_get_signature_length(struct gse_context *gse_ctx, @@ -1017,4 +980,4 @@ NTSTATUS gse_sigcheck(TALLOC_CTX *mem_ctx, struct gse_context *gse_ctx, return NT_STATUS_NOT_IMPLEMENTED; } -#endif /* HAVE_KRB5 && HAVE_GSSAPI_EXT_H && HAVE_GSS_WRAP_IOV */ +#endif /* HAVE_KRB5 && HAVE_GSS_WRAP_IOV */ diff --git a/source3/librpc/crypto/gse.h b/source3/librpc/crypto/gse.h index fbcf5b6e10..27cc2e9255 100644 --- a/source3/librpc/crypto/gse.h +++ b/source3/librpc/crypto/gse.h @@ -56,7 +56,8 @@ NTSTATUS gse_get_client_name(struct gse_context *gse_ctx, TALLOC_CTX *mem_ctx, char **client_name); NTSTATUS gse_get_authz_data(struct gse_context *gse_ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *pac); -NTSTATUS gse_get_authtime(struct gse_context *gse_ctx, time_t *authtime); +NTSTATUS gse_get_pac_blob(struct gse_context *gse_ctx, + TALLOC_CTX *mem_ctx, DATA_BLOB *pac_blob); size_t gse_get_signature_length(struct gse_context *gse_ctx, int seal, size_t payload_size); diff --git a/source3/rpc_server/dcesrv_gssapi.c b/source3/rpc_server/dcesrv_gssapi.c index ec02459633..b63f4f129e 100644 --- a/source3/rpc_server/dcesrv_gssapi.c +++ b/source3/rpc_server/dcesrv_gssapi.c @@ -23,6 +23,7 @@ #include "../librpc/gen_ndr/ndr_krb5pac.h" #include "librpc/crypto/gse.h" #include "auth.h" +#include "libcli/auth/krb5_wrap.h" NTSTATUS gssapi_server_auth_start(TALLOC_CTX *mem_ctx, bool do_sign, @@ -105,14 +106,9 @@ NTSTATUS gssapi_server_get_user_info(struct gse_context *gse_ctx, struct auth_serversupplied_info **server_info) { TALLOC_CTX *tmp_ctx; - DATA_BLOB auth_data; - time_t tgs_authtime; - NTTIME tgs_authtime_nttime; - DATA_BLOB pac; + DATA_BLOB pac_blob; struct PAC_DATA *pac_data; - struct PAC_LOGON_NAME *logon_name = NULL; struct PAC_LOGON_INFO *logon_info = NULL; - enum ndr_err_code ndr_err; unsigned int i; bool is_mapped; bool is_guest; @@ -122,14 +118,13 @@ NTSTATUS gssapi_server_get_user_info(struct gse_context *gse_ctx, char *username; struct passwd *pw; NTSTATUS status; - bool bret; tmp_ctx = talloc_new(mem_ctx); if (!tmp_ctx) { return NT_STATUS_NO_MEMORY; } - status = gse_get_authz_data(gse_ctx, tmp_ctx, &auth_data); + status = gse_get_pac_blob(gse_ctx, tmp_ctx, &pac_blob); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { /* TODO: Fetch user by principal name ? */ status = NT_STATUS_ACCESS_DENIED; @@ -139,37 +134,18 @@ NTSTATUS gssapi_server_get_user_info(struct gse_context *gse_ctx, goto done; } - bret = unwrap_pac(tmp_ctx, &auth_data, &pac); - if (!bret) { - DEBUG(1, ("Failed to unwrap PAC\n")); - status = NT_STATUS_ACCESS_DENIED; - goto done; - } - - status = gse_get_client_name(gse_ctx, tmp_ctx, &princ_name); + status = kerberos_decode_pac(tmp_ctx, + pac_blob, + NULL, NULL, NULL, NULL, 0, &pac_data); + data_blob_free(&pac_blob); if (!NT_STATUS_IS_OK(status)) { goto done; } - status = gse_get_authtime(gse_ctx, &tgs_authtime); + status = gse_get_client_name(gse_ctx, tmp_ctx, &princ_name); if (!NT_STATUS_IS_OK(status)) { goto done; } - unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime); - - pac_data = talloc_zero(tmp_ctx, struct PAC_DATA); - if (!pac_data) { - status = NT_STATUS_NO_MEMORY; - goto done; - } - - ndr_err = ndr_pull_struct_blob(&pac, pac_data, pac_data, - (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - DEBUG(1, ("Failed to parse the PAC for %s\n", princ_name)); - status = ndr_map_error2ntstatus(ndr_err); - goto done; - } /* get logon name and logon info */ for (i = 0; i < pac_data->num_buffers; i++) { @@ -182,9 +158,6 @@ NTSTATUS gssapi_server_get_user_info(struct gse_context *gse_ctx, } logon_info = data_buf->info->logon_info.info; break; - case PAC_TYPE_LOGON_NAME: - logon_name = &data_buf->info->logon_name; - break; default: break; } @@ -194,25 +167,6 @@ NTSTATUS gssapi_server_get_user_info(struct gse_context *gse_ctx, status = NT_STATUS_NOT_FOUND; goto done; } - if (!logon_name) { - DEBUG(1, ("Invalid PAC data, missing logon info!\n")); - status = NT_STATUS_NOT_FOUND; - goto done; - } - - /* check time */ - if (tgs_authtime_nttime != logon_name->logon_time) { - DEBUG(1, ("Logon time mismatch between ticket and PAC!\n" - "PAC Time = %s | Ticket Time = %s\n", - nt_time_string(tmp_ctx, logon_name->logon_time), - nt_time_string(tmp_ctx, tgs_authtime_nttime))); - status = NT_STATUS_ACCESS_DENIED; - goto done; - } - - /* TODO: Should we check princ_name against account_name in - * logon_name ? Are they supposed to be identical, or can an - * account_name be different from the UPN ? */ status = get_user_from_kerberos_info(tmp_ctx, client_id->name, princ_name, logon_info, diff --git a/source3/wscript b/source3/wscript index 49f11f2e5b..6081ac9d4d 100644 --- a/source3/wscript +++ b/source3/wscript @@ -632,7 +632,7 @@ msg.msg_acctrightslen = sizeof(fd); if conf.CHECK_FUNCS_IN('gss_display_status', 'gssapi') or \ conf.CHECK_FUNCS_IN('gss_display_status', 'gssapi_krb5'): have_gssapi=True - conf.CHECK_FUNCS_IN('gss_wrap_iov gss_krb5_import_cred', 'gssapi gssapi_krb5 krb5') + conf.CHECK_FUNCS_IN('gss_wrap_iov gss_krb5_import_cred gss_get_name_attribute', 'gssapi gssapi_krb5 krb5') conf.CHECK_FUNCS_IN('krb5_mk_req_extended krb5_kt_compare', 'krb5') conf.CHECK_FUNCS(''' krb5_set_real_time krb5_set_default_in_tkt_etypes krb5_set_default_tgs_enctypes diff --git a/source3/wscript_build b/source3/wscript_build index dc01554c2e..8b337e6ccb 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -1327,6 +1327,7 @@ if not bld.env.toplevel_build: bld.RECURSE('../lib/util/charset') bld.RECURSE('../auth') +bld.RECURSE('../auth/kerberos') bld.RECURSE('../lib/addns') bld.RECURSE('../lib/async_req') bld.RECURSE('../libcli/auth') diff --git a/wscript_build b/wscript_build index d955f90dc2..174a25a282 100644 --- a/wscript_build +++ b/wscript_build @@ -49,6 +49,7 @@ bld.RECURSE('source4/smbd') bld.RECURSE('source4/libnet') bld.RECURSE('source4/auth') bld.RECURSE('auth') +bld.RECURSE('auth/kerberos') bld.RECURSE('lib/iniparser/src') bld.RECURSE('nsswitch') bld.RECURSE('nsswitch/libwbclient') -- cgit