From a6c25d6eaf737fd9be46e49e8cca781aa6f11dc6 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 17:39:51 +0200 Subject: [PATCH 01/32] s4-backupkey: Ensure RSA modulus is 2048 bits RSA_generate_key_ex doesn't always generate a modulus of requested bit length. Tests with Windows 7 clients showed that they decline x509 certificates (MS-BKRP 2.2.1) in cases where the modulus length is smaller than the specified 2048 bits. For the user this resulted in DPAPI failing to retrieve stored credentials after the user password has been changed at least two times. On the server side log.samba showed that the client also called the as yet unlimplemented ServerWrap sub- protocol function BACKUPKEY_BACKUP_KEY_GUID after it had called the ClientWarp function BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID. After enabling DPAPI auditing on the Windows Clients the Event Viewer showed Event-ID 4692 failing with a FailureReason value of 0x7a in these cases. Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=10980 (cherry picked from commit 9b2ff26c893e5748d12d7a37a93eef7b1f4b1a1b) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 9020da7..7daa500 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -759,6 +759,7 @@ static WERROR create_heimdal_rsa_key(TALLOC_CTX *ctx, hx509_context *hctx, uint8_t *p0, *p; size_t len; int bits = 2048; + int RSA_returned_bits; *_rsa = NULL; @@ -776,11 +777,15 @@ static WERROR create_heimdal_rsa_key(TALLOC_CTX *ctx, hx509_context *hctx, return WERR_INTERNAL_ERROR; } - ret = RSA_generate_key_ex(rsa, bits, pub_expo, NULL); - if(ret != 1) { - RSA_free(rsa); - BN_free(pub_expo); - return WERR_INTERNAL_ERROR; + while (RSA_returned_bits != bits) { + ret = RSA_generate_key_ex(rsa, bits, pub_expo, NULL); + if(ret != 1) { + RSA_free(rsa); + BN_free(pub_expo); + return WERR_INTERNAL_ERROR; + } + RSA_returned_bits = BN_num_bits(rsa->n); + DEBUG(6, ("RSA_generate_key_ex returned %d Bits\n", RSA_returned_bits)); } BN_free(pub_expo); -- 2.1.4 From 752aa5ca29299b8c192796813b82e7b8bca6e1ae Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 17:59:29 +0200 Subject: [PATCH 02/32] s4-backupkey: Cert lifetime of 365 days, not secs hx509_ca_tbs_set_notAfter_lifetime expects the lifetime value in in seconds. The Windows 7 client didn't seem to care that the lifetime was only 6'03''. Two other TODOs in this implementation: * Since notBefore is not set explicietely to "now", the heimdal code default of now-(24 hours) is applied. * Server side validity checks and cert renewal are missing. Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 89803009b957b980818aa971a0f5dd14f75cbbe1) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 7daa500..5abfa87 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -994,7 +994,7 @@ static WERROR generate_bkrp_cert(TALLOC_CTX *ctx, struct dcesrv_call_state *dce_ char *secret_name; struct bkrp_exported_RSA_key_pair keypair; enum ndr_err_code ndr_err; - uint32_t nb_days_validity = 365; + uint32_t nb_days_validity = 3600 * 24 * 365; DEBUG(6, ("Trying to generate a certificate\n")); hx509_context_init(&hctx); -- 2.1.4 From 5a15cf703f7b34a12d07e4128933dbc53725ad2d Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:12:47 +0200 Subject: [PATCH 03/32] s4-backupkey: check for talloc failure Check for talloc_memdup failure for uniqueid.data. Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit d633fcb5666085fef290adbe05161a2f36329abf) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 5abfa87..4d75b02 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -836,6 +836,9 @@ static WERROR self_sign_cert(TALLOC_CTX *ctx, hx509_context *hctx, hx509_request int ret; uniqueid.data = talloc_memdup(ctx, guidblob->data, guidblob->length); + if (uniqueid.data == NULL) { + return WERR_NOMEM; + } /* uniqueid is a bit string in which each byte represent 1 bit (1 or 0) * so as 1 byte is 8 bits we need to provision 8 times more space as in the * blob -- 2.1.4 From f710a284bb19868b44509ab3c47ee047b44b2295 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:15:37 +0200 Subject: [PATCH 04/32] s4-backupkey: de-duplicate error handling Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 525c93caa6c264de7c0cb463d005d3dcda7e45af) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 59 +++++++++---------------- 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 4d75b02..5db7685 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -849,77 +849,58 @@ static WERROR self_sign_cert(TALLOC_CTX *ctx, hx509_context *hctx, hx509_request ret = hx509_request_get_name(*hctx, *req, &subject); if (ret !=0) { - talloc_free(uniqueid.data); - return WERR_INTERNAL_ERROR; + goto fail_subject; } ret = hx509_request_get_SubjectPublicKeyInfo(*hctx, *req, &spki); if (ret !=0) { - talloc_free(uniqueid.data); - hx509_name_free(&subject); - return WERR_INTERNAL_ERROR; + goto fail_spki; } ret = hx509_ca_tbs_init(*hctx, &tbs); if (ret !=0) { - talloc_free(uniqueid.data); - hx509_name_free(&subject); - free_SubjectPublicKeyInfo(&spki); - return WERR_INTERNAL_ERROR; + goto fail_tbs; } ret = hx509_ca_tbs_set_spki(*hctx, tbs, &spki); if (ret !=0) { - talloc_free(uniqueid.data); - hx509_name_free(&subject); - free_SubjectPublicKeyInfo(&spki); - hx509_ca_tbs_free(&tbs); - return WERR_INTERNAL_ERROR; + goto fail; } ret = hx509_ca_tbs_set_subject(*hctx, tbs, subject); if (ret !=0) { - talloc_free(uniqueid.data); - hx509_name_free(&subject); - free_SubjectPublicKeyInfo(&spki); - hx509_ca_tbs_free(&tbs); - return WERR_INTERNAL_ERROR; + goto fail; } ret = hx509_ca_tbs_set_ca(*hctx, tbs, 1); if (ret !=0) { - talloc_free(uniqueid.data); - hx509_name_free(&subject); - free_SubjectPublicKeyInfo(&spki); - hx509_ca_tbs_free(&tbs); - return WERR_INTERNAL_ERROR; + goto fail; } ret = hx509_ca_tbs_set_notAfter_lifetime(*hctx, tbs, lifetime); if (ret !=0) { - talloc_free(uniqueid.data); - hx509_name_free(&subject); - free_SubjectPublicKeyInfo(&spki); - hx509_ca_tbs_free(&tbs); - return WERR_INTERNAL_ERROR; + goto fail; } ret = hx509_ca_tbs_set_unique(*hctx, tbs, &uniqueid, &uniqueid); if (ret !=0) { - talloc_free(uniqueid.data); - hx509_name_free(&subject); - free_SubjectPublicKeyInfo(&spki); - hx509_ca_tbs_free(&tbs); - return WERR_INTERNAL_ERROR; + goto fail; } ret = hx509_ca_sign_self(*hctx, tbs, *private_key, cert); if (ret !=0) { - talloc_free(uniqueid.data); - hx509_name_free(&subject); - free_SubjectPublicKeyInfo(&spki); - hx509_ca_tbs_free(&tbs); - return WERR_INTERNAL_ERROR; + goto fail; } hx509_name_free(&subject); free_SubjectPublicKeyInfo(&spki); hx509_ca_tbs_free(&tbs); return WERR_OK; + +fail: + hx509_ca_tbs_free(&tbs); +fail_tbs: + free_SubjectPublicKeyInfo(&spki); +fail_spki: + hx509_name_free(&subject); +fail_subject: + talloc_free(uniqueid.data); + talloc_free(serialnumber.data); + return WERR_INTERNAL_ERROR; } static WERROR create_req(TALLOC_CTX *ctx, hx509_context *hctx, hx509_request *req, -- 2.1.4 From c8fc38f31c4f955dd620f4e9abbf3b944ddcb31b Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:18:30 +0200 Subject: [PATCH 05/32] s4-backupkey: Set defined cert serialnumber [MS-BKRP] 2.2.1 specifies that the serialnumber of the certificate should be set identical to the subjectUniqueID. In fact certificates generated by native AD have this field encoded in little-endian format. See also https://www.mail-archive.com/cifs-protocol@cifs.org/msg01364.html Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 577fa69b5287b047ee2564786e19c9941a25734c) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 5db7685..f748cd1 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -833,7 +833,8 @@ static WERROR self_sign_cert(TALLOC_CTX *ctx, hx509_context *hctx, hx509_request hx509_name subject = NULL; hx509_ca_tbs tbs; struct heim_bit_string uniqueid; - int ret; + struct heim_integer serialnumber; + int ret, i; uniqueid.data = talloc_memdup(ctx, guidblob->data, guidblob->length); if (uniqueid.data == NULL) { @@ -845,6 +846,22 @@ static WERROR self_sign_cert(TALLOC_CTX *ctx, hx509_context *hctx, hx509_request */ uniqueid.length = 8 * guidblob->length; + serialnumber.data = talloc_array(ctx, uint8_t, + guidblob->length); + if (serialnumber.data == NULL) { + talloc_free(uniqueid.data); + return WERR_NOMEM; + } + + /* Native AD generates certificates with serialnumber in reversed notation */ + for (i = 0; i < guidblob->length; i++) { + uint8_t *reversed = (uint8_t *)serialnumber.data; + uint8_t *uncrypt = guidblob->data; + reversed[i] = uncrypt[guidblob->length - 1 - i]; + } + serialnumber.length = guidblob->length; + serialnumber.negative = 0; + memset(&spki, 0, sizeof(spki)); ret = hx509_request_get_name(*hctx, *req, &subject); @@ -881,6 +898,10 @@ static WERROR self_sign_cert(TALLOC_CTX *ctx, hx509_context *hctx, hx509_request if (ret !=0) { goto fail; } + ret = hx509_ca_tbs_set_serialnumber(*hctx, tbs, &serialnumber); + if (ret !=0) { + goto fail; + } ret = hx509_ca_sign_self(*hctx, tbs, *private_key, cert); if (ret !=0) { goto fail; -- 2.1.4 From 864db30a2636756efaf2212057c076170e354cba Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:25:29 +0200 Subject: [PATCH 06/32] s4-backupkey: Comply with [MS-BKRP] 2.2.1 [MS-BKRP] 2.2.1 specifies "The Common Name field of the Subject name field SHOULD contain the name of the DNS domain assigned to the server." In fact Windows 7 clients don't seem to care. Also in certificates generated by native AD the domain name (after CN=) is encoded as UTF-16LE. Since hx509_parse_name only supports UTF-8 strings currently we just leave the encoding as it is for now. Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 007c3978a46d5f50051605752a76d12f30c5a0de) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index f748cd1..07af1c0 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -1184,8 +1184,7 @@ static WERROR bkrp_do_retreive_client_wrap_key(struct dcesrv_call_state *dce_cal if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { /* Ok we can be in this case if there was no certs */ struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; - char *dn = talloc_asprintf(mem_ctx, "CN=%s.%s", - lpcfg_netbios_name(lp_ctx), + char *dn = talloc_asprintf(mem_ctx, "CN=%s", lpcfg_realm(lp_ctx)); WERROR werr = generate_bkrp_cert(mem_ctx, dce_call, ldb_ctx, dn); -- 2.1.4 From 6536ad50cd7c53e6c358fa6d5e186d53b6eac3dd Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:36:49 +0200 Subject: [PATCH 07/32] s4-backupkey: Initialize ndr->switchlist for print ndr_print_bkrp_data_in_blob requires the level to be set in the proper ndr->switch_list context. Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 6af3cf60e31fdaa0873f45fd821165e265335c55) --- librpc/ndr/ndr_backupkey.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/librpc/ndr/ndr_backupkey.c b/librpc/ndr/ndr_backupkey.c index ddbaeea..827bc69 100644 --- a/librpc/ndr/ndr_backupkey.c +++ b/librpc/ndr/ndr_backupkey.c @@ -71,6 +71,11 @@ _PUBLIC_ void ndr_print_bkrp_BackupKey(struct ndr_print *ndr, const char *name, ndr->depth--; level = backupkeyguid_to_uint(r->in.guidActionAgent); + ndr_err = ndr_print_set_switch_value(ndr, &inblob, level); + if (unlikely(!NDR_ERR_CODE_IS_SUCCESS(ndr_err))) { \ + DEBUG(0,("ERROR: ndr_print_bkrp_BackupKey ndr_print_set_switch_value failed: %d\n", ndr_err)); + return; + } blob.data = r->in.data_in; blob.length = r->in.data_in_len; ndr_err = ndr_pull_union_blob(&blob, ndr, &inblob, level, -- 2.1.4 From 78bf430462375a996268a7cef3ed430a27f2fbcf Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:43:05 +0200 Subject: [PATCH 08/32] s4-backupkey: fix ndr_pull error on empty input [MS-BKRP] 3.1.4.1 specifies for BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID that the server must ignore the input data. This patch fixes ndr_pull_error(11): Pull bytes 4 (../librpc/ndr/ndr_basic.c:148) Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 3bc3bec6d702ef62bf026ff64855edc8fb900088) --- librpc/idl/backupkey.idl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/librpc/idl/backupkey.idl b/librpc/idl/backupkey.idl index e21030b..b504ca5 100644 --- a/librpc/idl/backupkey.idl +++ b/librpc/idl/backupkey.idl @@ -47,6 +47,9 @@ interface backupkey uint8 key[256]; } bkrp_dc_serverwrap_key; + [public] typedef struct { + } bkrp_empty; + [public,gensize] typedef struct { uint32 version; uint32 encrypted_secret_len; @@ -103,7 +106,7 @@ interface backupkey [public] typedef [nodiscriminant] union { [case(BACKUPKEY_RESTORE_GUID_INTEGER)] bkrp_client_side_wrapped restore_req; - [case(BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID_INTEGER)] bkrp_client_side_wrapped cert_req; + [case(BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID_INTEGER)] bkrp_empty empty; } bkrp_data_in_blob; /******************/ -- 2.1.4 From ac438cc789e7f89f09c591f58b7fabb50df8ab50 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:48:41 +0200 Subject: [PATCH 09/32] s4-backupkey: IDL for ServerWrap subprotocol This adds some IDL structs for the ServerWrap subprotocol, allowing parsing of the incoming RPC calls and returning WERR_NOT_SUPPORTED instead of WERR_INVALID_PARAM. Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 879b65710b266fecaca01b9dd40474b2cc35d417) --- librpc/idl/backupkey.idl | 26 ++++++++++++++++++++++++- source4/rpc_server/backupkey/dcesrv_backupkey.c | 12 ++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/librpc/idl/backupkey.idl b/librpc/idl/backupkey.idl index b504ca5..18098cd 100644 --- a/librpc/idl/backupkey.idl +++ b/librpc/idl/backupkey.idl @@ -98,15 +98,39 @@ interface backupkey uint8 hash[64]; } bkrp_access_check_v3; + [public] typedef struct { + [subcontext(0),subcontext_size(32),flag(NDR_REMAINING)] DATA_BLOB r3; + [subcontext(0),subcontext_size(20),flag(NDR_REMAINING)] DATA_BLOB mac; + dom_sid sid; + [subcontext(0),flag(NDR_REMAINING)] DATA_BLOB secret; + } bkrp_rc4encryptedpayload; + + [public] typedef struct { + [value(0x00000001)] uint32 magic; + uint32 payload_length; + uint32 cyphertext_length; + [subcontext(0),subcontext_size(16),flag(NDR_REMAINING)] DATA_BLOB guid_of_wrapping_key; + [subcontext(0),subcontext_size(68),flag(NDR_REMAINING)] DATA_BLOB r2; + [subcontext(0),flag(NDR_REMAINING)] DATA_BLOB rc4encryptedpayload; + } bkrp_server_side_wrapped; + + [public] typedef struct { + [flag(NDR_REMAINING)] DATA_BLOB opaque; + } bkrp_opaque_blob; + typedef enum { BACKUPKEY_INVALID_GUID_INTEGER = 0xFFFF, BACKUPKEY_RESTORE_GUID_INTEGER = 0x0000, - BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID_INTEGER = 0x0001 + BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID_INTEGER = 0x0001, + BACKUPKEY_RESTORE_GUID_WIN2K_INTEGER = 0x0002, + BACKUPKEY_BACKUP_GUID_INTEGER = 0x0003 } bkrp_guid_to_integer; [public] typedef [nodiscriminant] union { [case(BACKUPKEY_RESTORE_GUID_INTEGER)] bkrp_client_side_wrapped restore_req; [case(BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID_INTEGER)] bkrp_empty empty; + [case(BACKUPKEY_RESTORE_GUID_WIN2K_INTEGER)] bkrp_server_side_wrapped unsign_req; + [case(BACKUPKEY_BACKUP_GUID_INTEGER)] bkrp_opaque_blob sign_req; } bkrp_data_in_blob; /******************/ diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 07af1c0..9dc7951 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -1308,6 +1308,18 @@ static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call, DEBUG(debuglevel, ("Client %s requested certificate for client wrapped secret\n", addr)); error = bkrp_do_retreive_client_wrap_key(dce_call, mem_ctx, r, ldb_ctx); } + + if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), + BACKUPKEY_RESTORE_GUID_WIN2K, strlen(BACKUPKEY_RESTORE_GUID_WIN2K)) == 0) { + DEBUG(debuglevel, ("Client %s requested to decrypt a server side wrapped secret, not implemented yet\n", addr)); + return WERR_NOT_SUPPORTED; /* is this appropriate? */ + } + + if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), + BACKUPKEY_BACKUP_GUID, strlen(BACKUPKEY_BACKUP_GUID)) == 0) { + DEBUG(debuglevel, ("Client %s requested a server wrapped secret, not implemented yet\n", addr)); + return WERR_NOT_SUPPORTED; /* is this appropriate? */ + } } /*else: I am a RODC so I don't handle backup key protocol */ -- 2.1.4 From 60d3fe2cac8b8de7086162acfff32f029a44b7d4 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:56:39 +0200 Subject: [PATCH 10/32] s4-backupkey: typo fix Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 8473f6da6902d753ed46073e453a496aa90cb94b) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 9dc7951..f08eb83 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -1161,7 +1161,7 @@ static WERROR generate_bkrp_cert(TALLOC_CTX *ctx, struct dcesrv_call_state *dce_ return WERR_OK; } -static WERROR bkrp_do_retreive_client_wrap_key(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, +static WERROR bkrp_do_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx) { struct GUID guid; @@ -1306,7 +1306,7 @@ static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call, if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID, strlen(BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID)) == 0) { DEBUG(debuglevel, ("Client %s requested certificate for client wrapped secret\n", addr)); - error = bkrp_do_retreive_client_wrap_key(dce_call, mem_ctx, r, ldb_ctx); + error = bkrp_do_retrieve_client_wrap_key(dce_call, mem_ctx, r, ldb_ctx); } if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), -- 2.1.4 From a059810225da5cd14ba1bd77a272fc42e8720e91 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Tue, 8 Jul 2014 16:12:13 +0200 Subject: [PATCH 11/32] s4-backupkey: improve variable name Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit e25c61c5f17230a6932f704ed849f140b00a45aa) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index f08eb83..4e4beab 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -999,7 +999,7 @@ static WERROR generate_bkrp_cert(TALLOC_CTX *ctx, struct dcesrv_call_state *dce_ char *secret_name; struct bkrp_exported_RSA_key_pair keypair; enum ndr_err_code ndr_err; - uint32_t nb_days_validity = 3600 * 24 * 365; + uint32_t nb_seconds_validity = 3600 * 24 * 365; DEBUG(6, ("Trying to generate a certificate\n")); hx509_context_init(&hctx); @@ -1017,7 +1017,7 @@ static WERROR generate_bkrp_cert(TALLOC_CTX *ctx, struct dcesrv_call_state *dce_ return WERR_INVALID_DATA; } - w_err = self_sign_cert(ctx, &hctx, &req, nb_days_validity, &pk, &cert, &blob); + w_err = self_sign_cert(ctx, &hctx, &req, nb_seconds_validity, &pk, &cert, &blob); if (!W_ERROR_IS_OK(w_err)) { hx509_private_key_free(&pk); hx509_context_free(&hctx); -- 2.1.4 From 4a449c576e6004d7a287d5e0cd2b733a6a93ee08 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Tue, 8 Jul 2014 17:25:53 +0200 Subject: [PATCH 12/32] s4-backupkey: consistent naming of werr variable Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit e6e9e490ae1352b0d572dbd3d546c14d367cbedb) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 4e4beab..fb55875 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -931,11 +931,11 @@ static WERROR create_req(TALLOC_CTX *ctx, hx509_context *hctx, hx509_request *re SubjectPublicKeyInfo key; hx509_name name; - WERROR w_err; + WERROR werr; - w_err = create_heimdal_rsa_key(ctx, hctx, signer, rsa); - if (!W_ERROR_IS_OK(w_err)) { - return w_err; + werr = create_heimdal_rsa_key(ctx, hctx, signer, rsa); + if (!W_ERROR_IS_OK(werr)) { + return werr; } hx509_request_init(*hctx, req); @@ -983,7 +983,7 @@ static WERROR create_req(TALLOC_CTX *ctx, hx509_context *hctx, hx509_request *re static WERROR generate_bkrp_cert(TALLOC_CTX *ctx, struct dcesrv_call_state *dce_call, struct ldb_context *ldb_ctx, const char *dn) { heim_octet_string data; - WERROR w_err; + WERROR werr; RSA *rsa; hx509_context hctx; hx509_private_key pk; @@ -1003,10 +1003,10 @@ static WERROR generate_bkrp_cert(TALLOC_CTX *ctx, struct dcesrv_call_state *dce_ DEBUG(6, ("Trying to generate a certificate\n")); hx509_context_init(&hctx); - w_err = create_req(ctx, &hctx, &req, &pk, &rsa, dn); - if (!W_ERROR_IS_OK(w_err)) { + werr = create_req(ctx, &hctx, &req, &pk, &rsa, dn); + if (!W_ERROR_IS_OK(werr)) { hx509_context_free(&hctx); - return w_err; + return werr; } status = GUID_to_ndr_blob(&guid, ctx, &blob); @@ -1017,8 +1017,8 @@ static WERROR generate_bkrp_cert(TALLOC_CTX *ctx, struct dcesrv_call_state *dce_ return WERR_INVALID_DATA; } - w_err = self_sign_cert(ctx, &hctx, &req, nb_seconds_validity, &pk, &cert, &blob); - if (!W_ERROR_IS_OK(w_err)) { + werr = self_sign_cert(ctx, &hctx, &req, nb_seconds_validity, &pk, &cert, &blob); + if (!W_ERROR_IS_OK(werr)) { hx509_private_key_free(&pk); hx509_context_free(&hctx); return WERR_INVALID_DATA; -- 2.1.4 From 51551f4576be255f5ead3b04425326b516c6ebf5 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Tue, 23 Dec 2014 18:56:20 +0100 Subject: [PATCH 13/32] s4:torture/rpc/backupkey: Require 2048 bit RSA key Signed-off-by: Arvid Requate Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam (fixed cleanup of memory) Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 16ad6de6b8d4481b1e00630c9a23895d1371d971) --- source4/torture/rpc/backupkey.c | 75 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/source4/torture/rpc/backupkey.c b/source4/torture/rpc/backupkey.c index 75f756c..f74cded 100644 --- a/source4/torture/rpc/backupkey.c +++ b/source4/torture/rpc/backupkey.c @@ -30,6 +30,7 @@ #include #include #include +#include /* Our very special and valued secret */ @@ -1034,6 +1035,77 @@ static bool test_RestoreGUID_badhashaccesscheck(struct torture_context *tctx, return true; } +/* + * Check that the RSA modulus in the certificate of the DCs has 2048 bits. + */ +static bool test_RetreiveBackupKeyGUID_2048bits(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct dcerpc_binding_handle *b = p->binding_handle; + DATA_BLOB out_blob; + struct bkrp_BackupKey *r = createRetreiveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + + hx509_context hctx; + int hret; + hx509_cert cert; + SubjectPublicKeyInfo spki; + RSA *rsa; + int RSA_returned_bits; + + hx509_context_init(&hctx); + + if (r == NULL) { + return false; + } + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + const unsigned char *spki_spk_data; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, r), + "Get GUID"); + + out_blob.length = *r->out.data_out_len; + + hret = hx509_cert_init_data(hctx, out_blob.data, out_blob.length, &cert); + torture_assert_int_equal(tctx, hret, 0, "hx509_cert_init_data failed"); + + hret = hx509_cert_get_SPKI(hctx, cert , &spki); + torture_assert_int_equal(tctx, hret, 0, "hx509_cert_get_SPKI failed"); + + /* We must take a copy, as d2i_RSAPublicKey *changes* the input parameter */ + spki_spk_data = spki.subjectPublicKey.data; + rsa = d2i_RSAPublicKey(NULL, &spki_spk_data, spki.subjectPublicKey.length / 8); + torture_assert_int_equal(tctx, rsa != NULL, 1, "d2i_RSAPublicKey failed"); + + RSA_returned_bits = BN_num_bits(rsa->n); + torture_assert_int_equal(tctx, + RSA_returned_bits, + 2048, + "RSA Key doesn't have 2048 bits"); + + RSA_free(rsa); + + /* + * Because we prevented spki from being changed above, + * we can now safely call this to free it + */ + free_SubjectPublicKeyInfo(&spki); + hx509_cert_free(cert); + hx509_context_free(&hctx); + + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, r), + NT_STATUS_ACCESS_DENIED, + "Get GUID"); + } + return true; +} + struct torture_suite *torture_rpc_backupkey(TALLOC_CTX *mem_ctx) { struct torture_rpc_tcase *tcase; @@ -1080,5 +1152,8 @@ struct torture_suite *torture_rpc_backupkey(TALLOC_CTX *mem_ctx) torture_rpc_tcase_add_test(tcase, "empty_request_restore_guid", test_RestoreGUID_emptyrequest); + torture_rpc_tcase_add_test(tcase, "retreive_backup_key_guid_2048_bits", + test_RetreiveBackupKeyGUID_2048bits); + return suite; } -- 2.1.4 From c0d77b9edbff7d3618f16a47caf0cc79fde39896 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Feb 2015 09:51:27 +1300 Subject: [PATCH 14/32] torture-backupkey: Add consistent assertions that createRestoreGUIDStruct() suceeds Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit d9529dbab6f0482d408bf9c4ea9bd911da8897e5) --- source4/torture/rpc/backupkey.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/source4/torture/rpc/backupkey.c b/source4/torture/rpc/backupkey.c index f74cded..7f5676a 100644 --- a/source4/torture/rpc/backupkey.c +++ b/source4/torture/rpc/backupkey.c @@ -767,6 +767,7 @@ static bool test_RestoreGUID_ko(struct torture_context *tctx, if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, true, false, false, false, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); @@ -795,6 +796,7 @@ static bool test_RestoreGUID_wrongversion(struct torture_context *tctx, if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, false, true, false, false, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); @@ -823,6 +825,7 @@ static bool test_RestoreGUID_wronguser(struct torture_context *tctx, if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, false, false, true, false, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); @@ -851,6 +854,7 @@ static bool test_RestoreGUID_v3(struct torture_context *tctx, if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob, false, false, false, false, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); @@ -880,6 +884,7 @@ static bool test_RestoreGUID(struct torture_context *tctx, if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, false, false, false, false, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); @@ -909,6 +914,7 @@ static bool test_RestoreGUID_badmagiconsecret(struct torture_context *tctx, if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob, false, false, false, true, false, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); @@ -936,7 +942,7 @@ static bool test_RestoreGUID_emptyrequest(struct torture_context *tctx, struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob, false, false, false, true, false, false, true); - torture_assert_int_equal(tctx, r != NULL, 1, "Error while creating the restoreGUID struct"); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); r->in.data_in = talloc(tctx, uint8_t); r->in.data_in_len = 0; r->in.param = 0; @@ -966,6 +972,7 @@ static bool test_RestoreGUID_badcertguid(struct torture_context *tctx, if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob, false, false, false, false, false, false, true); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct() failed"); torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); @@ -994,6 +1001,7 @@ static bool test_RestoreGUID_badmagicaccesscheck(struct torture_context *tctx, if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, false, false, false, false, true, false, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); @@ -1022,6 +1030,7 @@ static bool test_RestoreGUID_badhashaccesscheck(struct torture_context *tctx, if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob, false, false, false, false, false, true, false); + torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); @@ -1054,6 +1063,8 @@ static bool test_RetreiveBackupKeyGUID_2048bits(struct torture_context *tctx, RSA *rsa; int RSA_returned_bits; + torture_assert(tctx, r != NULL, "createRetreiveBackupKeyGUIDStruct failed"); + hx509_context_init(&hctx); if (r == NULL) { -- 2.1.4 From bd0ee4bd005abf82b7e2a77c49269187d785014e Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Feb 2015 11:45:45 +1300 Subject: [PATCH 15/32] torture-backupkey: Assert dcerpc_bkrp_BackupKey_r call was successful Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 286223f150dbb84022d48ef845119cd47afc30d3) --- source4/torture/rpc/backupkey.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source4/torture/rpc/backupkey.c b/source4/torture/rpc/backupkey.c index 7f5676a..8187643 100644 --- a/source4/torture/rpc/backupkey.c +++ b/source4/torture/rpc/backupkey.c @@ -579,6 +579,9 @@ static struct bkrp_BackupKey *createRestoreGUIDStruct(struct torture_context *tc torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Get GUID"); + torture_assert_werr_ok(tctx, r->out.result, + "Get GUID"); + /* * We have to set it outside of the function createRetreiveBackupKeyGUIDStruct * the len of the blob, this is due to the fact that they don't have the @@ -1079,6 +1082,9 @@ static bool test_RetreiveBackupKeyGUID_2048bits(struct torture_context *tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Get GUID"); + torture_assert_werr_ok(tctx, r->out.result, + "Get GUID"); + out_blob.length = *r->out.data_out_len; hret = hx509_cert_init_data(hctx, out_blob.data, out_blob.length, &cert); -- 2.1.4 From 77160edbcb4ab80d676d97608714704512a2362f Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Thu, 5 Feb 2015 11:07:30 +1300 Subject: [PATCH 16/32] backupkey: begin by factoring out the server wrap functions Signed-off-by: Garming Sam Reviewed-by: Andrew Bartlett (cherry picked from commit a4e6873c4356fa221a0833336413f70e7c9411ca) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index fb55875..1bcb115 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -1224,7 +1224,7 @@ static WERROR bkrp_do_retrieve_client_wrap_key(struct dcesrv_call_state *dce_cal */ return WERR_FILE_NOT_FOUND; } - + cert_secret_name = talloc_asprintf(mem_ctx, "BCKUPKEY_%s", guid_string); @@ -1259,6 +1259,18 @@ static WERROR bkrp_do_retrieve_client_wrap_key(struct dcesrv_call_state *dce_cal return WERR_NOT_SUPPORTED; } +static WERROR bkrp_do_uncrypt_server_wrap_key(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, + struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx) +{ + return WERR_NOT_SUPPORTED; +} + +static WERROR bkrp_do_retrieve_server_wrap_key(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, + struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx) +{ + return WERR_NOT_SUPPORTED; +} + static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct bkrp_BackupKey *r) { @@ -1312,13 +1324,13 @@ static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call, if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), BACKUPKEY_RESTORE_GUID_WIN2K, strlen(BACKUPKEY_RESTORE_GUID_WIN2K)) == 0) { DEBUG(debuglevel, ("Client %s requested to decrypt a server side wrapped secret, not implemented yet\n", addr)); - return WERR_NOT_SUPPORTED; /* is this appropriate? */ + error = bkrp_do_uncrypt_server_wrap_key(dce_call, mem_ctx, r, ldb_ctx); } if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), BACKUPKEY_BACKUP_GUID, strlen(BACKUPKEY_BACKUP_GUID)) == 0) { DEBUG(debuglevel, ("Client %s requested a server wrapped secret, not implemented yet\n", addr)); - return WERR_NOT_SUPPORTED; /* is this appropriate? */ + error = bkrp_do_retrieve_server_wrap_key(dce_call, mem_ctx, r, ldb_ctx); } } /*else: I am a RODC so I don't handle backup key protocol */ -- 2.1.4 From 54f3e979536f8c3fefb391ebbf78adf42f088477 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Thu, 5 Feb 2015 18:17:58 +1300 Subject: [PATCH 17/32] backupkey: Improve IDL Signed-off-by: Garming Sam Reviewed-by: Andrew Bartlett (cherry picked from commit bc0b90a300654a248a08e4796133bb6b880e9789) --- librpc/idl/backupkey.idl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/librpc/idl/backupkey.idl b/librpc/idl/backupkey.idl index 18098cd..76c0eb7 100644 --- a/librpc/idl/backupkey.idl +++ b/librpc/idl/backupkey.idl @@ -99,19 +99,19 @@ interface backupkey } bkrp_access_check_v3; [public] typedef struct { - [subcontext(0),subcontext_size(32),flag(NDR_REMAINING)] DATA_BLOB r3; - [subcontext(0),subcontext_size(20),flag(NDR_REMAINING)] DATA_BLOB mac; + uint8 r3[32]; + uint8 mac[20]; dom_sid sid; - [subcontext(0),flag(NDR_REMAINING)] DATA_BLOB secret; + [subcontext(0),flag(NDR_REMAINING)] DATA_BLOB secret_data; } bkrp_rc4encryptedpayload; [public] typedef struct { [value(0x00000001)] uint32 magic; uint32 payload_length; - uint32 cyphertext_length; - [subcontext(0),subcontext_size(16),flag(NDR_REMAINING)] DATA_BLOB guid_of_wrapping_key; - [subcontext(0),subcontext_size(68),flag(NDR_REMAINING)] DATA_BLOB r2; - [subcontext(0),flag(NDR_REMAINING)] DATA_BLOB rc4encryptedpayload; + uint32 ciphertext_length; + GUID guid; + uint8 r2[68]; + uint8 rc4encryptedpayload[ciphertext_length]; } bkrp_server_side_wrapped; [public] typedef struct { -- 2.1.4 From 20db2534aad50c6bc32efc0093f4d9f816069153 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 15:48:06 +1300 Subject: [PATCH 18/32] backupkey: Move SID comparison to inside get_and_verify_access_check() Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit f69b180cf86ad2c43dbbd89c7e906e7ab2350ee2) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 55 ++++++++++++------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 1bcb115..e3310c9 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -380,7 +380,7 @@ static WERROR get_and_verify_access_check(TALLOC_CTX *sub_ctx, uint8_t *key_and_iv, uint8_t *access_check, uint32_t access_check_len, - struct dom_sid **access_sid) + struct auth_session_info *session_info) { heim_octet_string iv; heim_octet_string access_check_os; @@ -393,10 +393,12 @@ static WERROR get_and_verify_access_check(TALLOC_CTX *sub_ctx, enum ndr_err_code ndr_err; hx509_context hctx; + struct dom_sid *access_sid = NULL; + struct dom_sid *caller_sid = NULL; + /* This one should not be freed */ const AlgorithmIdentifier *alg; - *access_sid = NULL; switch (version) { case 2: key_len = 24; @@ -451,7 +453,9 @@ static WERROR get_and_verify_access_check(TALLOC_CTX *sub_ctx, hx509_crypto_destroy(crypto); - if (version == 2) { + switch (version) { + case 2: + { uint32_t hash_size = 20; uint8_t hash[hash_size]; struct sha sctx; @@ -483,14 +487,11 @@ static WERROR get_and_verify_access_check(TALLOC_CTX *sub_ctx, DEBUG(2, ("Wrong hash value in the access check in backup key remote protocol\n")); return WERR_INVALID_DATA; } - *access_sid = dom_sid_dup(sub_ctx, &(uncrypted_accesscheckv2.sid)); - if (*access_sid == NULL) { - return WERR_NOMEM; - } - return WERR_OK; + access_sid = &(uncrypted_accesscheckv2.sid); + break; } - - if (version == 3) { + case 3: + { uint32_t hash_size = 64; uint8_t hash[hash_size]; struct hc_sha512state sctx; @@ -522,15 +523,20 @@ static WERROR get_and_verify_access_check(TALLOC_CTX *sub_ctx, DEBUG(2, ("Wrong hash value in the access check in backup key remote protocol\n")); return WERR_INVALID_DATA; } - *access_sid = dom_sid_dup(sub_ctx, &(uncrypted_accesscheckv3.sid)); - if (*access_sid == NULL) { - return WERR_NOMEM; - } - return WERR_OK; + access_sid = &(uncrypted_accesscheckv3.sid); + break; } - - /* Never reached normally as we filtered at the switch / case level */ - return WERR_INVALID_DATA; + default: + /* Never reached normally as we filtered at the switch / case level */ + return WERR_INVALID_DATA; + } + + caller_sid = &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; + + if (!dom_sid_equal(caller_sid, access_sid)) { + return WERR_INVALID_ACCESS; + } + return WERR_OK; } static WERROR bkrp_do_uncrypt_client_wrap_key(struct dcesrv_call_state *dce_call, @@ -599,11 +605,9 @@ static WERROR bkrp_do_uncrypt_client_wrap_key(struct dcesrv_call_state *dce_call struct bkrp_exported_RSA_key_pair keypair; hx509_private_key pk; uint32_t i, res; - struct dom_sid *access_sid = NULL; heim_octet_string reversed_secret; heim_octet_string uncrypted_secret; AlgorithmIdentifier alg; - struct dom_sid *caller_sid; DATA_BLOB blob_us; WERROR werr; @@ -669,7 +673,7 @@ static WERROR bkrp_do_uncrypt_client_wrap_key(struct dcesrv_call_state *dce_call uncrypted_secretv2.payload_key, uncrypt_request.access_check, uncrypt_request.access_check_len, - &access_sid); + dce_call->conn->auth_state.session_info); if (!W_ERROR_IS_OK(werr)) { return werr; } @@ -704,7 +708,7 @@ static WERROR bkrp_do_uncrypt_client_wrap_key(struct dcesrv_call_state *dce_call uncrypted_secretv3.payload_key, uncrypt_request.access_check, uncrypt_request.access_check_len, - &access_sid); + dce_call->conn->auth_state.session_info); if (!W_ERROR_IS_OK(werr)) { return werr; } @@ -718,13 +722,6 @@ static WERROR bkrp_do_uncrypt_client_wrap_key(struct dcesrv_call_state *dce_call uncrypted->length = uncrypted_secretv3.secret_len; } - caller_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; - - if (!dom_sid_equal(caller_sid, access_sid)) { - talloc_free(uncrypted); - return WERR_INVALID_ACCESS; - } - /* * Yeah if we are here all looks pretty good: * - hash is ok -- 2.1.4 From 7039b2ea66460b559c0d07136bab3005ad65624d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 15:50:15 +1300 Subject: [PATCH 19/32] backupkey: Improve function names and comments for clarity Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit c55f3936490a89004364a203361d201bee5fce08) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 42 ++++++++++++++++++++----- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index e3310c9..a6484cd 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -539,10 +539,27 @@ static WERROR get_and_verify_access_check(TALLOC_CTX *sub_ctx, return WERR_OK; } -static WERROR bkrp_do_uncrypt_client_wrap_key(struct dcesrv_call_state *dce_call, - TALLOC_CTX *mem_ctx, - struct bkrp_BackupKey *r, - struct ldb_context *ldb_ctx) +/* + * We have some data, such as saved website or IMAP passwords that the + * client has in profile on-disk. This needs to be decrypted. This + * version gives the server the data over the network (protected by + * the X.509 certificate and public key encryption, and asks that it + * be decrypted returned for short-term use, protected only by the + * negotiated transport encryption. + * + * The data is NOT stored in the LSA, but a X.509 certificate, public + * and private keys used to encrypt the data will be stored. There is + * only one active encryption key pair and certificate per domain, it + * is pointed at with G$BCKUPKEY_PREFERRED in the LSA secrets store. + * + * The potentially multiple valid decrypting key pairs are in turn + * stored in the LSA secrets store as G$BCKUPKEY_keyGuidString. + * + */ +static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, + TALLOC_CTX *mem_ctx, + struct bkrp_BackupKey *r, + struct ldb_context *ldb_ctx) { struct bkrp_client_side_wrapped uncrypt_request; DATA_BLOB blob; @@ -704,6 +721,15 @@ static WERROR bkrp_do_uncrypt_client_wrap_key(struct dcesrv_call_state *dce_call return WERR_INVALID_DATA; } + /* + * Confirm that the caller is permitted to + * read this particular data. Because one key + * pair is used per domain, the caller could + * have stolen the profile data on-disk and + * would otherwise be able to read the + * passwords. + */ + werr = get_and_verify_access_check(mem_ctx, 3, uncrypted_secretv3.payload_key, uncrypt_request.access_check, @@ -1158,8 +1184,8 @@ static WERROR generate_bkrp_cert(TALLOC_CTX *ctx, struct dcesrv_call_state *dce_ return WERR_OK; } -static WERROR bkrp_do_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx) +static WERROR bkrp_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, + struct bkrp_BackupKey *r, struct ldb_context *ldb_ctx) { struct GUID guid; char *guid_string; @@ -1309,13 +1335,13 @@ static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call, if(strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), BACKUPKEY_RESTORE_GUID, strlen(BACKUPKEY_RESTORE_GUID)) == 0) { DEBUG(debuglevel, ("Client %s requested to decrypt a client side wrapped secret\n", addr)); - error = bkrp_do_uncrypt_client_wrap_key(dce_call, mem_ctx, r, ldb_ctx); + error = bkrp_client_wrap_decrypt_data(dce_call, mem_ctx, r, ldb_ctx); } if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID, strlen(BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID)) == 0) { DEBUG(debuglevel, ("Client %s requested certificate for client wrapped secret\n", addr)); - error = bkrp_do_retrieve_client_wrap_key(dce_call, mem_ctx, r, ldb_ctx); + error = bkrp_retrieve_client_wrap_key(dce_call, mem_ctx, r, ldb_ctx); } if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), -- 2.1.4 From 3811c2a1693ec4190fc229aedfb06062d70d149d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 16:02:00 +1300 Subject: [PATCH 20/32] backupkey: Implement ServerWrap Encrypt protocol BUG: https://bugzilla.samba.org/attachment.cgi?bugid=11097 Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 33c616406726a3e144b5b4bbc2c8d3166e0c4be5) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 312 +++++++++++++++++++++++- 1 file changed, 299 insertions(+), 13 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index a6484cd..e7fb3d4 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -34,9 +34,13 @@ #include #include #include +#include +#include #include #include "../lib/tsocket/tsocket.h" #include "../libcli/security/security.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "lib/crypto/arcfour.h" #define BACKUPKEY_MIN_VERSION 2 #define BACKUPKEY_MAX_VERSION 3 @@ -1271,29 +1275,311 @@ static WERROR bkrp_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, W_ERROR_HAVE_NO_MEMORY(*(r->out.data_out)); return WERR_OK; } else { - DEBUG(10, ("No or broken secret called %s\n", cert_secret_name)); - return WERR_FILE_NOT_FOUND; + DEBUG(1, ("No or broken secret called %s\n", cert_secret_name)); + return WERR_INTERNAL_ERROR; } - } else { - DEBUG(10, ("No secret BCKUPKEY_PREFERRED\n")); - return WERR_FILE_NOT_FOUND; } return WERR_NOT_SUPPORTED; } -static WERROR bkrp_do_uncrypt_server_wrap_key(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx) +static WERROR generate_bkrp_server_wrap_key(TALLOC_CTX *ctx, struct ldb_context *ldb_ctx) { - return WERR_NOT_SUPPORTED; + struct GUID guid = GUID_random(); + enum ndr_err_code ndr_err; + DATA_BLOB blob_wrap_key, guid_blob; + struct bkrp_dc_serverwrap_key wrap_key; + NTSTATUS status; + char *secret_name; + TALLOC_CTX *frame = talloc_stackframe(); + + generate_random_buffer(wrap_key.key, sizeof(wrap_key.key)); + + ndr_err = ndr_push_struct_blob(&blob_wrap_key, ctx, &wrap_key, (ndr_push_flags_fn_t)ndr_push_bkrp_dc_serverwrap_key); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + TALLOC_FREE(frame); + return WERR_INVALID_DATA; + } + + secret_name = talloc_asprintf(frame, "BCKUPKEY_%s", GUID_string(ctx, &guid)); + if (secret_name == NULL) { + TALLOC_FREE(frame); + return WERR_NOMEM; + } + + status = set_lsa_secret(frame, ldb_ctx, secret_name, &blob_wrap_key); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(2, ("Failed to save the secret %s\n", secret_name)); + TALLOC_FREE(frame); + return WERR_INTERNAL_ERROR; + } + + status = GUID_to_ndr_blob(&guid, frame, &guid_blob); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(2, ("Failed to save the secret %s\n", secret_name)); + TALLOC_FREE(frame); + } + + status = set_lsa_secret(frame, ldb_ctx, "BCKUPKEY_P", &guid_blob); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(2, ("Failed to save the secret %s\n", secret_name)); + TALLOC_FREE(frame); + return WERR_INTERNAL_ERROR; + } + + TALLOC_FREE(frame); + + return WERR_OK; } -static WERROR bkrp_do_retrieve_server_wrap_key(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx) +static WERROR bkrp_server_wrap_decrypt_data(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, + struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx) { + struct bkrp_server_side_wrapped uncrypt_request; + DATA_BLOB blob; + enum ndr_err_code ndr_err; + + blob.data = r->in.data_in; + blob.length = r->in.data_in_len; + + if (r->in.data_in_len == 0 || r->in.data_in == NULL) { + return WERR_INVALID_PARAM; + } + + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &uncrypt_request, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_server_side_wrapped); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INVALID_PARAM; + } + return WERR_NOT_SUPPORTED; } +/* + * Find the current, preferred ServerWrap Key by looking at + * G$BCKUPKEY_P in the LSA secrets store. + * + * Then find the current decryption keys from the LSA secrets store as + * G$BCKUPKEY_keyGuidString. + */ + +static WERROR bkrp_do_retrieve_server_wrap_key(TALLOC_CTX *mem_ctx, struct ldb_context *ldb_ctx, + struct bkrp_dc_serverwrap_key *server_key, + struct GUID *guid) +{ + NTSTATUS status; + DATA_BLOB guid_binary, lsa_secret; + char *secret_name; + char *guid_string; + enum ndr_err_code ndr_err; + + status = get_lsa_secret(mem_ctx, ldb_ctx, "BCKUPKEY_P", &guid_binary); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Error while fetching secret BCKUPKEY_P to find current GUID\n")); + return WERR_FILE_NOT_FOUND; + } else if (guid_binary.length == 0) { + /* RODC case, we do not have secrets locally */ + DEBUG(1, ("Unable to fetch value for secret BCKUPKEY_P, are we an undetected RODC?\n")); + return WERR_INTERNAL_ERROR; + } + + status = GUID_from_ndr_blob(&guid_binary, guid); + if (!NT_STATUS_IS_OK(status)) { + return WERR_FILE_NOT_FOUND; + } + + guid_string = GUID_string(mem_ctx, guid); + if (guid_string == NULL) { + /* We return file not found because the client + * expect this error + */ + return WERR_FILE_NOT_FOUND; + } + + secret_name = talloc_asprintf(mem_ctx, "BCKUPKEY_%s", guid_string); + if (secret_name == NULL) { + return WERR_NOMEM; + } + + status = get_lsa_secret(mem_ctx, ldb_ctx, secret_name, &lsa_secret); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Error while fetching secret %s\n", secret_name)); + return WERR_FILE_NOT_FOUND; + } else if (guid_binary.length == 0) { + /* RODC case, we do not have secrets locally */ + DEBUG(1, ("Unable to fetch value for secret %s, are we an undetected RODC?\n", + secret_name)); + return WERR_INTERNAL_ERROR; + } + ndr_err = ndr_pull_struct_blob(&lsa_secret, mem_ctx, server_key, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_dc_serverwrap_key); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(2, ("Unable to parse the ndr encoded server wrap key %s\n", secret_name)); + return WERR_FILE_NOT_FOUND; + } + + return WERR_OK; +} + +/* + * We have some data, such as saved website or IMAP passwords that the + * client would like to put into the profile on-disk. This needs to + * be encrypted. This version gives the server the data over the + * network (protected only by the negotiated transport encryption), + * and asks that it be encrypted and returned for long-term storage. + * + * The data is NOT stored in the LSA, but a key to encrypt the data + * will be stored. There is only one active encryption key per domain, + * it is pointed at with G$BCKUPKEY_P in the LSA secrets store. + * + * The potentially multiple valid decryptiong keys (and the encryption + * key) are in turn stored in the LSA secrets store as + * G$BCKUPKEY_keyGuidString. + * + */ + +static WERROR bkrp_server_wrap_encrypt_data(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, + struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx) +{ + DATA_BLOB sid_blob, encrypted_blob, symkey_blob, server_wrapped_blob; + WERROR werr; + struct dom_sid *caller_sid; + uint8_t symkey[20]; /* SHA-1 hash len */ + uint8_t mackey[20]; /* SHA-1 hash len */ + unsigned int hash_len; + struct bkrp_rc4encryptedpayload rc4payload; + HMAC_CTX ctx; + struct bkrp_dc_serverwrap_key server_key; + enum ndr_err_code ndr_err; + struct bkrp_server_side_wrapped server_side_wrapped; + struct GUID guid; + + if (r->in.data_in_len == 0 || r->in.data_in == NULL) { + return WERR_INVALID_PARAM; + } + + werr = bkrp_do_retrieve_server_wrap_key(mem_ctx, + ldb_ctx, &server_key, + &guid); + + if (!W_ERROR_IS_OK(werr)) { + if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) { + /* Generate the server wrap key since one wasn't found */ + werr = generate_bkrp_server_wrap_key(mem_ctx, + ldb_ctx); + if (!W_ERROR_IS_OK(werr)) { + return WERR_INVALID_PARAMETER; + } + werr = bkrp_do_retrieve_server_wrap_key(mem_ctx, + ldb_ctx, &server_key, &guid); + + if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) { + /* Ok we really don't manage to get this secret ...*/ + return WERR_FILE_NOT_FOUND; + } + } else { + /* In theory we should NEVER reach this point as it + should only appear in a rodc server */ + /* we do not have the real secret attribute */ + return WERR_INVALID_PARAMETER; + } + } + + caller_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; + + dump_data_pw("server_key: \n", server_key.key, sizeof(server_key.key)); + + /* + * This is the key derivation step, so that the HMAC and RC4 + * operations over the user-supplied data are not able to + * disclose the master key. By using random data, the symkey + * and mackey values are unique for this operation, and + * discovering these (by reversing the RC4 over the + * attacker-controlled data) does not return something able to + * be used to decyrpt the encrypted data of other users + */ + generate_random_buffer(server_side_wrapped.r2, sizeof(server_side_wrapped.r2)); + + dump_data_pw("r2: \n", server_side_wrapped.r2, sizeof(server_side_wrapped.r2)); + + generate_random_buffer(rc4payload.r3, sizeof(rc4payload.r3)); + + dump_data_pw("r3: \n", rc4payload.r3, sizeof(rc4payload.r3)); + + + /* + * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1 + * BACKUPKEY_BACKUP_GUID, it really is the whole key + */ + HMAC(EVP_sha1(), server_key.key, sizeof(server_key.key), + server_side_wrapped.r2, sizeof(server_side_wrapped.r2), + symkey, &hash_len); + + dump_data_pw("symkey: \n", symkey, hash_len); + + /* + * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1 + * BACKUPKEY_BACKUP_GUID, it really is the whole key + */ + HMAC(EVP_sha1(), server_key.key, sizeof(server_key.key), + rc4payload.r3, sizeof(rc4payload.r3), + mackey, &hash_len); + + dump_data_pw("mackey: \n", mackey, sizeof(mackey)); + + ndr_err = ndr_push_struct_blob(&sid_blob, mem_ctx, caller_sid, + (ndr_push_flags_fn_t)ndr_push_dom_sid); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INTERNAL_ERROR; + } + + rc4payload.secret_data.data = r->in.data_in; + rc4payload.secret_data.length = r->in.data_in_len; + + + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, mackey, 20, EVP_sha1(), NULL); + /* SID field */ + HMAC_Update(&ctx, sid_blob.data, sid_blob.length); + /* Secret field */ + HMAC_Update(&ctx, rc4payload.secret_data.data, rc4payload.secret_data.length); + HMAC_Final(&ctx, rc4payload.mac, &hash_len); + HMAC_CTX_cleanup(&ctx); + + dump_data_pw("rc4payload.mac: \n", rc4payload.mac, sizeof(rc4payload.mac)); + + rc4payload.sid = *caller_sid; + + ndr_err = ndr_push_struct_blob(&encrypted_blob, mem_ctx, &rc4payload, + (ndr_push_flags_fn_t)ndr_push_bkrp_rc4encryptedpayload); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INTERNAL_ERROR; + } + + /* rc4 encrypt sid and secret using sym key */ + symkey_blob = data_blob_const(symkey, sizeof(symkey)); + arcfour_crypt_blob(encrypted_blob.data, encrypted_blob.length, &symkey_blob); + + /* create server wrap structure */ + + server_side_wrapped.payload_length = rc4payload.secret_data.length; + server_side_wrapped.ciphertext_length = encrypted_blob.length; + server_side_wrapped.guid = guid; + server_side_wrapped.rc4encryptedpayload = encrypted_blob.data; + + ndr_err = ndr_push_struct_blob(&server_wrapped_blob, mem_ctx, &server_side_wrapped, + (ndr_push_flags_fn_t)ndr_push_bkrp_server_side_wrapped); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INTERNAL_ERROR; + } + + + *(r->out.data_out) = server_wrapped_blob.data; + *(r->out.data_out_len) = server_wrapped_blob.length; + + return WERR_OK; +} + static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct bkrp_BackupKey *r) { @@ -1347,13 +1633,13 @@ static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call, if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), BACKUPKEY_RESTORE_GUID_WIN2K, strlen(BACKUPKEY_RESTORE_GUID_WIN2K)) == 0) { DEBUG(debuglevel, ("Client %s requested to decrypt a server side wrapped secret, not implemented yet\n", addr)); - error = bkrp_do_uncrypt_server_wrap_key(dce_call, mem_ctx, r, ldb_ctx); + error = bkrp_server_wrap_decrypt_data(dce_call, mem_ctx, r, ldb_ctx); } if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), BACKUPKEY_BACKUP_GUID, strlen(BACKUPKEY_BACKUP_GUID)) == 0) { - DEBUG(debuglevel, ("Client %s requested a server wrapped secret, not implemented yet\n", addr)); - error = bkrp_do_retrieve_server_wrap_key(dce_call, mem_ctx, r, ldb_ctx); + DEBUG(debuglevel, ("Client %s requested a server wrapped secret\n", addr)); + error = bkrp_server_wrap_encrypt_data(dce_call, mem_ctx, r, ldb_ctx); } } /*else: I am a RODC so I don't handle backup key protocol */ -- 2.1.4 From d24514082257d9ca4e7c6922b28cb2a081cde591 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 16:16:20 +1300 Subject: [PATCH 21/32] backupkey: Use the name lsa_secret rather than just secret This makes it clear that this is the data stored on the LSA secrets store and not the client-provided data to be encrypted. Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 0ff9733479f27cf40a9cc0f749de088d33591272) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index e7fb3d4..8c8e036 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -54,7 +54,7 @@ static const AlgorithmIdentifier _hx509_signature_rsa_with_var_num = { static NTSTATUS set_lsa_secret(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const char *name, - const DATA_BLOB *secret) + const DATA_BLOB *lsa_secret) { struct ldb_message *msg; struct ldb_result *res; @@ -141,8 +141,8 @@ static NTSTATUS set_lsa_secret(TALLOC_CTX *mem_ctx, talloc_free(msg); return NT_STATUS_NO_MEMORY; } - val.data = secret->data; - val.length = secret->length; + val.data = lsa_secret->data; + val.length = lsa_secret->length; ret = ldb_msg_add_value(msg, "currentValue", &val, NULL); if (ret != LDB_SUCCESS) { talloc_free(msg); @@ -176,7 +176,7 @@ static NTSTATUS set_lsa_secret(TALLOC_CTX *mem_ctx, static NTSTATUS get_lsa_secret(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const char *name, - DATA_BLOB *secret) + DATA_BLOB *lsa_secret) { TALLOC_CTX *tmp_mem; struct ldb_result *res; @@ -190,8 +190,8 @@ static NTSTATUS get_lsa_secret(TALLOC_CTX *mem_ctx, }; int ret; - secret->data = NULL; - secret->length = 0; + lsa_secret->data = NULL; + lsa_secret->length = 0; domain_dn = ldb_get_default_basedn(ldb); if (!domain_dn) { @@ -241,8 +241,8 @@ static NTSTATUS get_lsa_secret(TALLOC_CTX *mem_ctx, } data = val->data; - secret->data = talloc_move(mem_ctx, &data); - secret->length = val->length; + lsa_secret->data = talloc_move(mem_ctx, &data); + lsa_secret->length = val->length; talloc_free(tmp_mem); return NT_STATUS_OK; @@ -570,7 +570,7 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, enum ndr_err_code ndr_err; char *guid_string; char *cert_secret_name; - DATA_BLOB secret; + DATA_BLOB lsa_secret; DATA_BLOB *uncrypted; NTSTATUS status; @@ -610,7 +610,7 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, status = get_lsa_secret(mem_ctx, ldb_ctx, cert_secret_name, - &secret); + &lsa_secret); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("Error while fetching secret %s\n", cert_secret_name)); if (NT_STATUS_EQUAL(status,NT_STATUS_OBJECT_NAME_NOT_FOUND)) { @@ -621,7 +621,7 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, } } - if (secret.length != 0) { + if (lsa_secret.length != 0) { hx509_context hctx; struct bkrp_exported_RSA_key_pair keypair; hx509_private_key pk; @@ -632,7 +632,7 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, DATA_BLOB blob_us; WERROR werr; - ndr_err = ndr_pull_struct_blob(&secret, mem_ctx, &keypair, (ndr_pull_flags_fn_t)ndr_pull_bkrp_exported_RSA_key_pair); + ndr_err = ndr_pull_struct_blob(&lsa_secret, mem_ctx, &keypair, (ndr_pull_flags_fn_t)ndr_pull_bkrp_exported_RSA_key_pair); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(2, ("Unable to parse the ndr encoded cert in key %s\n", cert_secret_name)); return WERR_FILE_NOT_FOUND; @@ -1193,7 +1193,7 @@ static WERROR bkrp_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, { struct GUID guid; char *guid_string; - DATA_BLOB secret; + DATA_BLOB lsa_secret; enum ndr_err_code ndr_err; NTSTATUS status; @@ -1205,7 +1205,7 @@ static WERROR bkrp_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, status = get_lsa_secret(mem_ctx, ldb_ctx, "BCKUPKEY_PREFERRED", - &secret); + &lsa_secret); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("Error while fetching secret BCKUPKEY_PREFERRED\n")); if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { @@ -1221,7 +1221,7 @@ static WERROR bkrp_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, status = get_lsa_secret(mem_ctx, ldb_ctx, "BCKUPKEY_PREFERRED", - &secret); + &lsa_secret); if (!NT_STATUS_IS_OK(status)) { /* Ok we really don't manage to get this certs ...*/ @@ -1236,10 +1236,10 @@ static WERROR bkrp_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, } } - if (secret.length != 0) { + if (lsa_secret.length != 0) { char *cert_secret_name; - status = GUID_from_ndr_blob(&secret, &guid); + status = GUID_from_ndr_blob(&lsa_secret, &guid); if (!NT_STATUS_IS_OK(status)) { return WERR_FILE_NOT_FOUND; } @@ -1258,14 +1258,14 @@ static WERROR bkrp_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, status = get_lsa_secret(mem_ctx, ldb_ctx, cert_secret_name, - &secret); + &lsa_secret); if (!NT_STATUS_IS_OK(status)) { return WERR_FILE_NOT_FOUND; } - if (secret.length != 0) { + if (lsa_secret.length != 0) { struct bkrp_exported_RSA_key_pair keypair; - ndr_err = ndr_pull_struct_blob(&secret, mem_ctx, &keypair, + ndr_err = ndr_pull_struct_blob(&lsa_secret, mem_ctx, &keypair, (ndr_pull_flags_fn_t)ndr_pull_bkrp_exported_RSA_key_pair); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return WERR_FILE_NOT_FOUND; -- 2.1.4 From 1a00bbdee46bda4bd4030b39594cdf40107ddb0f Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 16:23:17 +1300 Subject: [PATCH 22/32] backupkey: Improve variable names to make clear this is client-provided data The values we return here are client-provided passwords or other keys, that we decrypt for them. Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 51086f30dd1f0ca656b5391e1500cc65480564e3) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 8c8e036..7200e16 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -571,7 +571,7 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, char *guid_string; char *cert_secret_name; DATA_BLOB lsa_secret; - DATA_BLOB *uncrypted; + DATA_BLOB *uncrypted_data; NTSTATUS status; blob.data = r->in.data_in; @@ -698,13 +698,13 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, if (!W_ERROR_IS_OK(werr)) { return werr; } - uncrypted = talloc(mem_ctx, DATA_BLOB); - if (uncrypted == NULL) { + uncrypted_data = talloc(mem_ctx, DATA_BLOB); + if (uncrypted_data == NULL) { return WERR_INVALID_DATA; } - uncrypted->data = uncrypted_secretv2.secret; - uncrypted->length = uncrypted_secretv2.secret_len; + uncrypted_data->data = uncrypted_secretv2.secret; + uncrypted_data->length = uncrypted_secretv2.secret_len; } if (uncrypt_request.version == 3) { struct bkrp_encrypted_secret_v3 uncrypted_secretv3; @@ -743,13 +743,13 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, return werr; } - uncrypted = talloc(mem_ctx, DATA_BLOB); - if (uncrypted == NULL) { + uncrypted_data = talloc(mem_ctx, DATA_BLOB); + if (uncrypted_data == NULL) { return WERR_INVALID_DATA; } - uncrypted->data = uncrypted_secretv3.secret; - uncrypted->length = uncrypted_secretv3.secret_len; + uncrypted_data->data = uncrypted_secretv3.secret; + uncrypted_data->length = uncrypted_secretv3.secret_len; } /* @@ -760,7 +760,7 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, */ } - if (uncrypted->data == NULL) { + if (uncrypted_data->data == NULL) { return WERR_INVALID_DATA; } @@ -769,10 +769,10 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, * parent structure is just an array of bytes it a lot of work * work just prepending 4 bytes */ - *(r->out.data_out) = talloc_zero_array(mem_ctx, uint8_t, uncrypted->length + 4); + *(r->out.data_out) = talloc_zero_array(mem_ctx, uint8_t, uncrypted_data->length + 4); W_ERROR_HAVE_NO_MEMORY(*(r->out.data_out)); - memcpy(4+*(r->out.data_out), uncrypted->data, uncrypted->length); - *(r->out.data_out_len) = uncrypted->length + 4; + memcpy(4+*(r->out.data_out), uncrypted_data->data, uncrypted_data->length); + *(r->out.data_out_len) = uncrypted_data->length + 4; return WERR_OK; } -- 2.1.4 From 458acce90e612cf615ed610095e357edba913f24 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 16:26:23 +1300 Subject: [PATCH 23/32] backupkey: Handle more clearly the case where we find the secret, but it has no value This happen on the RODC, a case that we try not to permit at all. Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit cdecd8540a8e5ef1266684fda0dd10d72466d4d8) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 78 +++++++++++-------------- 1 file changed, 33 insertions(+), 45 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 7200e16..70925dc 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -213,18 +213,12 @@ static NTSTATUS get_lsa_secret(TALLOC_CTX *mem_ctx, "(&(cn=%s Secret)(objectclass=secret))", ldb_binary_encode_string(tmp_mem, name)); - if (ret != LDB_SUCCESS || res->count == 0) { + if (ret != LDB_SUCCESS) { talloc_free(tmp_mem); - /* - * Important NOT to use NT_STATUS_OBJECT_NAME_NOT_FOUND - * as this return value is used to detect the case - * when we have the secret but without the currentValue - * (case RODC) - */ + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } else if (res->count == 0) { return NT_STATUS_RESOURCE_NAME_NOT_FOUND; - } - - if (res->count > 1) { + } else if (res->count > 1) { DEBUG(2, ("Secret %s collision\n", name)); talloc_free(tmp_mem); return NT_STATUS_INTERNAL_DB_CORRUPTION; @@ -236,8 +230,9 @@ static NTSTATUS get_lsa_secret(TALLOC_CTX *mem_ctx, * The secret object is here but we don't have the secret value * The most common case is a RODC */ + *lsa_secret = data_blob_null; talloc_free(tmp_mem); - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + return NT_STATUS_OK; } data = val->data; @@ -613,15 +608,11 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, &lsa_secret); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("Error while fetching secret %s\n", cert_secret_name)); - if (NT_STATUS_EQUAL(status,NT_STATUS_OBJECT_NAME_NOT_FOUND)) { - /* we do not have the real secret attribute */ - return WERR_INVALID_PARAMETER; - } else { - return WERR_FILE_NOT_FOUND; - } - } - - if (lsa_secret.length != 0) { + return WERR_FILE_NOT_FOUND; + } else if (lsa_secret.length == 0) { + /* we do not have the real secret attribute, like if we are an RODC */ + return WERR_INVALID_PARAMETER; + } else { hx509_context hctx; struct bkrp_exported_RSA_key_pair keypair; hx509_private_key pk; @@ -1206,37 +1197,34 @@ static WERROR bkrp_retrieve_client_wrap_key(struct dcesrv_call_state *dce_call, ldb_ctx, "BCKUPKEY_PREFERRED", &lsa_secret); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("Error while fetching secret BCKUPKEY_PREFERRED\n")); - if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { - /* Ok we can be in this case if there was no certs */ - struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; - char *dn = talloc_asprintf(mem_ctx, "CN=%s", - lpcfg_realm(lp_ctx)); - - WERROR werr = generate_bkrp_cert(mem_ctx, dce_call, ldb_ctx, dn); - if (!W_ERROR_IS_OK(werr)) { - return WERR_INVALID_PARAMETER; - } - status = get_lsa_secret(mem_ctx, + if (NT_STATUS_EQUAL(status, NT_STATUS_RESOURCE_NAME_NOT_FOUND)) { + /* Ok we can be in this case if there was no certs */ + struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx; + char *dn = talloc_asprintf(mem_ctx, "CN=%s", + lpcfg_realm(lp_ctx)); + + WERROR werr = generate_bkrp_cert(mem_ctx, dce_call, ldb_ctx, dn); + if (!W_ERROR_IS_OK(werr)) { + return WERR_INVALID_PARAMETER; + } + status = get_lsa_secret(mem_ctx, ldb_ctx, "BCKUPKEY_PREFERRED", &lsa_secret); - - if (!NT_STATUS_IS_OK(status)) { - /* Ok we really don't manage to get this certs ...*/ - DEBUG(2, ("Unable to locate BCKUPKEY_PREFERRED after cert generation\n")); - return WERR_FILE_NOT_FOUND; - } - } else { - /* In theory we should NEVER reach this point as it - should only appear in a rodc server */ - /* we do not have the real secret attribute */ - return WERR_INVALID_PARAMETER; + + if (!NT_STATUS_IS_OK(status)) { + /* Ok we really don't manage to get this certs ...*/ + DEBUG(2, ("Unable to locate BCKUPKEY_PREFERRED after cert generation\n")); + return WERR_FILE_NOT_FOUND; } + } else if (!NT_STATUS_IS_OK(status)) { + return WERR_INTERNAL_ERROR; } - if (lsa_secret.length != 0) { + if (lsa_secret.length == 0) { + DEBUG(1, ("No secret in BCKUPKEY_PREFERRED, are we an undetected RODC?\n")); + return WERR_INTERNAL_ERROR; + } else { char *cert_secret_name; status = GUID_from_ndr_blob(&lsa_secret, &guid); -- 2.1.4 From 5b4fd393c160f50671f73ed08e379cd41883651c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Feb 2015 09:53:58 +1300 Subject: [PATCH 24/32] backupkey: Implement ServerWrap Decrypt We implement both modes in BACKUPKEY_RESTORE_GUID, as it may decrypt both ServerWrap and ClientWrap data, and we implement BACKUPKEY_RESTORE_GUID_WIN2K. BUG: https://bugzilla.samba.org/attachment.cgi?bugid=11097 Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit c3c54b9bf36ef5075fdca9042296f033db8673ce) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 237 +++++++++++++++++++----- 1 file changed, 186 insertions(+), 51 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 70925dc..4c9115c 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -1320,34 +1320,8 @@ static WERROR generate_bkrp_server_wrap_key(TALLOC_CTX *ctx, struct ldb_context return WERR_OK; } -static WERROR bkrp_server_wrap_decrypt_data(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, - struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx) -{ - struct bkrp_server_side_wrapped uncrypt_request; - DATA_BLOB blob; - enum ndr_err_code ndr_err; - - blob.data = r->in.data_in; - blob.length = r->in.data_in_len; - - if (r->in.data_in_len == 0 || r->in.data_in == NULL) { - return WERR_INVALID_PARAM; - } - - ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &uncrypt_request, - (ndr_pull_flags_fn_t)ndr_pull_bkrp_server_side_wrapped); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - return WERR_INVALID_PARAM; - } - - return WERR_NOT_SUPPORTED; -} - /* - * Find the current, preferred ServerWrap Key by looking at - * G$BCKUPKEY_P in the LSA secrets store. - * - * Then find the current decryption keys from the LSA secrets store as + * Find the specified decryption keys from the LSA secrets store as * G$BCKUPKEY_keyGuidString. */ @@ -1360,22 +1334,7 @@ static WERROR bkrp_do_retrieve_server_wrap_key(TALLOC_CTX *mem_ctx, struct ldb_c char *secret_name; char *guid_string; enum ndr_err_code ndr_err; - - status = get_lsa_secret(mem_ctx, ldb_ctx, "BCKUPKEY_P", &guid_binary); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("Error while fetching secret BCKUPKEY_P to find current GUID\n")); - return WERR_FILE_NOT_FOUND; - } else if (guid_binary.length == 0) { - /* RODC case, we do not have secrets locally */ - DEBUG(1, ("Unable to fetch value for secret BCKUPKEY_P, are we an undetected RODC?\n")); - return WERR_INTERNAL_ERROR; - } - status = GUID_from_ndr_blob(&guid_binary, guid); - if (!NT_STATUS_IS_OK(status)) { - return WERR_FILE_NOT_FOUND; - } - guid_string = GUID_string(mem_ctx, guid); if (guid_string == NULL) { /* We return file not found because the client @@ -1392,7 +1351,7 @@ static WERROR bkrp_do_retrieve_server_wrap_key(TALLOC_CTX *mem_ctx, struct ldb_c status = get_lsa_secret(mem_ctx, ldb_ctx, secret_name, &lsa_secret); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("Error while fetching secret %s\n", secret_name)); - return WERR_FILE_NOT_FOUND; + return WERR_INVALID_DATA; } else if (guid_binary.length == 0) { /* RODC case, we do not have secrets locally */ DEBUG(1, ("Unable to fetch value for secret %s, are we an undetected RODC?\n", @@ -1403,13 +1362,187 @@ static WERROR bkrp_do_retrieve_server_wrap_key(TALLOC_CTX *mem_ctx, struct ldb_c (ndr_pull_flags_fn_t)ndr_pull_bkrp_dc_serverwrap_key); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(2, ("Unable to parse the ndr encoded server wrap key %s\n", secret_name)); + return WERR_INVALID_DATA; + } + + return WERR_OK; +} + +/* + * Find the current, preferred ServerWrap Key by looking at + * G$BCKUPKEY_P in the LSA secrets store. + * + * Then find the current decryption keys from the LSA secrets store as + * G$BCKUPKEY_keyGuidString. + */ + +static WERROR bkrp_do_retrieve_default_server_wrap_key(TALLOC_CTX *mem_ctx, + struct ldb_context *ldb_ctx, + struct bkrp_dc_serverwrap_key *server_key, + struct GUID *returned_guid) +{ + NTSTATUS status; + DATA_BLOB guid_binary; + + status = get_lsa_secret(mem_ctx, ldb_ctx, "BCKUPKEY_P", &guid_binary); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Error while fetching secret BCKUPKEY_P to find current GUID\n")); + return WERR_FILE_NOT_FOUND; + } else if (guid_binary.length == 0) { + /* RODC case, we do not have secrets locally */ + DEBUG(1, ("Unable to fetch value for secret BCKUPKEY_P, are we an undetected RODC?\n")); + return WERR_INTERNAL_ERROR; + } + + status = GUID_from_ndr_blob(&guid_binary, returned_guid); + if (!NT_STATUS_IS_OK(status)) { return WERR_FILE_NOT_FOUND; } + return bkrp_do_retrieve_server_wrap_key(mem_ctx, ldb_ctx, + server_key, returned_guid); +} + +static WERROR bkrp_server_wrap_decrypt_data(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, + struct bkrp_BackupKey *r ,struct ldb_context *ldb_ctx) +{ + WERROR werr; + struct bkrp_server_side_wrapped decrypt_request; + DATA_BLOB sid_blob, encrypted_blob, symkey_blob; + DATA_BLOB blob; + enum ndr_err_code ndr_err; + struct bkrp_dc_serverwrap_key server_key; + struct bkrp_rc4encryptedpayload rc4payload; + struct dom_sid *caller_sid; + uint8_t symkey[20]; /* SHA-1 hash len */ + uint8_t mackey[20]; /* SHA-1 hash len */ + uint8_t mac[20]; /* SHA-1 hash len */ + unsigned int hash_len; + HMAC_CTX ctx; + + blob.data = r->in.data_in; + blob.length = r->in.data_in_len; + + if (r->in.data_in_len == 0 || r->in.data_in == NULL) { + return WERR_INVALID_PARAM; + } + + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &decrypt_request, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_server_side_wrapped); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INVALID_PARAM; + } + + if (decrypt_request.magic != 1) { + return WERR_INVALID_PARAM; + } + + werr = bkrp_do_retrieve_server_wrap_key(mem_ctx, ldb_ctx, &server_key, + &decrypt_request.guid); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + dump_data_pw("server_key: \n", server_key.key, sizeof(server_key.key)); + + dump_data_pw("r2: \n", decrypt_request.r2, sizeof(decrypt_request.r2)); + + /* + * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1 + * BACKUPKEY_BACKUP_GUID, it really is the whole key + */ + HMAC(EVP_sha1(), server_key.key, sizeof(server_key.key), + decrypt_request.r2, sizeof(decrypt_request.r2), + symkey, &hash_len); + + dump_data_pw("symkey: \n", symkey, hash_len); + + /* rc4 decrypt sid and secret using sym key */ + symkey_blob = data_blob_const(symkey, sizeof(symkey)); + + encrypted_blob = data_blob_const(decrypt_request.rc4encryptedpayload, + decrypt_request.ciphertext_length); + + arcfour_crypt_blob(encrypted_blob.data, encrypted_blob.length, &symkey_blob); + + ndr_err = ndr_pull_struct_blob(&encrypted_blob, mem_ctx, &rc4payload, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_rc4encryptedpayload); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INVALID_PARAM; + } + + if (decrypt_request.payload_length != rc4payload.secret_data.length) { + return WERR_INVALID_PARAM; + } + + dump_data_pw("r3: \n", rc4payload.r3, sizeof(rc4payload.r3)); + + /* + * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1 + * BACKUPKEY_BACKUP_GUID, it really is the whole key + */ + HMAC(EVP_sha1(), server_key.key, sizeof(server_key.key), + rc4payload.r3, sizeof(rc4payload.r3), + mackey, &hash_len); + + dump_data_pw("mackey: \n", mackey, sizeof(mackey)); + + ndr_err = ndr_push_struct_blob(&sid_blob, mem_ctx, &rc4payload.sid, + (ndr_push_flags_fn_t)ndr_push_dom_sid); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INTERNAL_ERROR; + } + + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, mackey, hash_len, EVP_sha1(), NULL); + /* SID field */ + HMAC_Update(&ctx, sid_blob.data, sid_blob.length); + /* Secret field */ + HMAC_Update(&ctx, rc4payload.secret_data.data, rc4payload.secret_data.length); + HMAC_Final(&ctx, mac, &hash_len); + HMAC_CTX_cleanup(&ctx); + + dump_data_pw("mac: \n", mac, sizeof(mac)); + dump_data_pw("rc4payload.mac: \n", rc4payload.mac, sizeof(rc4payload.mac)); + + if (memcmp(mac, rc4payload.mac, sizeof(mac)) != 0) { + return WERR_INVALID_ACCESS; + } + + caller_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; + + if (!dom_sid_equal(&rc4payload.sid, caller_sid)) { + return WERR_INVALID_ACCESS; + } + + *(r->out.data_out) = rc4payload.secret_data.data; + *(r->out.data_out_len) = rc4payload.secret_data.length; + return WERR_OK; } /* + * For BACKUPKEY_RESTORE_GUID we need to check the first 4 bytes to + * determine what type of restore is wanted. + * + * See MS-BKRP 3.1.4.1.4 BACKUPKEY_RESTORE_GUID point 1. + */ + +static WERROR bkrp_generic_decrypt_data(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, + struct bkrp_BackupKey *r, struct ldb_context *ldb_ctx) +{ + if (r->in.data_in_len < 4 || r->in.data_in == NULL) { + return WERR_INVALID_PARAM; + } + + if (IVAL(r->in.data_in, 0) == 1) { + return bkrp_server_wrap_decrypt_data(dce_call, mem_ctx, r, ldb_ctx); + } + + return bkrp_client_wrap_decrypt_data(dce_call, mem_ctx, r, ldb_ctx); +} + +/* * We have some data, such as saved website or IMAP passwords that the * client would like to put into the profile on-disk. This needs to * be encrypted. This version gives the server the data over the @@ -1446,9 +1579,9 @@ static WERROR bkrp_server_wrap_encrypt_data(struct dcesrv_call_state *dce_call, return WERR_INVALID_PARAM; } - werr = bkrp_do_retrieve_server_wrap_key(mem_ctx, - ldb_ctx, &server_key, - &guid); + werr = bkrp_do_retrieve_default_server_wrap_key(mem_ctx, + ldb_ctx, &server_key, + &guid); if (!W_ERROR_IS_OK(werr)) { if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) { @@ -1458,8 +1591,10 @@ static WERROR bkrp_server_wrap_encrypt_data(struct dcesrv_call_state *dce_call, if (!W_ERROR_IS_OK(werr)) { return WERR_INVALID_PARAMETER; } - werr = bkrp_do_retrieve_server_wrap_key(mem_ctx, - ldb_ctx, &server_key, &guid); + werr = bkrp_do_retrieve_default_server_wrap_key(mem_ctx, + ldb_ctx, + &server_key, + &guid); if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) { /* Ok we really don't manage to get this secret ...*/ @@ -1608,8 +1743,8 @@ static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call, if (!is_rodc) { if(strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), BACKUPKEY_RESTORE_GUID, strlen(BACKUPKEY_RESTORE_GUID)) == 0) { - DEBUG(debuglevel, ("Client %s requested to decrypt a client side wrapped secret\n", addr)); - error = bkrp_client_wrap_decrypt_data(dce_call, mem_ctx, r, ldb_ctx); + DEBUG(debuglevel, ("Client %s requested to decrypt a wrapped secret\n", addr)); + error = bkrp_generic_decrypt_data(dce_call, mem_ctx, r, ldb_ctx); } if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), @@ -1620,7 +1755,7 @@ static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call, if (strncasecmp(GUID_string(mem_ctx, r->in.guidActionAgent), BACKUPKEY_RESTORE_GUID_WIN2K, strlen(BACKUPKEY_RESTORE_GUID_WIN2K)) == 0) { - DEBUG(debuglevel, ("Client %s requested to decrypt a server side wrapped secret, not implemented yet\n", addr)); + DEBUG(debuglevel, ("Client %s requested to decrypt a server side wrapped secret\n", addr)); error = bkrp_server_wrap_decrypt_data(dce_call, mem_ctx, r, ldb_ctx); } -- 2.1.4 From 78875088504cd88240d64ac75911bbc78fd95b9d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Feb 2015 13:37:16 +1300 Subject: [PATCH 25/32] backupkey: Change expected error codes to match Windows 2008R2 and Windows 2012R2 This is done in both smbtoture and in our server Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 93510eb513598431c260cd0b85a02d0e49cc821b) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 4 ++-- source4/torture/rpc/backupkey.c | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 4c9115c..22c86c7 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -608,7 +608,7 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, &lsa_secret); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("Error while fetching secret %s\n", cert_secret_name)); - return WERR_FILE_NOT_FOUND; + return WERR_INVALID_DATA; } else if (lsa_secret.length == 0) { /* we do not have the real secret attribute, like if we are an RODC */ return WERR_INVALID_PARAMETER; @@ -661,7 +661,7 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, hx509_private_key_free(&pk); if (res != 0) { /* We are not able to decrypt the secret, looks like something is wrong */ - return WERR_INVALID_DATA; + return WERR_INVALID_PARAMETER; } blob_us.data = uncrypted_secret.data; blob_us.length = uncrypted_secret.length; diff --git a/source4/torture/rpc/backupkey.c b/source4/torture/rpc/backupkey.c index 8187643..967ea47 100644 --- a/source4/torture/rpc/backupkey.c +++ b/source4/torture/rpc/backupkey.c @@ -775,7 +775,7 @@ static bool test_RestoreGUID_ko(struct torture_context *tctx, out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped"); - torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_DATA, "Wrong error code"); + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_PARAM, "Wrong error code"); } else { struct bkrp_BackupKey *r = createRetreiveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), @@ -980,7 +980,14 @@ static bool test_RestoreGUID_badcertguid(struct torture_context *tctx, out_blob.length = *r->out.data_out_len; ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped"); - torture_assert_werr_equal(tctx, r->out.result, WERR_FILE_NOT_FOUND, "Bad error code on wrong has in access check"); + + /* + * Windows 2012R2 has, presumably, a programming error + * returning an NTSTATUS code on this interface + */ + if (W_ERROR_V(r->out.result) != NT_STATUS_V(NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_DATA, "Bad error code on wrong has in access check"); + } } else { struct bkrp_BackupKey *r = createRetreiveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), -- 2.1.4 From 0d90a051ca185197d9963e058fa2a223e3097272 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Feb 2015 17:46:42 +1300 Subject: [PATCH 26/32] backupkey: Add tests for ServerWrap protocol Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit d8cc370d01445b5120678dde02955f13f3773bb2) --- source4/torture/rpc/backupkey.c | 647 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 645 insertions(+), 2 deletions(-) diff --git a/source4/torture/rpc/backupkey.c b/source4/torture/rpc/backupkey.c index 967ea47..3abc2d7 100644 --- a/source4/torture/rpc/backupkey.c +++ b/source4/torture/rpc/backupkey.c @@ -32,6 +32,16 @@ #include #include +enum test_wrong { + WRONG_MAGIC, + WRONG_R2, + WRONG_PAYLOAD_LENGTH, + WRONG_CIPHERTEXT_LENGTH, + SHORT_PAYLOAD_LENGTH, + SHORT_CIPHERTEXT_LENGTH, + ZERO_PAYLOAD_LENGTH, + ZERO_CIPHERTEXT_LENGTH +}; /* Our very special and valued secret */ /* No need to put const as we cast the array in uint8_t @@ -1130,6 +1140,604 @@ static bool test_RetreiveBackupKeyGUID_2048bits(struct torture_context *tctx, return true; } +static bool test_ServerWrap_encrypt_decrypt(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct bkrp_BackupKey r; + struct GUID guid; + DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret)); + DATA_BLOB encrypted; + uint32_t enclen; + DATA_BLOB decrypted; + uint32_t declen; + struct dcerpc_binding_handle *b = p->binding_handle; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + ZERO_STRUCT(r); + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + /* Encrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_BACKUP_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = plaintext.data; + r.in.data_in_len = plaintext.length; + r.in.param = 0; + r.out.data_out = &encrypted.data; + r.out.data_out_len = &enclen; + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "encrypt"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "encrypt"); + return true; + } + torture_assert_werr_ok(tctx, + r.out.result, + "encrypt"); + encrypted.length = *r.out.data_out_len; + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_ok(tctx, + r.out.result, + "decrypt"); + decrypted.length = *r.out.data_out_len; + + /* Compare */ + torture_assert_data_blob_equal(tctx, plaintext, decrypted, "Decrypt failed"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_ok(tctx, + r.out.result, + "decrypt"); + decrypted.length = *r.out.data_out_len; + + /* Compare */ + torture_assert_data_blob_equal(tctx, plaintext, decrypted, "Decrypt failed"); + return true; +} + +static bool test_ServerWrap_decrypt_wrong_keyGUID(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct bkrp_BackupKey r; + struct GUID guid; + DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret)); + DATA_BLOB encrypted; + uint32_t enclen; + DATA_BLOB decrypted; + uint32_t declen; + struct dcerpc_binding_handle *b = p->binding_handle; + enum ndr_err_code ndr_err; + struct bkrp_server_side_wrapped server_side_wrapped; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + ZERO_STRUCT(r); + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + /* Encrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_BACKUP_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = plaintext.data; + r.in.data_in_len = plaintext.length; + r.in.param = 0; + r.out.data_out = &encrypted.data; + r.out.data_out_len = &enclen; + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "encrypt"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "encrypt"); + return true; + } + torture_assert_werr_ok(tctx, + r.out.result, + "encrypt"); + encrypted.length = *r.out.data_out_len; + + ndr_err = ndr_pull_struct_blob(&encrypted, tctx, &server_side_wrapped, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_server_side_wrapped); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "pull of server_side_wrapped"); + + /* Change the GUID */ + server_side_wrapped.guid = GUID_random(); + + ndr_err = ndr_push_struct_blob(&encrypted, tctx, &server_side_wrapped, + (ndr_push_flags_fn_t)ndr_push_bkrp_server_side_wrapped); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "push of server_side_wrapped"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_DATA, + "decrypt should fail with WERR_INVALID_DATA"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_DATA, + "decrypt should fail with WERR_INVALID_DATA"); + + return true; +} + +static bool test_ServerWrap_decrypt_empty_request(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct bkrp_BackupKey r; + struct GUID guid; + DATA_BLOB decrypted; + uint32_t declen; + struct dcerpc_binding_handle *b = p->binding_handle; + uint8_t short_request[4] = { 1, 0, 0, 0 }; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + ZERO_STRUCT(r); + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 0; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "encrypt"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "encrypt"); + return true; + } + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAM, + "decrypt should fail with WERR_INVALID_PARAM"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 0; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAM, + "decrypt should fail with WERR_INVALID_PARAM"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = NULL; + r.in.data_in_len = 0; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_INVALID_PARAMETER_MIX, + "decrypt"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = NULL; + r.in.data_in_len = 0; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_INVALID_PARAMETER_MIX, + "decrypt"); + + return true; +} + + +static bool test_ServerWrap_decrypt_short_request(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + struct bkrp_BackupKey r; + struct GUID guid; + DATA_BLOB decrypted; + uint32_t declen; + struct dcerpc_binding_handle *b = p->binding_handle; + uint8_t short_request[4] = { 1, 0, 0, 0 }; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + ZERO_STRUCT(r); + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 4; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "encrypt"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "encrypt"); + return true; + } + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAM, + "decrypt should fail with WERR_INVALID_PARM"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 4; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAM, + "decrypt should fail with WERR_INVALID_PARAM"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 1; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAM, + "decrypt should fail with WERR_INVALID_PARAM"); + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = short_request; + r.in.data_in_len = 1; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAM, + "decrypt should fail with WERR_INVALID_PARAM"); + + return true; +} + + +static bool test_ServerWrap_decrypt_wrong_stuff(struct torture_context *tctx, + struct dcerpc_pipe *p, + enum test_wrong wrong) +{ + struct bkrp_BackupKey r; + struct GUID guid; + DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret)); + DATA_BLOB encrypted; + uint32_t enclen; + DATA_BLOB decrypted; + uint32_t declen; + struct dcerpc_binding_handle *b = p->binding_handle; + enum ndr_err_code ndr_err; + struct bkrp_server_side_wrapped server_side_wrapped; + bool repush = false; + enum dcerpc_AuthType auth_type; + enum dcerpc_AuthLevel auth_level; + ZERO_STRUCT(r); + + dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level); + + /* Encrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_BACKUP_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = plaintext.data; + r.in.data_in_len = plaintext.length; + r.in.param = 0; + r.out.data_out = &encrypted.data; + r.out.data_out_len = &enclen; + if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) { + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "encrypt"); + } else { + torture_assert_ntstatus_equal(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + NT_STATUS_ACCESS_DENIED, + "encrypt"); + return true; + } + torture_assert_werr_ok(tctx, + r.out.result, + "encrypt"); + encrypted.length = *r.out.data_out_len; + + ndr_err = ndr_pull_struct_blob(&encrypted, tctx, &server_side_wrapped, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_server_side_wrapped); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "pull of server_side_wrapped"); + + torture_assert_int_equal(tctx, server_side_wrapped.payload_length, plaintext.length, + "wrong payload length"); + + switch (wrong) { + case WRONG_MAGIC: + /* Change the magic. Forced by our NDR layer, so do it raw */ + SIVAL(encrypted.data, 0, 78); /* valid values are 1-3 */ + break; + case WRONG_R2: + server_side_wrapped.r2[0] = 78; + server_side_wrapped.r2[1] = 78; + server_side_wrapped.r2[3] = 78; + repush = true; + break; + case WRONG_PAYLOAD_LENGTH: + server_side_wrapped.payload_length = UINT32_MAX - 8; + repush = true; + break; + case WRONG_CIPHERTEXT_LENGTH: + /* + * Change the ciphertext len. We can't push this if + * we have it wrong, so do it raw + */ + SIVAL(encrypted.data, 8, UINT32_MAX - 8); /* valid values are 1-3 */ + break; + case SHORT_PAYLOAD_LENGTH: + server_side_wrapped.payload_length = server_side_wrapped.payload_length - 8; + repush = true; + break; + case SHORT_CIPHERTEXT_LENGTH: + /* + * Change the ciphertext len. We can't push this if + * we have it wrong, so do it raw + */ + SIVAL(encrypted.data, 8, server_side_wrapped.ciphertext_length - 8); /* valid values are 1-3 */ + break; + case ZERO_PAYLOAD_LENGTH: + server_side_wrapped.payload_length = 0; + repush = true; + break; + case ZERO_CIPHERTEXT_LENGTH: + /* + * Change the ciphertext len. We can't push this if + * we have it wrong, so do it raw + */ + SIVAL(encrypted.data, 8, 0); /* valid values are 1-3 */ + break; + } + + if (repush) { + ndr_err = ndr_push_struct_blob(&encrypted, tctx, &server_side_wrapped, + (ndr_push_flags_fn_t)ndr_push_bkrp_server_side_wrapped); + torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "push of server_side_wrapped"); + } + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + if (wrong == WRONG_R2 && W_ERROR_EQUAL(r.out.result, WERR_INVALID_SID)) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_SID, + "decrypt should fail with WERR_INVALID_SID or WERR_INVALID_PARAM"); + } else { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAM, + "decrypt should fail with WERR_INVALID_PARAM"); + } + + /* Decrypt */ + torture_assert_ntstatus_ok(tctx, + GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid), + "obtain GUID"); + + r.in.guidActionAgent = &guid; + r.in.data_in = encrypted.data; + r.in.data_in_len = encrypted.length; + r.in.param = 0; + r.out.data_out = &(decrypted.data); + r.out.data_out_len = &declen; + torture_assert_ntstatus_ok(tctx, + dcerpc_bkrp_BackupKey_r(b, tctx, &r), + "decrypt"); + if (wrong == WRONG_R2 && W_ERROR_EQUAL(r.out.result, WERR_INVALID_SID)) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_SID, + "decrypt should fail with WERR_INVALID_SID or WERR_INVALID_PARAM"); + } else { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_PARAM, + "decrypt should fail with WERR_INVALID_PARAM"); + } + + return true; +} + +static bool test_ServerWrap_decrypt_wrong_magic(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_MAGIC); +} + +static bool test_ServerWrap_decrypt_wrong_r2(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_R2); +} + +static bool test_ServerWrap_decrypt_wrong_payload_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_PAYLOAD_LENGTH); +} + +static bool test_ServerWrap_decrypt_short_payload_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, SHORT_PAYLOAD_LENGTH); +} + +static bool test_ServerWrap_decrypt_zero_payload_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, ZERO_PAYLOAD_LENGTH); +} + +static bool test_ServerWrap_decrypt_wrong_ciphertext_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_CIPHERTEXT_LENGTH); +} + +static bool test_ServerWrap_decrypt_short_ciphertext_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, SHORT_CIPHERTEXT_LENGTH); +} + +static bool test_ServerWrap_decrypt_zero_ciphertext_length(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, ZERO_CIPHERTEXT_LENGTH); +} + struct torture_suite *torture_rpc_backupkey(TALLOC_CTX *mem_ctx) { struct torture_rpc_tcase *tcase; @@ -1147,7 +1755,7 @@ struct torture_suite *torture_rpc_backupkey(TALLOC_CTX *mem_ctx) torture_rpc_tcase_add_test(tcase, "restore_guid version 3", test_RestoreGUID_v3); -/* We double the test in order to be sure that we don't mess stuff (ie. freeing static stuff */ +/* We double the test in order to be sure that we don't mess stuff (ie. freeing static stuff) */ torture_rpc_tcase_add_test(tcase, "restore_guid_2nd", test_RestoreGUID); @@ -1177,7 +1785,42 @@ struct torture_suite *torture_rpc_backupkey(TALLOC_CTX *mem_ctx) test_RestoreGUID_emptyrequest); torture_rpc_tcase_add_test(tcase, "retreive_backup_key_guid_2048_bits", - test_RetreiveBackupKeyGUID_2048bits); + test_RetreiveBackupKeyGUID_2048bits); + + torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt", + test_ServerWrap_encrypt_decrypt); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_keyGUID", + test_ServerWrap_decrypt_wrong_keyGUID); + + torture_rpc_tcase_add_test(tcase, "server_wrap_empty_request", + test_ServerWrap_decrypt_empty_request); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_short_request", + test_ServerWrap_decrypt_short_request); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_magic", + test_ServerWrap_decrypt_wrong_magic); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_r2", + test_ServerWrap_decrypt_wrong_r2); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_payload_length", + test_ServerWrap_decrypt_wrong_payload_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_short_payload_length", + test_ServerWrap_decrypt_short_payload_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_zero_payload_length", + test_ServerWrap_decrypt_zero_payload_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_ciphertext_length", + test_ServerWrap_decrypt_wrong_ciphertext_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_short_ciphertext_length", + test_ServerWrap_decrypt_short_ciphertext_length); + torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_zero_ciphertext_length", + test_ServerWrap_decrypt_zero_ciphertext_length); return suite; } -- 2.1.4 From 78f90139fb494c24543edfd4e7c70870f9d63da2 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 12 Feb 2015 16:15:41 +1300 Subject: [PATCH 27/32] backupkey: Better handling for different wrap version headers Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit 3254f9bc009bae3d8463035d63eb1625f23606e6) --- librpc/idl/backupkey.idl | 6 +++++ source4/rpc_server/backupkey/dcesrv_backupkey.c | 31 +++++++++++++++---------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/librpc/idl/backupkey.idl b/librpc/idl/backupkey.idl index 76c0eb7..81e0db6 100644 --- a/librpc/idl/backupkey.idl +++ b/librpc/idl/backupkey.idl @@ -119,6 +119,12 @@ interface backupkey } bkrp_opaque_blob; typedef enum { + BACKUPKEY_SERVER_WRAP_VERSION = 1, + BACKUPKEY_CLIENT_WRAP_VERSION2 = 2, + BACKUPKEY_CLIENT_WRAP_VERSION3 = 3 + } bkrp_versions; + + typedef enum { BACKUPKEY_INVALID_GUID_INTEGER = 0xFFFF, BACKUPKEY_RESTORE_GUID_INTEGER = 0x0000, BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID_INTEGER = 0x0001, diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 22c86c7..5a2e8a4 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -42,9 +42,6 @@ #include "librpc/gen_ndr/ndr_security.h" #include "lib/crypto/arcfour.h" -#define BACKUPKEY_MIN_VERSION 2 -#define BACKUPKEY_MAX_VERSION 3 - static const unsigned rsa_with_var_num[] = { 1, 2, 840, 113549, 1, 1, 1 }; /* Equivalent to asn1_oid_id_pkcs1_rsaEncryption*/ static const AlgorithmIdentifier _hx509_signature_rsa_with_var_num = { @@ -568,25 +565,35 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, DATA_BLOB lsa_secret; DATA_BLOB *uncrypted_data; NTSTATUS status; - + uint32_t requested_version; + blob.data = r->in.data_in; blob.length = r->in.data_in_len; - if (r->in.data_in_len == 0 || r->in.data_in == NULL) { + if (r->in.data_in_len < 4 || r->in.data_in == NULL) { return WERR_INVALID_PARAM; } + /* + * We check for the version here, so we can actually print the + * message as we are unlikely to parse it with NDR. + */ + requested_version = IVAL(r->in.data_in, 0); + if ((requested_version != BACKUPKEY_CLIENT_WRAP_VERSION2) + && (requested_version != BACKUPKEY_CLIENT_WRAP_VERSION3)) { + DEBUG(1, ("Request for unknown BackupKey sub-protocol %d\n", requested_version)); + return WERR_INVALID_PARAMETER; + } + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &uncrypt_request, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_wrapped); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return WERR_INVALID_PARAM; } - if (uncrypt_request.version < BACKUPKEY_MIN_VERSION) { - return WERR_INVALID_PARAMETER; - } - - if (uncrypt_request.version > BACKUPKEY_MAX_VERSION) { + if ((uncrypt_request.version != BACKUPKEY_CLIENT_WRAP_VERSION2) + && (uncrypt_request.version != BACKUPKEY_CLIENT_WRAP_VERSION3)) { + DEBUG(1, ("Request for unknown BackupKey sub-protocol %d\n", uncrypt_request.version)); return WERR_INVALID_PARAMETER; } @@ -1433,7 +1440,7 @@ static WERROR bkrp_server_wrap_decrypt_data(struct dcesrv_call_state *dce_call, return WERR_INVALID_PARAM; } - if (decrypt_request.magic != 1) { + if (decrypt_request.magic != BACKUPKEY_SERVER_WRAP_VERSION) { return WERR_INVALID_PARAM; } @@ -1535,7 +1542,7 @@ static WERROR bkrp_generic_decrypt_data(struct dcesrv_call_state *dce_call, TALL return WERR_INVALID_PARAM; } - if (IVAL(r->in.data_in, 0) == 1) { + if (IVAL(r->in.data_in, 0) == BACKUPKEY_SERVER_WRAP_VERSION) { return bkrp_server_wrap_decrypt_data(dce_call, mem_ctx, r, ldb_ctx); } -- 2.1.4 From 2cfc9b8b03b14a588236ca643f87a41d2ddfd32e Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 13 Feb 2015 12:59:45 +1300 Subject: [PATCH 28/32] torture-backupkey: Add tests that read the secret from the server, and validate These show that MS-BKRP 3.1.4.1.1 BACKUPKEY_BACKUP_GUID is incorrect when it states that the key must be the leading 64 bytes, it must be the whole 256 byte buffer. Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam (cherry picked from commit f7b6e696ed552f02195c87a7eede5a0090f8df1f) --- source4/torture/rpc/backupkey.c | 321 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 312 insertions(+), 9 deletions(-) diff --git a/source4/torture/rpc/backupkey.c b/source4/torture/rpc/backupkey.c index 3abc2d7..53caf74 100644 --- a/source4/torture/rpc/backupkey.c +++ b/source4/torture/rpc/backupkey.c @@ -23,14 +23,21 @@ #include "librpc/gen_ndr/ndr_backupkey_c.h" #include "librpc/gen_ndr/ndr_backupkey.h" #include "librpc/gen_ndr/ndr_lsa_c.h" +#include "librpc/gen_ndr/ndr_security.h" #include "torture/rpc/torture_rpc.h" +#include "torture/ndr/ndr.h" #include "lib/cmdline/popt_common.h" +#include "libcli/auth/proto.h" +#include "lib/crypto/arcfour.h" #include #include #include #include #include #include +#include +#include +#include enum test_wrong { WRONG_MAGIC, @@ -40,7 +47,10 @@ enum test_wrong { SHORT_PAYLOAD_LENGTH, SHORT_CIPHERTEXT_LENGTH, ZERO_PAYLOAD_LENGTH, - ZERO_CIPHERTEXT_LENGTH + ZERO_CIPHERTEXT_LENGTH, + RIGHT_KEY, + WRONG_KEY, + WRONG_SID, }; /* Our very special and valued secret */ @@ -50,10 +60,9 @@ enum test_wrong { static const char secret[] = "tata yoyo mais qu'est ce qu'il y a sous ton grand chapeau ?"; /* Get the SID from a user */ -static const struct dom_sid *get_user_sid(struct torture_context *tctx, - struct dcerpc_pipe *p, - TALLOC_CTX *mem_ctx, - const char *user) +static struct dom_sid *get_user_sid(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + const char *user) { struct lsa_ObjectAttribute attr; struct lsa_QosInfo qos; @@ -258,7 +267,7 @@ static DATA_BLOB *create_access_check(struct torture_context *tctx, TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); DATA_BLOB *blob = talloc_zero(mem_ctx, DATA_BLOB); enum ndr_err_code ndr_err; - const struct dom_sid *sid = get_user_sid(tctx, p, tmp_ctx, user); + const struct dom_sid *sid = get_user_sid(tctx, tmp_ctx, user); if (sid == NULL) { return NULL; @@ -1527,6 +1536,239 @@ static bool test_ServerWrap_decrypt_short_request(struct torture_context *tctx, return true; } +static bool test_ServerWrap_encrypt_decrypt_manual(struct torture_context *tctx, + struct bkrp_server_side_wrapped *server_side_wrapped, + enum test_wrong wrong) +{ + struct dcerpc_pipe *lsa_p; + struct dcerpc_binding_handle *lsa_b; + struct lsa_OpenSecret r_secret; + struct lsa_QuerySecret r_query_secret; + struct policy_handle *handle, sec_handle; + struct bkrp_BackupKey r; + struct GUID preferred_key_guid; + DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret)); + DATA_BLOB preferred_key, preferred_key_clear, session_key, + decrypt_key, decrypt_key_clear, encrypted_blob, symkey_blob, + sid_blob; + struct bkrp_dc_serverwrap_key server_key; + struct lsa_DATA_BUF_PTR bufp1; + char *key_guid_string; + struct bkrp_rc4encryptedpayload rc4payload; + struct dom_sid *caller_sid; + uint8_t symkey[20]; /* SHA-1 hash len */ + uint8_t mackey[20]; /* SHA-1 hash len */ + uint8_t mac[20]; /* SHA-1 hash len */ + unsigned int hash_len; + HMAC_CTX ctx; + ZERO_STRUCT(r); + ZERO_STRUCT(r_secret); + ZERO_STRUCT(r_query_secret); + + /* Now read BCKUPKEY_P and prove we can do a matching decrypt and encrypt */ + + torture_assert_ntstatus_ok(tctx, + torture_rpc_connection(tctx, &lsa_p, &ndr_table_lsarpc), + "Opening LSA pipe"); + lsa_b = lsa_p->binding_handle; + + torture_assert(tctx, test_lsa_OpenPolicy2(lsa_b, tctx, &handle), "OpenPolicy failed"); + r_secret.in.name.string = "G$BCKUPKEY_P"; + + r_secret.in.handle = handle; + r_secret.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r_secret.out.sec_handle = &sec_handle; + + torture_comment(tctx, "Testing OpenSecret\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenSecret_r(lsa_b, tctx, &r_secret), + "OpenSecret failed"); + torture_assert_ntstatus_ok(tctx, r_secret.out.result, + "OpenSecret failed"); + + r_query_secret.in.sec_handle = &sec_handle; + r_query_secret.in.new_val = &bufp1; + bufp1.buf = NULL; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(lsa_b, tctx, &r_query_secret), + "QuerySecret failed"); + torture_assert_ntstatus_ok(tctx, r_query_secret.out.result, + "QuerySecret failed"); + + + preferred_key.data = r_query_secret.out.new_val->buf->data; + preferred_key.length = r_query_secret.out.new_val->buf->size; + torture_assert_ntstatus_ok(tctx, dcerpc_fetch_session_key(lsa_p, &session_key), + "dcerpc_fetch_session_key failed"); + + torture_assert_ntstatus_ok(tctx, + sess_decrypt_blob(tctx, + &preferred_key, &session_key, &preferred_key_clear), + "sess_decrypt_blob failed"); + + torture_assert_ntstatus_ok(tctx, GUID_from_ndr_blob(&preferred_key_clear, &preferred_key_guid), + "GUID parse failed"); + + torture_assert_guid_equal(tctx, server_side_wrapped->guid, + preferred_key_guid, + "GUID didn't match value pointed at by G$BCKUPKEY_P"); + + /* And read BCKUPKEY_ and get the actual key */ + + key_guid_string = GUID_string(tctx, &server_side_wrapped->guid); + r_secret.in.name.string = talloc_asprintf(tctx, "G$BCKUPKEY_%s", key_guid_string); + + r_secret.in.handle = handle; + r_secret.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r_secret.out.sec_handle = &sec_handle; + + torture_comment(tctx, "Testing OpenSecret\n"); + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenSecret_r(lsa_b, tctx, &r_secret), + "OpenSecret failed"); + torture_assert_ntstatus_ok(tctx, r_secret.out.result, + "OpenSecret failed"); + + r_query_secret.in.sec_handle = &sec_handle; + r_query_secret.in.new_val = &bufp1; + + torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(lsa_b, tctx, &r_query_secret), + "QuerySecret failed"); + torture_assert_ntstatus_ok(tctx, r_query_secret.out.result, + "QuerySecret failed"); + + + decrypt_key.data = r_query_secret.out.new_val->buf->data; + decrypt_key.length = r_query_secret.out.new_val->buf->size; + + torture_assert_ntstatus_ok(tctx, + sess_decrypt_blob(tctx, + &decrypt_key, &session_key, &decrypt_key_clear), + "sess_decrypt_blob failed"); + + torture_assert_ndr_err_equal(tctx, ndr_pull_struct_blob(&decrypt_key_clear, tctx, &server_key, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_dc_serverwrap_key), + NDR_ERR_SUCCESS, "Failed to parse server_key"); + + torture_assert_int_equal(tctx, server_key.magic, 1, "Failed to correctly decrypt server key"); + + /* + * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1 + * BACKUPKEY_BACKUP_GUID, it really is the whole key + */ + HMAC(EVP_sha1(), server_key.key, sizeof(server_key.key), + server_side_wrapped->r2, sizeof(server_side_wrapped->r2), + symkey, &hash_len); + + /* rc4 decrypt sid and secret using sym key */ + symkey_blob = data_blob_const(symkey, sizeof(symkey)); + + encrypted_blob = data_blob_talloc(tctx, server_side_wrapped->rc4encryptedpayload, + server_side_wrapped->ciphertext_length); + + arcfour_crypt_blob(encrypted_blob.data, encrypted_blob.length, &symkey_blob); + + torture_assert_ndr_err_equal(tctx, ndr_pull_struct_blob(&encrypted_blob, tctx, &rc4payload, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_rc4encryptedpayload), + NDR_ERR_SUCCESS, "Failed to parse rc4encryptedpayload"); + + torture_assert_int_equal(tctx, rc4payload.secret_data.length, + server_side_wrapped->payload_length, + "length of decrypted payload not the length declared in surrounding structure"); + + /* + * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1 + * BACKUPKEY_BACKUP_GUID, it really is the whole key + */ + HMAC(EVP_sha1(), server_key.key, sizeof(server_key.key), + rc4payload.r3, sizeof(rc4payload.r3), + mackey, &hash_len); + + torture_assert_ndr_err_equal(tctx, ndr_push_struct_blob(&sid_blob, tctx, &rc4payload.sid, + (ndr_push_flags_fn_t)ndr_push_dom_sid), + NDR_ERR_SUCCESS, "unable to push SID"); + + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, mackey, hash_len, EVP_sha1(), NULL); + /* SID field */ + HMAC_Update(&ctx, sid_blob.data, sid_blob.length); + /* Secret field */ + HMAC_Update(&ctx, rc4payload.secret_data.data, rc4payload.secret_data.length); + HMAC_Final(&ctx, mac, &hash_len); + HMAC_CTX_cleanup(&ctx); + + torture_assert_mem_equal(tctx, mac, rc4payload.mac, sizeof(mac), "mac not correct"); + torture_assert_int_equal(tctx, rc4payload.secret_data.length, + plaintext.length, "decrypted data is not correct length"); + torture_assert_mem_equal(tctx, rc4payload.secret_data.data, + plaintext.data, plaintext.length, + "decrypted data is not correct"); + + /* Not strictly correct all the time, but good enough for this test */ + caller_sid = get_user_sid(tctx, tctx, cli_credentials_get_username(cmdline_credentials)); + + torture_assert_sid_equal(tctx, &rc4payload.sid, caller_sid, "Secret saved with wrong SID"); + + + /* RE-encrypt */ + + if (wrong == WRONG_SID) { + rc4payload.sid.sub_auths[rc4payload.sid.num_auths - 1] = DOMAIN_RID_KRBTGT; + } + + dump_data_pw("mackey: \n", mackey, sizeof(mackey)); + + torture_assert_ndr_err_equal(tctx, + ndr_push_struct_blob(&sid_blob, tctx, &rc4payload.sid, + (ndr_push_flags_fn_t)ndr_push_dom_sid), + NDR_ERR_SUCCESS, + "push of sid failed"); + + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, mackey, 20, EVP_sha1(), NULL); + /* SID field */ + HMAC_Update(&ctx, sid_blob.data, sid_blob.length); + /* Secret field */ + HMAC_Update(&ctx, rc4payload.secret_data.data, rc4payload.secret_data.length); + HMAC_Final(&ctx, rc4payload.mac, &hash_len); + HMAC_CTX_cleanup(&ctx); + + dump_data_pw("rc4payload.mac: \n", rc4payload.mac, sizeof(rc4payload.mac)); + + torture_assert_ndr_err_equal(tctx, + ndr_push_struct_blob(&encrypted_blob, tctx, &rc4payload, + (ndr_push_flags_fn_t)ndr_push_bkrp_rc4encryptedpayload), + NDR_ERR_SUCCESS, + "push of rc4payload failed"); + + if (wrong == WRONG_KEY) { + symkey_blob.data[0] = 78; + symkey_blob.data[1] = 78; + symkey_blob.data[2] = 78; + } + + /* rc4 encrypt sid and secret using sym key */ + arcfour_crypt_blob(encrypted_blob.data, encrypted_blob.length, &symkey_blob); + + /* re-create server wrap structure */ + + torture_assert_int_equal(tctx, encrypted_blob.length, + server_side_wrapped->ciphertext_length, + "expected encrypted length not to change"); + if (wrong == RIGHT_KEY) { + torture_assert_mem_equal(tctx, server_side_wrapped->rc4encryptedpayload, + encrypted_blob.data, + encrypted_blob.length, + "expected encrypted data not to change"); + } + + server_side_wrapped->payload_length = rc4payload.secret_data.length; + server_side_wrapped->ciphertext_length = encrypted_blob.length; + server_side_wrapped->rc4encryptedpayload = encrypted_blob.data; + + return true; +} + static bool test_ServerWrap_decrypt_wrong_stuff(struct torture_context *tctx, struct dcerpc_pipe *p, @@ -1627,6 +1869,15 @@ static bool test_ServerWrap_decrypt_wrong_stuff(struct torture_context *tctx, */ SIVAL(encrypted.data, 8, 0); /* valid values are 1-3 */ break; + + case RIGHT_KEY: + case WRONG_KEY: + case WRONG_SID: + torture_assert(tctx, + test_ServerWrap_encrypt_decrypt_manual(tctx, &server_side_wrapped, wrong), + "test_ServerWrap_encrypt_decrypt_manual failed"); + repush = true; + break; } if (repush) { @@ -1649,11 +1900,23 @@ static bool test_ServerWrap_decrypt_wrong_stuff(struct torture_context *tctx, torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, &r), "decrypt"); - if (wrong == WRONG_R2 && W_ERROR_EQUAL(r.out.result, WERR_INVALID_SID)) { + + if ((wrong == WRONG_R2 || wrong == WRONG_KEY) + && W_ERROR_EQUAL(r.out.result, WERR_INVALID_SID)) { torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_SID, "decrypt should fail with WERR_INVALID_SID or WERR_INVALID_PARAM"); + } else if (wrong == RIGHT_KEY) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_OK, + "decrypt should succeed!"); + } else if (wrong == WRONG_SID) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_ACCESS, + "decrypt should fail with WERR_INVALID_ACCESS"); } else { torture_assert_werr_equal(tctx, r.out.result, @@ -1675,11 +1938,23 @@ static bool test_ServerWrap_decrypt_wrong_stuff(struct torture_context *tctx, torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, &r), "decrypt"); - if (wrong == WRONG_R2 && W_ERROR_EQUAL(r.out.result, WERR_INVALID_SID)) { + + if ((wrong == WRONG_R2 || wrong == WRONG_KEY) + && W_ERROR_EQUAL(r.out.result, WERR_INVALID_SID)) { torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_SID, "decrypt should fail with WERR_INVALID_SID or WERR_INVALID_PARAM"); + } else if (wrong == RIGHT_KEY) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_OK, + "decrypt should succeed!"); + } else if (wrong == WRONG_SID) { + torture_assert_werr_equal(tctx, + r.out.result, + WERR_INVALID_ACCESS, + "decrypt should fail with WERR_INVALID_ACCESS"); } else { torture_assert_werr_equal(tctx, r.out.result, @@ -1733,11 +2008,29 @@ static bool test_ServerWrap_decrypt_short_ciphertext_length(struct torture_conte } static bool test_ServerWrap_decrypt_zero_ciphertext_length(struct torture_context *tctx, - struct dcerpc_pipe *p) + struct dcerpc_pipe *p) { return test_ServerWrap_decrypt_wrong_stuff(tctx, p, ZERO_CIPHERTEXT_LENGTH); } +static bool test_ServerWrap_encrypt_decrypt_remote_key(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, RIGHT_KEY); +} + +static bool test_ServerWrap_encrypt_decrypt_wrong_key(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_KEY); +} + +static bool test_ServerWrap_encrypt_decrypt_wrong_sid(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_SID); +} + struct torture_suite *torture_rpc_backupkey(TALLOC_CTX *mem_ctx) { struct torture_rpc_tcase *tcase; @@ -1822,5 +2115,15 @@ struct torture_suite *torture_rpc_backupkey(TALLOC_CTX *mem_ctx) torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_zero_ciphertext_length", test_ServerWrap_decrypt_zero_ciphertext_length); + + torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt_remote_key", + test_ServerWrap_encrypt_decrypt_remote_key); + + torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt_wrong_key", + test_ServerWrap_encrypt_decrypt_wrong_key); + + torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt_wrong_sid", + test_ServerWrap_encrypt_decrypt_wrong_sid); + return suite; } -- 2.1.4 From cfa6d70a50d1097d8d71a07ae9cf79969a468d71 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Fri, 13 Feb 2015 16:49:58 +1300 Subject: [PATCH 29/32] build: Require GnuTLS if building with Active Directory Without GnuTLS, we don't have ldaps:// support and we are unable to readily create RSA keys of the correct length for the BackupKey protocol. Signed-off-by: Garming Sam Reviewed-by: Andrew Bartlett (cherry picked from commit a1f1db277a2c452b63b9fe2d67cabfe0df60223d) --- source4/lib/tls/wscript | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source4/lib/tls/wscript b/source4/lib/tls/wscript index 57cd894..ae96395 100644 --- a/source4/lib/tls/wscript +++ b/source4/lib/tls/wscript @@ -25,6 +25,9 @@ def configure(conf): if 'HAVE_GNUTLS' in conf.env: conf.DEFINE('ENABLE_GNUTLS', 1) + else: + if 'AD_DC_BUILD_IS_ENABLED' in conf.env: + conf.fatal("Building the AD DC requires GnuTLS (eg libgnutls-dev, gnutls-devel) for ldaps:// support and for the BackupKey protocol") conf.CHECK_FUNCS_IN('gnutls_global_init', 'gnutls', headers='gnutls/gnutls.h') -- 2.1.4 From a8f47ab4378cde305e80d6e939cf0eac69d8780a Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Fri, 13 Feb 2015 09:54:50 +1300 Subject: [PATCH 30/32] backupkey: replace heimdal rsa key generation with GnuTLS We use GnuTLS because it can reliably generate 2048 bit keys every time. Windows clients strictly require 2048, no more since it won't fit and no less either. Heimdal would almost always generate a smaller key. Signed-off-by: Garming Sam Reviewed-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=10980 (cherry picked from commit 43d3e90418b5e0ac5986e08f9483146f4f5d2357) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 126 +++++++++++++++--------- 1 file changed, 82 insertions(+), 44 deletions(-) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index 5a2e8a4..ae4c871 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -41,6 +41,12 @@ #include "../libcli/security/security.h" #include "librpc/gen_ndr/ndr_security.h" #include "lib/crypto/arcfour.h" +#include +#include +#if HAVE_GCRYPT_H +#include +#endif + static const unsigned rsa_with_var_num[] = { 1, 2, 840, 113549, 1, 1, 1 }; /* Equivalent to asn1_oid_id_pkcs1_rsaEncryption*/ @@ -775,62 +781,67 @@ static WERROR bkrp_client_wrap_decrypt_data(struct dcesrv_call_state *dce_call, return WERR_OK; } +/* + * Strictly, this function no longer uses Heimdal in order to generate an RSA + * key, but GnuTLS. + * + * The resulting key is then imported into Heimdal's RSA structure. + * + * We use GnuTLS because it can reliably generate 2048 bit keys every time. + * Windows clients strictly require 2048, no more since it won't fit and no + * less either. Heimdal would almost always generate a smaller key. + */ static WERROR create_heimdal_rsa_key(TALLOC_CTX *ctx, hx509_context *hctx, - hx509_private_key *pk, RSA **_rsa) + hx509_private_key *pk, RSA **rsa) { - BIGNUM *pub_expo; - RSA *rsa; int ret; - uint8_t *p0, *p; + uint8_t *p0 = NULL; + const uint8_t *p; size_t len; int bits = 2048; int RSA_returned_bits; + gnutls_x509_privkey gtls_key; + WERROR werr; - *_rsa = NULL; + *rsa = NULL; - pub_expo = BN_new(); - if(pub_expo == NULL) { + gnutls_global_init(); +#ifdef HAVE_GCRYPT_H + DEBUG(3,("Enabling QUICK mode in gcrypt\n")); + gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0); +#endif + ret = gnutls_x509_privkey_init(>ls_key); + if (ret != 0) { + gnutls_global_deinit(); return WERR_INTERNAL_ERROR; } - /* set the public expo to 65537 like everyone */ - BN_set_word(pub_expo, 0x10001); - - rsa = RSA_new(); - if(rsa == NULL) { - BN_free(pub_expo); - return WERR_INTERNAL_ERROR; + ret = gnutls_x509_privkey_generate(gtls_key, GNUTLS_PK_RSA, bits, 0); + if (ret != 0) { + werr = WERR_INTERNAL_ERROR; + goto done; } - while (RSA_returned_bits != bits) { - ret = RSA_generate_key_ex(rsa, bits, pub_expo, NULL); - if(ret != 1) { - RSA_free(rsa); - BN_free(pub_expo); - return WERR_INTERNAL_ERROR; - } - RSA_returned_bits = BN_num_bits(rsa->n); - DEBUG(6, ("RSA_generate_key_ex returned %d Bits\n", RSA_returned_bits)); - } - BN_free(pub_expo); + /* No need to check error code, this SHOULD fail */ + gnutls_x509_privkey_export(gtls_key, GNUTLS_X509_FMT_DER, NULL, &len); - len = i2d_RSAPrivateKey(rsa, NULL); if (len < 1) { - RSA_free(rsa); - return WERR_INTERNAL_ERROR; + werr = WERR_INTERNAL_ERROR; + goto done; } - p0 = p = talloc_array(ctx, uint8_t, len); - if (p == NULL) { - RSA_free(rsa); - return WERR_INTERNAL_ERROR; + p0 = talloc_size(ctx, len); + if (p0 == NULL) { + werr = WERR_NOMEM; + goto done; } + p = p0; - len = i2d_RSAPrivateKey(rsa, &p); - if (len < 1) { - RSA_free(rsa); - talloc_free(p0); - return WERR_INTERNAL_ERROR; + ret = gnutls_x509_privkey_export(gtls_key, GNUTLS_X509_FMT_DER, p0, &len); + + if (ret != 0) { + werr = WERR_INTERNAL_ERROR; + goto done; } /* @@ -839,15 +850,42 @@ static WERROR create_heimdal_rsa_key(TALLOC_CTX *ctx, hx509_context *hctx, */ ret = hx509_parse_private_key(*hctx, &_hx509_signature_rsa_with_var_num , p0, len, HX509_KEY_FORMAT_DER, pk); - memset(p0, 0, len); - talloc_free(p0); - if (ret !=0) { - RSA_free(rsa); - return WERR_INTERNAL_ERROR; + + if (ret != 0) { + werr = WERR_INTERNAL_ERROR; + goto done; } - *_rsa = rsa; - return WERR_OK; + *rsa = d2i_RSAPrivateKey(NULL, &p, len); + TALLOC_FREE(p0); + + if (*rsa == NULL) { + hx509_private_key_free(pk); + werr = WERR_INTERNAL_ERROR; + goto done; + } + + RSA_returned_bits = BN_num_bits((*rsa)->n); + DEBUG(6, ("GnuTLS returned an RSA private key with %d bits\n", RSA_returned_bits)); + + if (RSA_returned_bits != bits) { + DEBUG(0, ("GnuTLS unexpectedly returned an RSA private key with %d bits, needed %d\n", RSA_returned_bits, bits)); + hx509_private_key_free(pk); + werr = WERR_INTERNAL_ERROR; + goto done; + } + + werr = WERR_OK; + +done: + if (p0 != NULL) { + memset(p0, 0, len); + TALLOC_FREE(p0); + } + + gnutls_x509_privkey_deinit(gtls_key); + gnutls_global_deinit(); + return werr; } static WERROR self_sign_cert(TALLOC_CTX *ctx, hx509_context *hctx, hx509_request *req, -- 2.1.4 From df6ee4c808942a810a3f29b7bf878d45e87f16d9 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Fri, 13 Feb 2015 16:55:07 +1300 Subject: [PATCH 31/32] torture-backupkey: Check the dcerpc call return code before calling ndr pull Signed-off-by: Garming Sam Reviewed-by: Andrew Bartlett (cherry picked from commit 5ca9a4ebe53fd225e2491a4da4635468fef60829) --- source4/torture/rpc/backupkey.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source4/torture/rpc/backupkey.c b/source4/torture/rpc/backupkey.c index 53caf74..1a57bd2 100644 --- a/source4/torture/rpc/backupkey.c +++ b/source4/torture/rpc/backupkey.c @@ -894,7 +894,6 @@ static bool test_RestoreGUID_v3(struct torture_context *tctx, static bool test_RestoreGUID(struct torture_context *tctx, struct dcerpc_pipe *p) { - enum ndr_err_code ndr_err; struct dcerpc_binding_handle *b = p->binding_handle; DATA_BLOB out_blob; struct bkrp_client_side_unwrapped resp; @@ -909,9 +908,12 @@ static bool test_RestoreGUID(struct torture_context *tctx, torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed"); torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID"); out_blob.length = *r->out.data_out_len; - ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped); - torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 1, "Unable to unmarshall bkrp_client_side_unwrapped"); torture_assert_werr_equal(tctx, r->out.result, WERR_OK, "Restore GUID"); + torture_assert_ndr_err_equal(tctx, + ndr_pull_struct_blob(&out_blob, tctx, &resp, + (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped), + NDR_ERR_SUCCESS, + "Unable to unmarshall bkrp_client_side_unwrapped"); torture_assert_str_equal(tctx, (char*)resp.secret.data, secret, "Wrong secret"); } else { struct bkrp_BackupKey *r = createRetreiveBackupKeyGUIDStruct(tctx, p, 2, &out_blob); -- 2.1.4 From 7fc1ada697247af0487ef7fb7fe4320e33056551 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 16 Feb 2015 11:26:37 +1300 Subject: [PATCH 32/32] backupkey: Explain more why we use GnuTLS here Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit 927ea9791e3d1a91516b1cec6918772da83a7fbb) --- source4/rpc_server/backupkey/dcesrv_backupkey.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c index ae4c871..bef4c93 100644 --- a/source4/rpc_server/backupkey/dcesrv_backupkey.c +++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c @@ -816,6 +816,13 @@ static WERROR create_heimdal_rsa_key(TALLOC_CTX *ctx, hx509_context *hctx, return WERR_INTERNAL_ERROR; } + /* + * Unlike Heimdal's RSA_generate_key_ex(), this generates a + * 2048 bit key 100% of the time. The heimdal code had a ~1/8 + * chance of doing so, chewing vast quantities of computation + * and entropy in the process. + */ + ret = gnutls_x509_privkey_generate(gtls_key, GNUTLS_PK_RSA, bits, 0); if (ret != 0) { werr = WERR_INTERNAL_ERROR; @@ -837,6 +844,19 @@ static WERROR create_heimdal_rsa_key(TALLOC_CTX *ctx, hx509_context *hctx, } p = p0; + /* + * Only this GnuTLS export function correctly exports the key, + * we can't use gnutls_rsa_params_export_raw() because while + * it appears to be fixed in more recent versions, in the + * Ubuntu 14.04 version 2.12.23 (at least) it incorrectly + * exports one of the key parameters (qInv). Additionally, we + * would have to work around subtle differences in big number + * representations. + * + * We need access to the RSA parameters directly (in the + * parameter RSA **rsa) as the caller has to manually encode + * them in a non-standard data structure. + */ ret = gnutls_x509_privkey_export(gtls_key, GNUTLS_X509_FMT_DER, p0, &len); if (ret != 0) { -- 2.1.4