From 54d32c262bcab2bbe82a30909c5820fbcfa7a444 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 13 May 2016 00:13:33 +0200 Subject: [PATCH] s4:kdc: provide a PAC_UPN_DNS_INFO element for logons Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett --- source4/kdc/pac-glue.c | 104 ++++++++++++++++++++++++++++++++++++++- source4/kdc/pac-glue.h | 4 +- source4/kdc/wdc-samba4.c | 37 ++++++++++++-- source4/torture/rpc/remote_pac.c | 2 +- 4 files changed, 140 insertions(+), 7 deletions(-) diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c index 0b9fd12b764..ff3f62a11c4 100644 --- a/source4/kdc/pac-glue.c +++ b/source4/kdc/pac-glue.c @@ -78,6 +78,42 @@ NTSTATUS samba_get_logon_info_pac_blob(TALLOC_CTX *mem_ctx, } static +NTSTATUS samba_get_upn_info_pac_blob(TALLOC_CTX *mem_ctx, + const struct auth_user_info_dc *info, + DATA_BLOB *upn_data) +{ + union PAC_INFO pac_upn; + enum ndr_err_code ndr_err; + NTSTATUS nt_status; + + ZERO_STRUCT(pac_upn); + + *upn_data = data_blob_null; + + pac_upn.upn_dns_info.upn_name = info->info->user_principal_name; + pac_upn.upn_dns_info.dns_domain_name = strupper_talloc(mem_ctx, + info->info->dns_domain_name); + if (pac_upn.upn_dns_info.dns_domain_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + if (info->info->user_principal_constructed) { + pac_upn.upn_dns_info.flags |= PAC_UPN_DNS_FLAG_CONSTRUCTED; + } + + ndr_err = ndr_push_union_blob(upn_data, mem_ctx, &pac_upn, + PAC_TYPE_UPN_DNS_INFO, + (ndr_push_flags_fn_t)ndr_push_PAC_INFO); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + nt_status = ndr_map_error2ntstatus(ndr_err); + DEBUG(1, ("PAC UPN_DNS_INFO (presig) push failed: %s\n", + nt_errstr(nt_status))); + return nt_status; + } + + return NT_STATUS_OK; +} + +static NTSTATUS samba_get_cred_info_ndr_blob(TALLOC_CTX *mem_ctx, const struct ldb_message *msg, DATA_BLOB *cred_blob) @@ -277,11 +313,13 @@ krb5_error_code samba_kdc_encrypt_pac_credentials(krb5_context context, krb5_error_code samba_make_krb5_pac(krb5_context context, const DATA_BLOB *logon_blob, const DATA_BLOB *cred_blob, + const DATA_BLOB *upn_blob, const DATA_BLOB *deleg_blob, krb5_pac *pac) { krb5_data logon_data; krb5_data cred_data; + krb5_data upn_data; krb5_data deleg_data; krb5_data null_data; krb5_error_code ret; @@ -311,6 +349,18 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, } } + ZERO_STRUCT(upn_data); + if (upn_blob != NULL) { + ret = krb5_copy_data_contents(&upn_data, + upn_blob->data, + upn_blob->length); + if (ret != 0) { + kerberos_free_data_contents(context, &logon_data); + kerberos_free_data_contents(context, &cred_data); + return ret; + } + } + ZERO_STRUCT(deleg_data); if (deleg_blob != NULL) { ret = krb5_copy_data_contents(&deleg_data, @@ -319,6 +369,7 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, if (ret != 0) { kerberos_free_data_contents(context, &logon_data); kerberos_free_data_contents(context, &cred_data); + kerberos_free_data_contents(context, &upn_data); return ret; } } @@ -327,6 +378,7 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, if (ret != 0) { kerberos_free_data_contents(context, &logon_data); kerberos_free_data_contents(context, &cred_data); + kerberos_free_data_contents(context, &upn_data); kerberos_free_data_contents(context, &deleg_data); return ret; } @@ -334,6 +386,7 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, ret = krb5_pac_add_buffer(context, *pac, PAC_TYPE_LOGON_INFO, &logon_data); kerberos_free_data_contents(context, &logon_data); if (ret != 0) { + kerberos_free_data_contents(context, &upn_data); kerberos_free_data_contents(context, &cred_data); kerberos_free_data_contents(context, &deleg_data); return ret; @@ -345,6 +398,32 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, &cred_data); kerberos_free_data_contents(context, &cred_data); if (ret != 0) { + kerberos_free_data_contents(context, &upn_data); + kerberos_free_data_contents(context, &deleg_data); + return ret; + } + } + + /* + * null_data will be filled by the generic KDC code in the caller + * here we just add it in order to have it before + * PAC_TYPE_UPN_DNS_INFO + */ + ret = krb5_pac_add_buffer(context, *pac, + PAC_TYPE_LOGON_NAME, + &null_data); + if (ret != 0) { + kerberos_free_data_contents(context, &upn_data); + kerberos_free_data_contents(context, &deleg_data); + return ret; + } + + if (upn_blob != NULL) { + ret = krb5_pac_add_buffer(context, *pac, + PAC_TYPE_UPN_DNS_INFO, + &upn_data); + kerberos_free_data_contents(context, &upn_data); + if (ret != 0) { kerberos_free_data_contents(context, &deleg_data); return ret; } @@ -451,17 +530,20 @@ int samba_krbtgt_is_in_db(struct samba_kdc_entry *p, NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx, struct samba_kdc_entry *p, DATA_BLOB **_logon_info_blob, - DATA_BLOB **_cred_ndr_blob) + DATA_BLOB **_cred_ndr_blob, + DATA_BLOB **_upn_info_blob) { struct auth_user_info_dc *user_info_dc; DATA_BLOB *logon_blob = NULL; DATA_BLOB *cred_blob = NULL; + DATA_BLOB *upn_blob = NULL; NTSTATUS nt_status; *_logon_info_blob = NULL; if (_cred_ndr_blob != NULL) { *_cred_ndr_blob = NULL; } + *_upn_info_blob = NULL; /* The user account may be set not to want the PAC */ if ( ! samba_princ_needs_pac(p)) { @@ -480,6 +562,11 @@ NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx, } } + upn_blob = talloc_zero(mem_ctx, DATA_BLOB); + if (upn_blob == NULL) { + return NT_STATUS_NO_MEMORY; + } + nt_status = authsam_make_user_info_dc(mem_ctx, p->kdc_db_ctx->samdb, lpcfg_netbios_name(p->kdc_db_ctx->lp_ctx), lpcfg_sam_name(p->kdc_db_ctx->lp_ctx), @@ -515,11 +602,21 @@ NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx, } } + nt_status = samba_get_upn_info_pac_blob(upn_blob, + user_info_dc, + upn_blob); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Building PAC UPN INFO failed: %s\n", + nt_errstr(nt_status))); + return nt_status; + } + TALLOC_FREE(user_info_dc); *_logon_info_blob = logon_blob; if (_cred_ndr_blob != NULL) { *_cred_ndr_blob = cred_blob; } + *_upn_info_blob = upn_blob; return NT_STATUS_OK; } @@ -528,14 +625,17 @@ NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx, DATA_BLOB **_logon_info_blob) { NTSTATUS nt_status; + DATA_BLOB *upn_blob = NULL; nt_status = samba_kdc_get_pac_blobs(mem_ctx, p, _logon_info_blob, - NULL); + NULL, /* cred_blob */ + &upn_blob); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } + TALLOC_FREE(upn_blob); return NT_STATUS_OK; } diff --git a/source4/kdc/pac-glue.h b/source4/kdc/pac-glue.h index 22f96c8d425..92a6bc78023 100644 --- a/source4/kdc/pac-glue.h +++ b/source4/kdc/pac-glue.h @@ -30,6 +30,7 @@ krb5_error_code samba_kdc_encrypt_pac_credentials(krb5_context context, krb5_error_code samba_make_krb5_pac(krb5_context context, const DATA_BLOB *logon_blob, const DATA_BLOB *cred_blob, + const DATA_BLOB *upn_blob, const DATA_BLOB *deleg_blob, krb5_pac *pac); @@ -42,7 +43,8 @@ int samba_krbtgt_is_in_db(struct samba_kdc_entry *skdc_entry, NTSTATUS samba_kdc_get_pac_blobs(TALLOC_CTX *mem_ctx, struct samba_kdc_entry *skdc_entry, DATA_BLOB **_logon_info_blob, - DATA_BLOB **_cred_ndr_blob); + DATA_BLOB **_cred_ndr_blob, + DATA_BLOB **_upn_info_blob); NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx, struct samba_kdc_entry *skdc_entry, DATA_BLOB **_logon_info_blob); diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c index 3c0a012fb80..fddf342787f 100644 --- a/source4/kdc/wdc-samba4.c +++ b/source4/kdc/wdc-samba4.c @@ -42,6 +42,7 @@ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context, DATA_BLOB **cred_ndr_ptr = NULL; DATA_BLOB _cred_blob = data_blob_null; DATA_BLOB *cred_blob = NULL; + DATA_BLOB *upn_blob = NULL; krb5_error_code ret; NTSTATUS nt_status; struct samba_kdc_entry *skdc_entry = @@ -59,7 +60,8 @@ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context, nt_status = samba_kdc_get_pac_blobs(mem_ctx, skdc_entry, &logon_blob, - cred_ndr_ptr); + cred_ndr_ptr, + &upn_blob); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return EINVAL; @@ -79,7 +81,7 @@ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context, } ret = samba_make_krb5_pac(context, logon_blob, cred_blob, - NULL, pac); + upn_blob, NULL, pac); talloc_free(mem_ctx); return ret; @@ -111,6 +113,7 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, TALLOC_CTX *mem_ctx = talloc_named(p, 0, "samba_kdc_reget_pac context"); krb5_pac new_pac = NULL; DATA_BLOB *pac_blob = NULL; + DATA_BLOB *upn_blob = NULL; DATA_BLOB *deleg_blob = NULL; krb5_error_code ret; NTSTATUS nt_status; @@ -124,6 +127,7 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, ssize_t logon_info_idx = -1; ssize_t delegation_idx = -1; ssize_t logon_name_idx = -1; + ssize_t upn_dns_info_idx = -1; ssize_t srv_checksum_idx = -1; ssize_t kdc_checksum_idx = -1; @@ -156,7 +160,8 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, client_skdc_entry = talloc_get_type_abort(client->ctx, struct samba_kdc_entry); - nt_status = samba_kdc_get_pac_blob(mem_ctx, client_skdc_entry, &pac_blob); + nt_status = samba_kdc_get_pac_blobs(mem_ctx, client_skdc_entry, + &pac_blob, NULL, &upn_blob); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return EINVAL; @@ -266,6 +271,18 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, } logon_name_idx = i; break; + case PAC_TYPE_UPN_DNS_INFO: + if (upn_dns_info_idx != -1) { + DEBUG(1, ("logon type[%d] twice [%d] and [%d]: \n", + (int)types[i], + (int)logon_info_idx, + (int)i)); + SAFE_FREE(types); + talloc_free(mem_ctx); + return EINVAL; + } + upn_dns_info_idx = i; + break; case PAC_TYPE_SRV_CHECKSUM: if (srv_checksum_idx != -1) { DEBUG(1, ("logon type[%d] twice [%d] and [%d]: \n", @@ -377,6 +394,20 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, * we just add a place holder here. */ type_blob = data_blob_const(&zero_byte, 1); + + if (upn_dns_info_idx == -1 && upn_blob != NULL) { + /* inject UPN_DNS_INFO behind */ + forced_next_type = PAC_TYPE_UPN_DNS_INFO; + } + break; + case PAC_TYPE_UPN_DNS_INFO: + /* + * Replace in the RODC case, otherwise + * upn_blob is NULL and we just copy. + */ + if (upn_blob != NULL) { + type_blob = *upn_blob; + } break; case PAC_TYPE_SRV_CHECKSUM: /* diff --git a/source4/torture/rpc/remote_pac.c b/source4/torture/rpc/remote_pac.c index dabc37ac2a1..25a581bb46b 100644 --- a/source4/torture/rpc/remote_pac.c +++ b/source4/torture/rpc/remote_pac.c @@ -133,7 +133,7 @@ static bool test_PACVerify(struct torture_context *tctx, { NTSTATUS status; bool pkinit_in_use = torture_setting_bool(tctx, "pkinit_in_use", false); - bool expect_pac_upn_dns_info = torture_setting_bool(tctx, "expect_pac_upn_dns_info", false); + bool expect_pac_upn_dns_info = torture_setting_bool(tctx, "expect_pac_upn_dns_info", true); size_t num_pac_buffers; struct netr_LogonSamLogon r; -- 2.11.4.GIT