diff options
author | Andrew Bartlett <abartlet@samba.org> | 2012-01-11 18:06:55 +1100 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2012-01-12 18:02:54 +1100 |
commit | 49f8113fabd2603b45439404c91d350b4d6eaeac (patch) | |
tree | ace3a19fcb94bb5ab6396f1be30fb2af7bec8817 /source4 | |
parent | d0bb8b8a15c76c739062e7a78c013b54729dc5ab (diff) | |
download | samba-49f8113fabd2603b45439404c91d350b4d6eaeac.tar.gz samba-49f8113fabd2603b45439404c91d350b4d6eaeac.tar.bz2 samba-49f8113fabd2603b45439404c91d350b4d6eaeac.zip |
s4-kdc Do the KDC PAC checksum validation in the Samba plugin
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
Diffstat (limited to 'source4')
-rw-r--r-- | source4/kdc/kdc.c | 29 | ||||
-rw-r--r-- | source4/kdc/mit_samba.c | 5 | ||||
-rw-r--r-- | source4/kdc/pac-glue.c | 114 | ||||
-rw-r--r-- | source4/kdc/pac-glue.h | 10 | ||||
-rw-r--r-- | source4/kdc/wdc-samba4.c | 36 | ||||
-rw-r--r-- | source4/kdc/wscript_build | 2 |
6 files changed, 152 insertions, 44 deletions
diff --git a/source4/kdc/kdc.c b/source4/kdc/kdc.c index e91b8a9b9f..d1ce527b24 100644 --- a/source4/kdc/kdc.c +++ b/source4/kdc/kdc.c @@ -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); diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c index 06ee46eac0..f56e6796d0 100644 --- a/source4/kdc/mit_samba.c +++ b/source4/kdc/mit_samba.c @@ -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))); diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c index 5718452cc0..3983d7b201 100644 --- a/source4/kdc/pac-glue.c +++ b/source4/kdc/pac-glue.c @@ -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); +} + + + diff --git a/source4/kdc/pac-glue.h b/source4/kdc/pac-glue.h index 66c20cdc1e..0e1cdcd2f2 100644 --- a/source4/kdc/pac-glue.h +++ b/source4/kdc/pac-glue.h @@ -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); diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c index 99ad96a6b5..70e849ccb7 100644 --- a/source4/kdc/wdc-samba4.c +++ b/source4/kdc/wdc-samba4.c @@ -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) { diff --git a/source4/kdc/wscript_build b/source4/kdc/wscript_build index 5b2ba28793..22eee12c8b 100644 --- a/source4/kdc/wscript_build +++ b/source4/kdc/wscript_build @@ -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, ) |