From d4a292afb659ac6a78a9455593215e861201037a Mon Sep 17 00:00:00 2001 From: root Date: Sun, 16 Aug 2015 21:36:21 +0200 Subject: [PATCH 4/4] Implement actual PAC_CREDENTIAL_INFO generation and attachment. Implements the actual PAC_CREDENTIAL_INFO generation and attachment for the PAC glue functions that take BLOBs retrieved from the auth_user_info_dc structure and formats them accordingly for wire transfer. --- source4/kdc/mit_samba.c | 22 ++++- source4/kdc/mit_samba_interface.h | 3 +- source4/kdc/pac-glue.c | 179 +++++++++++++++++++++++++++++++++++--- source4/kdc/pac-glue.h | 10 ++- source4/kdc/wdc-samba4.c | 12 +-- 5 files changed, 203 insertions(+), 23 deletions(-) diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c index f56e679..3f045ce 100644 --- a/source4/kdc/mit_samba.c +++ b/source4/kdc/mit_samba.c @@ -188,12 +188,14 @@ static int mit_samba_get_nextkey(struct mit_samba_context *ctx, return ret; } -static int mit_samba_get_pac_data(struct mit_samba_context *ctx, +static int mit_samba_get_pac_cred_data(struct mit_samba_context *ctx, hdb_entry_ex *client, - DATA_BLOB *data) + DATA_BLOB *data, + DATA_BLOB *cred) { TALLOC_CTX *tmp_ctx; DATA_BLOB *pac_blob; + DATA_BLOB *cred_blob; NTSTATUS nt_status; tmp_ctx = talloc_named(ctx, 0, "mit_samba_get_pac_data context"); @@ -201,7 +203,8 @@ static int mit_samba_get_pac_data(struct mit_samba_context *ctx, return ENOMEM; } - nt_status = samba_kdc_get_pac_blob(tmp_ctx, client, &pac_blob); + nt_status = samba_kdc_get_pac_cred_blob(tmp_ctx, client, &pac_blob, + &cred_blob); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return EINVAL; @@ -214,6 +217,17 @@ static int mit_samba_get_pac_data(struct mit_samba_context *ctx, } memcpy(data->data, pac_blob->data, pac_blob->length); data->length = pac_blob->length; + + if (cred) { + cred->data = (uint8_t *)malloc(cred_blob->length); + if (!cred->data) { + free(cred->data); + talloc_free(tmp_ctx); + return ENOMEM; + } + memcpy(cred->data, cred_blob->data, cred_blob->length); + cred->length = cred_blob->length; + } talloc_free(tmp_ctx); return 0; @@ -358,7 +372,7 @@ struct mit_samba_function_table mit_samba_function_table = { mit_samba_get_principal, mit_samba_get_firstkey, mit_samba_get_nextkey, - mit_samba_get_pac_data, + mit_samba_get_pac_cred_data, mit_samba_update_pac_data, mit_samba_check_client_access, mit_samba_check_s4u2proxy diff --git a/source4/kdc/mit_samba_interface.h b/source4/kdc/mit_samba_interface.h index b92f7bf..2a4e489 100644 --- a/source4/kdc/mit_samba_interface.h +++ b/source4/kdc/mit_samba_interface.h @@ -48,7 +48,8 @@ struct mit_samba_function_table { int (*get_nextkey)(struct mit_samba_context *, hdb_entry_ex **); /* windc */ - int (*get_pac)(struct mit_samba_context *, hdb_entry_ex *, DATA_BLOB *); + int (*get_pac_cred)(struct mit_samba_context *, hdb_entry_ex *, DATA_BLOB *, + DATA_BLOB *); int (*update_pac)(struct mit_samba_context *, hdb_entry_ex *, DATA_BLOB *, DATA_BLOB *); int (*client_access)(struct mit_samba_context *, diff --git a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c index cca74d8..d5a510b 100644 --- a/source4/kdc/pac-glue.c +++ b/source4/kdc/pac-glue.c @@ -35,12 +35,15 @@ #include "auth/kerberos/pac_utils.h" static -NTSTATUS samba_get_logon_info_pac_blob(TALLOC_CTX *mem_ctx, +NTSTATUS samba_get_logon_info_pac_cred_blob(TALLOC_CTX *mem_ctx, struct auth_user_info_dc *info, - DATA_BLOB *pac_data) + DATA_BLOB *pac_data, + DATA_BLOB *cred_data) { struct netr_SamInfo3 *info3; union PAC_INFO pac_info; + struct PAC_CREDENTIAL_DATA pac_cred_data; + DATA_BLOB ntlm_secpkg; enum ndr_err_code ndr_err; NTSTATUS nt_status; @@ -69,20 +72,79 @@ NTSTATUS samba_get_logon_info_pac_blob(TALLOC_CTX *mem_ctx, nt_errstr(nt_status))); return nt_status; } + + /* Only fill in credential data PAC buffer template when requested and + * passwords are actually available. + * This is required for PKINIT login to the domain so that the client + * can create the appropriate passwords for NTLM authentication without + * knowing them. */ + if (cred_data && info->passwords->flags) { + NDR_PRINT_DEBUG(PAC_CREDENTIAL_NTLM_SECPKG, info->passwords); + + ndr_err = ndr_push_struct_blob(&ntlm_secpkg, mem_ctx, info->passwords, + (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_NTLM_SECPKG); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + nt_status = ndr_map_error2ntstatus(ndr_err); + DEBUG(1, ("PAC (presig ntlm cred) push failed: %s\n", + nt_errstr(nt_status))); + return nt_status; + } + + ZERO_STRUCT(pac_cred_data); + + pac_cred_data.count = 1; + pac_cred_data.credentials = talloc_zero(mem_ctx, struct PAC_CREDENTIAL_SECPKG); + if (!pac_cred_data.credentials) { + return NT_STATUS_NO_MEMORY; + } + + pac_cred_data.credentials->pkgname.string = talloc_strdup(mem_ctx, "NTLM"); + pac_cred_data.credentials->size = ntlm_secpkg.length; + pac_cred_data.credentials->data = ntlm_secpkg.data; + + NDR_PRINT_DEBUG(PAC_CREDENTIAL_DATA, &pac_cred_data); + + ndr_err = ndr_push_struct_blob(cred_data, mem_ctx, &pac_cred_data, + (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_DATA); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + nt_status = ndr_map_error2ntstatus(ndr_err); + DEBUG(1, ("PAC (presig cred pkg) push failed: %s\n", + nt_errstr(nt_status))); + return nt_status; + } + + DEBUG(2, ("Created credential BLOB (len %u) for user\n", + cred_data->length)); + } else if (cred_data) { + DEBUG(2, ("Requested credentials, but none available to send\n")); + *cred_data = data_blob(NULL, 0); + } return NT_STATUS_OK; } krb5_error_code samba_make_krb5_pac(krb5_context context, + TALLOC_CTX *mem_ctx, DATA_BLOB *pac_blob, + DATA_BLOB *cred_blob, DATA_BLOB *deleg_blob, - krb5_pac *pac) + krb5_pac *pac, + const krb5_keyblock *pkreplykey) { krb5_data pac_data; + krb5_crypto cred_crypto; + krb5_enctype cred_enctype; + krb5_data cred_crypt_data; + struct PAC_CREDENTIAL_INFO pac_cred_info; + DATA_BLOB cred_info_blob; + krb5_data cred_data; krb5_data deleg_data; krb5_error_code ret; + const char *krb5err; + enum ndr_err_code ndr_err; + NTSTATUS nt_status; - /* The user account may be set not to want the PAC */ + /* The user account may be set not to want the PAC */ if (!pac_blob) { return 0; } @@ -91,6 +153,73 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, if (ret != 0) { return ret; } + + /* Only process credentials blob in case we have been passed a pkreplykey + * which allows the credentials blob to be encrypted. This does not need + * a data copy, as we encrypt the buffer anyway. */ + ZERO_STRUCT(cred_data); + if (cred_blob && cred_blob->length && pkreplykey) { + ret = krb5_crypto_init(context, pkreplykey, ETYPE_NULL, &cred_crypto); + if (ret != 0) { + krb5err = krb5_get_error_message(context, ret); + DEBUG(1, ("Failed initializing cred data crypto: %s\n", krb5err)); + krb5_free_error_message(context, krb5err); + krb5_data_free(&pac_data); + return ret; + } + + ret = krb5_crypto_getenctype(context, cred_crypto, &cred_enctype); + if (ret != 0) { + DEBUG(1, ("Failed getting crypto type for key\n")); + krb5_data_free(&pac_data); + krb5_crypto_destroy(context, cred_crypto); + return ret; + } + + ret = krb5_encrypt(context, cred_crypto, KRB5_KU_OTHER_ENCRYPTED, + cred_blob->data, cred_blob->length, &cred_crypt_data); + krb5_crypto_destroy(context, cred_crypto); + if (ret != 0) { + krb5err = krb5_get_error_message(context, ret); + DEBUG(1, ("Failed crypt of cred data: %s\n", krb5err)); + krb5_free_error_message(context, krb5err); + krb5_data_free(&pac_data); + return ret; + } + + ZERO_STRUCT(pac_cred_info); + + pac_cred_info.enctype = cred_enctype; + pac_cred_info.data.length = cred_crypt_data.length; + pac_cred_info.data.data = (uint8_t *)cred_crypt_data.data; + + NDR_PRINT_DEBUG(PAC_CREDENTIAL_INFO, &pac_cred_info); + + ndr_err = ndr_push_struct_blob(&cred_info_blob, mem_ctx, &pac_cred_info, + (ndr_push_flags_fn_t)ndr_push_PAC_CREDENTIAL_INFO); + krb5_data_free(&cred_crypt_data); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + nt_status = ndr_map_error2ntstatus(ndr_err); + DEBUG(1, ("PAC (wrapped cred info) push failed: %s\n", + nt_errstr(nt_status))); + krb5_data_free(&pac_data); + return KRB5KDC_ERR_SVC_UNAVAILABLE; + } + + DEBUG(2, ("Encrypted credential BLOB (len %u) with alg %d\n", + cred_info_blob.length, pac_cred_info.enctype)); + + ret = krb5_data_copy(&cred_data, + cred_info_blob.data, + cred_info_blob.length); + if (ret != 0) { + krb5_data_free(&pac_data); + return ret; + } + } else if (cred_blob && cred_blob->length) + DEBUG(2, ("Have credentials BLOB but no reply key, not adding it\n")); + else if (pkreplykey) + DEBUG(1, ("Have PKINIT reply key but no credentials blob to add\n")); ZERO_STRUCT(deleg_data); if (deleg_blob) { @@ -99,6 +228,7 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, deleg_blob->length); if (ret != 0) { krb5_data_free(&pac_data); + krb5_data_free(&cred_data); return ret; } } @@ -106,6 +236,7 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, ret = krb5_pac_init(context, pac); if (ret != 0) { krb5_data_free(&pac_data); + krb5_data_free(&cred_data); krb5_data_free(&deleg_data); return ret; } @@ -113,9 +244,21 @@ krb5_error_code samba_make_krb5_pac(krb5_context context, ret = krb5_pac_add_buffer(context, *pac, PAC_TYPE_LOGON_INFO, &pac_data); krb5_data_free(&pac_data); if (ret != 0) { + krb5_data_free(&cred_data); krb5_data_free(&deleg_data); return ret; } + + if (cred_blob && cred_blob->length && pkreplykey) { + ret = krb5_pac_add_buffer(context, *pac, + PAC_TYPE_CREDENTIAL_INFO, + &cred_data); + krb5_data_free(&cred_data); + if (ret != 0) { + krb5_data_free(&deleg_data); + return ret; + } + } if (deleg_blob) { ret = krb5_pac_add_buffer(context, *pac, @@ -216,18 +359,23 @@ int samba_krbtgt_is_in_db(struct hdb_entry_ex *princ, bool *is_in_db, bool *is_u return 0; } -NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx, +NTSTATUS samba_kdc_get_pac_cred_blob(TALLOC_CTX *mem_ctx, struct hdb_entry_ex *client, - DATA_BLOB **_pac_blob) + DATA_BLOB **_pac_blob, + DATA_BLOB **_cred_blob) { struct samba_kdc_entry *p = talloc_get_type(client->ctx, struct samba_kdc_entry); struct auth_user_info_dc *user_info_dc; DATA_BLOB *pac_blob; + DATA_BLOB *cred_blob = NULL; NTSTATUS nt_status; /* The user account may be set not to want the PAC */ if ( ! samba_princ_needs_pac(client)) { *_pac_blob = NULL; + if (_cred_blob) { + *_cred_blob = NULL; + } return NT_STATUS_OK; } @@ -235,6 +383,13 @@ NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx, if (!pac_blob) { return NT_STATUS_NO_MEMORY; } + + if (_cred_blob) { + cred_blob = talloc_zero(mem_ctx, DATA_BLOB); + if (!cred_blob) { + 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), @@ -250,14 +405,18 @@ NTSTATUS samba_kdc_get_pac_blob(TALLOC_CTX *mem_ctx, return nt_status; } - nt_status = samba_get_logon_info_pac_blob(mem_ctx, user_info_dc, pac_blob); + nt_status = samba_get_logon_info_pac_cred_blob(mem_ctx, user_info_dc, + pac_blob, cred_blob); if (!NT_STATUS_IS_OK(nt_status)) { - DEBUG(0, ("Building PAC failed: %s\n", + DEBUG(0, ("Building PAC blob(s) failed: %s\n", nt_errstr(nt_status))); return nt_status; } *_pac_blob = pac_blob; + if (_cred_blob) { + *_cred_blob = cred_blob; + } return NT_STATUS_OK; } @@ -277,8 +436,8 @@ NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx, return NT_STATUS_UNSUCCESSFUL; } - nt_status = samba_get_logon_info_pac_blob(mem_ctx, - user_info_dc, pac_blob); + nt_status = samba_get_logon_info_pac_cred_blob(mem_ctx, + user_info_dc, pac_blob, NULL); return nt_status; } diff --git a/source4/kdc/pac-glue.h b/source4/kdc/pac-glue.h index 0e1cdcd..e320bae 100644 --- a/source4/kdc/pac-glue.h +++ b/source4/kdc/pac-glue.h @@ -22,17 +22,21 @@ */ krb5_error_code samba_make_krb5_pac(krb5_context context, + TALLOC_CTX *mem_ctx, DATA_BLOB *pac_blob, + DATA_BLOB *cred_blob, DATA_BLOB *deleg_blob, - krb5_pac *pac); + krb5_pac *pac, + const krb5_keyblock *pkreplykey); bool samba_princ_needs_pac(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, +NTSTATUS samba_kdc_get_pac_cred_blob(TALLOC_CTX *mem_ctx, struct hdb_entry_ex *client, - DATA_BLOB **_pac_blob); + DATA_BLOB **_pac_blob, + DATA_BLOB **_cred_blob); NTSTATUS samba_kdc_update_pac_blob(TALLOC_CTX *mem_ctx, krb5_context context, diff --git a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c index 929ee38..a93545e 100644 --- a/source4/kdc/wdc-samba4.c +++ b/source4/kdc/wdc-samba4.c @@ -30,12 +30,12 @@ * key to encrypt data in the PAC. */ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context, struct hdb_entry_ex *client, - krb5_pac *pac) krb5_pac *pac, const krb5_keyblock *pkreplykey) { TALLOC_CTX *mem_ctx; DATA_BLOB *pac_blob; + DATA_BLOB *cred_blob = NULL; krb5_error_code ret; NTSTATUS nt_status; @@ -44,13 +44,14 @@ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context, return ENOMEM; } - nt_status = samba_kdc_get_pac_blob(mem_ctx, client, &pac_blob); + nt_status = samba_kdc_get_pac_cred_blob(mem_ctx, client, &pac_blob, &cred_blob); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return EINVAL; } - ret = samba_make_krb5_pac(context, pac_blob, NULL, pac); + ret = samba_make_krb5_pac(context, mem_ctx, pac_blob, + cred_blob, NULL, pac, pkreplykey); talloc_free(mem_ctx); return ret; @@ -99,7 +100,7 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, if (client == NULL) { return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; } - nt_status = samba_kdc_get_pac_blob(mem_ctx, client, &pac_blob); + nt_status = samba_kdc_get_pac_cred_blob(mem_ctx, client, &pac_blob, NULL); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); return EINVAL; @@ -167,7 +168,8 @@ static krb5_error_code samba_wdc_reget_pac(void *priv, krb5_context context, /* We now completely regenerate this pac */ krb5_pac_free(context, *pac); - ret = samba_make_krb5_pac(context, pac_blob, deleg_blob, pac); + ret = samba_make_krb5_pac(context, mem_ctx, pac_blob, + NULL, deleg_blob, pac, NULL); talloc_free(mem_ctx); return ret; -- 2.1.4