s4-kdc Do the KDC PAC checksum validation in the Samba plugin
authorAndrew Bartlett <abartlet@samba.org>
Wed, 11 Jan 2012 07:06:55 +0000 (18:06 +1100)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 12 Jan 2012 07:02:54 +0000 (18:02 +1100)
Here we can fetch the right key, and check if the PAC is likely to be signed by a key that
we know.  We cannot check the KDC signature on incoming trusts.

Andrew Bartlett

source4/kdc/kdc.c
source4/kdc/mit_samba.c
source4/kdc/pac-glue.c
source4/kdc/pac-glue.h
source4/kdc/wdc-samba4.c
source4/kdc/wscript_build

index e91b8a9b9fd2e83051a70cad1c6cf35680b6f9d4..d1ce527b24a73f3ddb05729e8d5ce7f69446ce43 100644 (file)
@@ -32,6 +32,7 @@
 #include "lib/socket/netif.h"
 #include "param/param.h"
 #include "kdc/kdc-glue.h"
+#include "kdc/pac-glue.h"
 #include "dsdb/samdb/samdb.h"
 #include "auth/session.h"
 
@@ -777,7 +778,6 @@ static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc, struct loadparm_c
        return NT_STATUS_OK;
 }
 
-
 static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
                                 struct kdc_check_generic_kerberos *r)
 {
@@ -786,12 +786,9 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
        struct PAC_SIGNATURE_DATA kdc_sig;
        struct kdc_server *kdc = talloc_get_type(msg->private_data, struct kdc_server);
        enum ndr_err_code ndr_err;
-       krb5_enctype etype;
        int ret;
        hdb_entry_ex ent;
        krb5_principal principal;
-       krb5_keyblock keyblock;
-       Key *key;
 
        /* There is no reply to this request */
        r->out.generic_reply = data_blob(NULL, 0);
@@ -816,16 +813,6 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
        srv_sig = data_blob_const(pac_validate.ChecksumAndSignature.data,
                                  pac_validate.ChecksumLength);
 
-       if (pac_validate.SignatureType == CKSUMTYPE_HMAC_MD5) {
-               etype = ETYPE_ARCFOUR_HMAC_MD5;
-       } else {
-               ret = krb5_cksumtype_to_enctype(kdc->smb_krb5_context->krb5_context, pac_validate.SignatureType,
-                                               &etype);
-               if (ret != 0) {
-                       return NT_STATUS_LOGON_FAILURE;
-               }
-       }
-
        ret = krb5_make_principal(kdc->smb_krb5_context->krb5_context, &principal,
                                  lpcfg_realm(kdc->task->lp_ctx),
                                  "krbtgt", lpcfg_realm(kdc->task->lp_ctx),
@@ -849,21 +836,11 @@ static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
                return NT_STATUS_LOGON_FAILURE;
        }
 
-       ret = hdb_enctype2key(kdc->smb_krb5_context->krb5_context, &ent.entry, etype, &key);
-
-       if (ret != 0) {
-               hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent);
-               krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);
-               return NT_STATUS_LOGON_FAILURE;
-       }
-
-       keyblock = key->key;
-
        kdc_sig.type = pac_validate.SignatureType;
        kdc_sig.signature = data_blob_const(&pac_validate.ChecksumAndSignature.data[pac_validate.ChecksumLength],
                                            pac_validate.SignatureLength);
-       ret = check_pac_checksum(msg, srv_sig, &kdc_sig,
-                          kdc->smb_krb5_context->krb5_context, &keyblock);
+
+       ret = kdc_check_pac(kdc->smb_krb5_context->krb5_context, srv_sig, &kdc_sig, &ent);
 
        hdb_free_entry(kdc->smb_krb5_context->krb5_context, &ent);
        krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);
index 06ee46eac0243bc8c085038658f42e290d5f1662..f56e6796d0919caa6b193d09de89ce087a654873 100644 (file)
@@ -254,8 +254,11 @@ static int mit_samba_update_pac_data(struct mit_samba_context *ctx,
                goto done;
        }
 
+       /* TODO: An implementation-specific decision will need to be
+        * made as to when to check the KDC pac signature, and how to
+        * untrust untrusted RODCs */
        nt_status = samba_kdc_update_pac_blob(tmp_ctx, ctx->context,
-                                             pac, logon_blob);
+                                             pac, logon_blob, NULL, NULL);
        if (!NT_STATUS_IS_OK(nt_status)) {
                DEBUG(0, ("Building PAC failed: %s\n",
                          nt_errstr(nt_status)));
index 5718452cc00c1ac9379e3b1f95ddaa8c7f3c3805..3983d7b2012f5419d210e7200afd9c0f542bbeff 100644 (file)
@@ -30,6 +30,8 @@
 #include "kdc/pac-glue.h"
 #include "param/param.h"
 #include "librpc/gen_ndr/ndr_krb5pac.h"
+#include "libcli/security/security.h"
+#include "dsdb/samdb/samdb.h"
 
 static
 NTSTATUS samba_get_logon_info_pac_blob(TALLOC_CTX *mem_ctx,
@@ -143,22 +145,74 @@ bool samba_princ_needs_pac(struct hdb_entry_ex *princ)
        return true;
 }
 
-/* Was the krbtgt an RODC (and we are not) */
-bool samba_krbtgt_was_untrusted_rodc(struct hdb_entry_ex *princ)
+/* Was the krbtgt in this DB (ie, should we check the incoming signature) and was it an RODC */
+int samba_krbtgt_is_in_db(struct hdb_entry_ex *princ, bool *is_in_db, bool *is_untrusted)
 {
-
+       NTSTATUS status;
        struct samba_kdc_entry *p = talloc_get_type(princ->ctx, struct samba_kdc_entry);
-       int rodc_krbtgt_number;
+       int rodc_krbtgt_number, trust_direction;
+       uint32_t rid;
+
+       TALLOC_CTX *mem_ctx = talloc_new(NULL);
+       if (!mem_ctx) {
+               return ENOMEM;
+       }
+       
+       trust_direction = ldb_msg_find_attr_as_int(p->msg, "trustDirection", 0);
+
+       if (trust_direction != 0) {
+               /* Domain trust - we cannot check the sig, but we trust it for a correct PAC
+                  
+                  This is exactly where we should flag for SID
+                  validation when we do inter-foreest trusts
+                */
+               talloc_free(mem_ctx);
+               *is_untrusted = false;
+               *is_in_db = false;
+               return 0;
+       }
+
+       /* The lack of password controls etc applies to krbtgt by
+        * virtue of being that particular RID */
+       status = dom_sid_split_rid(NULL, samdb_result_dom_sid(mem_ctx, p->msg, "objectSid"), NULL, &rid);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(mem_ctx);
+               return EINVAL;
+       }
 
-       /* Determine if this was printed by an RODC */
        rodc_krbtgt_number = ldb_msg_find_attr_as_int(p->msg, "msDS-SecondaryKrbTgtNumber", -1);
-       if (rodc_krbtgt_number == -1) {
-               return false;
-       } else if (rodc_krbtgt_number != p->kdc_db_ctx->my_krbtgt_number) {
-               return true;
+
+       if (p->kdc_db_ctx->my_krbtgt_number == 0) {
+               if (rid == DOMAIN_RID_KRBTGT) {
+                       *is_untrusted = false;
+                       *is_in_db = true;
+                       talloc_free(mem_ctx);
+                       return 0;
+               } else if (rodc_krbtgt_number != -1) {
+                       *is_in_db = true;
+                       *is_untrusted = true;
+                       talloc_free(mem_ctx);
+                       return 0;
+               }
+       } else if ((rid != DOMAIN_RID_KRBTGT) && (rodc_krbtgt_number == p->kdc_db_ctx->my_krbtgt_number)) {
+               talloc_free(mem_ctx);
+               *is_untrusted = false;
+               *is_in_db = true;
+               return 0;
+       } else if (rid == DOMAIN_RID_KRBTGT) {
+               /* krbtgt viewed from an RODC */
+               talloc_free(mem_ctx);
+               *is_untrusted = false;
+               *is_in_db = false;
+               return 0;
        }
 
-       return false;
+       /* Another RODC */
+       talloc_free(mem_ctx);
+       *is_untrusted = true;
+       *is_in_db = false;
+       return 0;
 }
 
 NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx,
@@ -208,14 +262,16 @@ NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx,
 
 NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx,
                                   krb5_context context,
-                                  const krb5_pac pac, DATA_BLOB *pac_blob)
+                                  const krb5_pac pac, DATA_BLOB *pac_blob,
+                                  struct PAC_SIGNATURE_DATA *pac_srv_sig,
+                                  struct PAC_SIGNATURE_DATA *pac_kdc_sig)
 {
        struct auth_user_info_dc *user_info_dc;
        krb5_error_code ret;
        NTSTATUS nt_status;
 
        ret = kerberos_pac_to_user_info_dc(mem_ctx, pac,
-                                          context, &user_info_dc, NULL, NULL);
+                                          context, &user_info_dc, pac_srv_sig, pac_kdc_sig);
        if (ret) {
                return NT_STATUS_UNSUCCESSFUL;
        }
@@ -405,3 +461,37 @@ NTSTATUS samba_kdc_check_client_access(struct samba_kdc_entry *kdc_entry,
        return nt_status;
 }
 
+int kdc_check_pac(krb5_context context,
+                 DATA_BLOB srv_sig,
+                 struct PAC_SIGNATURE_DATA *kdc_sig,
+                 hdb_entry_ex *ent)
+{
+       krb5_enctype etype;
+       int ret;
+       krb5_keyblock keyblock;
+       Key *key;
+       if (kdc_sig->type == CKSUMTYPE_HMAC_MD5) {
+               etype = ETYPE_ARCFOUR_HMAC_MD5;
+       } else {
+               ret = krb5_cksumtype_to_enctype(context, 
+                                               kdc_sig->type,
+                                               &etype);
+               if (ret != 0) {
+                       return ret;
+               }
+       }
+
+       ret = hdb_enctype2key(context, &ent->entry, etype, &key);
+
+       if (ret != 0) {
+               return ret;
+       }
+
+       keyblock = key->key;
+
+       return check_pac_checksum(NULL, srv_sig, kdc_sig,
+                                context, &keyblock);
+}
+
+
+
index 66c20cdc1e2c7e8e81140ff584abedaa516db286..0e1cdcd2f2521f93c8c84e6062a94d17fd7758ee 100644 (file)
@@ -28,7 +28,7 @@ krb5_error_code samba_make_krb5_pac(krb5_context context,
 
 bool samba_princ_needs_pac(struct hdb_entry_ex *princ);
 
-bool samba_krbtgt_was_untrusted_rodc(struct hdb_entry_ex *princ);
+int samba_krbtgt_is_in_db(struct hdb_entry_ex *princ, bool *is_in_db, bool *is_untrusted);
 
 NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx,
                                struct hdb_entry_ex *client,
@@ -36,7 +36,9 @@ NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx,
 
 NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx,
                                   krb5_context context,
-                                  const krb5_pac pac, DATA_BLOB *pac_blob);
+                                  const krb5_pac pac, DATA_BLOB *pac_blob,
+                                  struct PAC_SIGNATURE_DATA *pac_srv_sig,
+                                  struct PAC_SIGNATURE_DATA *pac_kdc_sig);
 
 NTSTATUS samba_kdc_update_delegation_info_blob(TALLOC_CTX *mem_ctx,
                                krb5_context context,
@@ -53,3 +55,7 @@ NTSTATUS samba_kdc_check_client_access(struct samba_kdc_entry *kdc_entry,
                                       const char *client_name,
                                       const char *workstation,
                                       bool password_change);
+int kdc_check_pac(krb5_context krb5_context,
+                 DATA_BLOB server_sig,
+                 struct PAC_SIGNATURE_DATA *kdc_sig,
+                 hdb_entry_ex *ent);
index 99ad96a6b5d9983beb4847defaf25f925707038d..70e849ccb74179b35409f9637806a72c3b4acb7f 100644 (file)
@@ -68,6 +68,9 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
        DATA_BLOB *deleg_blob = NULL;
        krb5_error_code ret;
        NTSTATUS nt_status;
+       struct PAC_SIGNATURE_DATA *pac_srv_sig;
+       struct PAC_SIGNATURE_DATA *pac_kdc_sig;
+       bool is_in_db, is_untrusted;
 
        if (!mem_ctx) {
                return ENOMEM;
@@ -82,7 +85,13 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
        /* If the krbtgt was generated by an RODC, and we are not that
         * RODC, then we need to regenerate the PAC - we can't trust
         * it */
-       if (samba_krbtgt_was_untrusted_rodc(krbtgt)) {
+       ret = samba_krbtgt_is_in_db(krbtgt, &is_in_db, &is_untrusted);
+       if (ret != 0) {
+               talloc_free(mem_ctx);
+               return ret;
+       }
+
+       if (is_untrusted) {
                if (client == NULL) {
                        return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
                }
@@ -98,14 +107,37 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context,
                        return ENOMEM;
                }
 
+               pac_srv_sig = talloc_zero(mem_ctx, struct PAC_SIGNATURE_DATA);
+               if (!pac_srv_sig) {
+                       talloc_free(mem_ctx);
+                       return ENOMEM;
+               }
+
+               pac_kdc_sig = talloc_zero(mem_ctx, struct PAC_SIGNATURE_DATA);
+               if (!pac_kdc_sig) {
+                       talloc_free(mem_ctx);
+                       return ENOMEM;
+               }
+
                nt_status = samba_kdc_update_pac_blob(mem_ctx, context,
-                                                     *pac, pac_blob);
+                                                     *pac, pac_blob,
+                                                     pac_srv_sig, pac_kdc_sig);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        DEBUG(0, ("Building PAC failed: %s\n",
                                  nt_errstr(nt_status)));
                        talloc_free(mem_ctx);
                        return EINVAL;
                }
+               
+               if (is_in_db) {
+                       /* Now check the KDC signature, fetching the correct key based on the enc type */
+                       ret = kdc_check_pac(context, pac_srv_sig->signature, pac_kdc_sig, krbtgt);
+                       if (ret != 0) {
+                               DEBUG(1, ("PAC KDC signature failed to verify\n"));
+                               talloc_free(mem_ctx);
+                               return ret;
+                       }
+               }
        }
 
        if (delegated_proxy_principal) {
index 5b2ba28793de4eefee990636b3932187e56f2bb1..22eee12c8bb43e0c95d0d6a42df2768c2ccf325a 100644 (file)
@@ -4,7 +4,7 @@ bld.SAMBA_MODULE('service_kdc',
        source='kdc.c kpasswdd.c proxy.c',
        subsystem='service',
        init_function='server_service_kdc_init',
-       deps='kdc HDB_SAMBA4 WDC_SAMBA4 samba-hostconfig LIBTSOCKET LIBSAMBA_TSOCKET com_err samba_server_gensec',
+       deps='kdc HDB_SAMBA4 WDC_SAMBA4 samba-hostconfig LIBTSOCKET LIBSAMBA_TSOCKET com_err samba_server_gensec PAC_GLUE',
        internal_module=False,
        )