auth/kerberos: Create common helper to get the verified PAC from GSSAPI
authorAndrew Bartlett <abartlet@samba.org>
Sat, 16 Apr 2011 05:41:50 +0000 (15:41 +1000)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 27 Apr 2011 01:56:48 +0000 (11:56 +1000)
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

12 files changed:
auth/kerberos/gssapi_pac.c [new file with mode: 0644]
auth/kerberos/wscript_build [new file with mode: 0644]
libcli/auth/krb5_wrap.h
libcli/auth/wscript_build
source3/Makefile.in
source3/configure.in
source3/librpc/crypto/gse.c
source3/librpc/crypto/gse.h
source3/rpc_server/dcesrv_gssapi.c
source3/wscript
source3/wscript_build
wscript_build

diff --git a/auth/kerberos/gssapi_pac.c b/auth/kerberos/gssapi_pac.c
new file mode 100644 (file)
index 0000000..dd2fb7e
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+   Unix SMB/CIFS implementation.
+   kerberos authorization data (PAC) utility library
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 (file)
index 0000000..c289aab
--- /dev/null
@@ -0,0 +1,3 @@
+bld.SAMBA_SUBSYSTEM('KRB5_PAC',
+                    source='gssapi_pac.c',
+                    deps='gssapi_krb5 krb5 ndr-krb5pac')
index 31bee352ab0f7c2a8bac512d7005c6fc2ed1631e..82769aede9341bd5ccc12d84800d00f9d011805b 100644 (file)
@@ -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);
index 541eaf04341edf759020d68cab09e8188775d3fc..262d483e068266bee9a496609d38094aadb35dfe 100644 (file)
@@ -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')
index 3407dfd215fec3845a09ce2de31e1d263d4daa8c..1e8b5bb994c74598f7f5ff7915e71dca74583d86 100644 (file)
@@ -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 \
index 556b8d3aeec41993cd065f7ef71f1205c596cfba..883f0b1df07fbbfc4156dbc9ac3cd2067c20a617 100644 (file)
@@ -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 <krb5.h>])
index 0d9eead082edc7f985900e96e6b2e45f5ab09f02..42e9c942a93d95664a44a62017d2eab473a3d84f 100644 (file)
@@ -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 */
index fbcf5b6e10d0846bb83a029a188a1ae7cf3e0595..27cc2e92559b202e18e59ba09b366ce9cc9bd06a 100644 (file)
@@ -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);
index ec024596332316ef14ef8d45e2cbc0c5aaeb90c3..b63f4f129e7553077ce4707ffbe910a827172041 100644 (file)
@@ -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,
index 49f11f2e5b2393d401b6333efdf9a8b7a74737fd..6081ac9d4da2b43ebd244af02f4bd405ce5bbb31 100644 (file)
@@ -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
index dc01554c2e8f09ad10b7eadf6f9ab806ac6c9ed5..8b337e6ccb3a3a4f146091c64406c79a003bb9bc 100755 (executable)
@@ -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')
index d955f90dc2ce9b97675d985be11d3debb8d64178..174a25a282e993d9a4a969a6f540b90dd9b15844 100644 (file)
@@ -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')