From b51fa78974d2e57639ec5e8afbe98b57552a10f6 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 17:39:51 +0200 Subject: [PATCH 01/31] 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 --- 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); -- 1.9.1 From 4102ac0c1dd0c1c37df2b5553839041896905ec3 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 17:59:29 +0200 Subject: [PATCH 02/31] 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 --- 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); -- 1.9.1 From ea7d893a013211500dac16e856d7e4e9b311061a Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:12:47 +0200 Subject: [PATCH 03/31] s4-backupkey: check for talloc failure Check for talloc_memdup failure for uniqueid.data. Signed-off-by: Arvid Requate --- 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 -- 1.9.1 From b0143130bd4beb9399af441cf243b9e18d4e6691 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:15:37 +0200 Subject: [PATCH 04/31] s4-backupkey: de-duplicate error handling Signed-off-by: Arvid Requate --- 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, -- 1.9.1 From 80411a8114f589e64c0b50a6aafc43345906f87d Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:18:30 +0200 Subject: [PATCH 05/31] 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 --- 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; -- 1.9.1 From 0c6569f45e50d87c55161432f43f07ab1485c501 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:25:29 +0200 Subject: [PATCH 06/31] 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 --- 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); -- 1.9.1 From e960ba0fe99684389d11dbe4203952605e8de90a Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:36:49 +0200 Subject: [PATCH 07/31] 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 --- 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, -- 1.9.1 From 5bcfd42354bf5c019846b675636a2f4a9f87ac57 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:43:05 +0200 Subject: [PATCH 08/31] 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 --- 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; /******************/ -- 1.9.1 From d634b4d1198890188b4a4a541b214c184863b791 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:48:41 +0200 Subject: [PATCH 09/31] 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 --- 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 */ -- 1.9.1 From c80e06d230c27c7425c0db4f515263394902de8e Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Mon, 7 Jul 2014 18:56:39 +0200 Subject: [PATCH 10/31] s4-backupkey: typo fix Signed-off-by: Arvid Requate --- 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), -- 1.9.1 From f178db0918bc5d009fdd14bb1b808efd5a3e133a Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Tue, 8 Jul 2014 16:12:13 +0200 Subject: [PATCH 11/31] s4-backupkey: improve variable name Signed-off-by: Arvid Requate --- 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); -- 1.9.1 From 84f4aff19b8062aeb74399195da7cf63616303c5 Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Tue, 8 Jul 2014 17:25:53 +0200 Subject: [PATCH 12/31] s4-backupkey: consistent naming of werr variable Signed-off-by: Arvid Requate --- 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; -- 1.9.1 From 9bb87623e93303995876bacadf2a4cd844d60eed Mon Sep 17 00:00:00 2001 From: Arvid Requate Date: Tue, 23 Dec 2014 18:56:20 +0100 Subject: [PATCH 13/31] s4:torture/rpc/backupkey: Require 2048 bit RSA key Signed-off-by: Arvid Requate (fixed cleanup of memory) Signed-off-by: Andrew Bartlett --- 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; } -- 1.9.1 From 120a8c21aa47876cbf1aa9e34bce889d67336e5d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Feb 2015 09:51:27 +1300 Subject: [PATCH 14/31] torture-backupkey: Add consistent assertions that createRestoreGUIDStruct() suceeds Signed-off-by: Andrew Bartlett --- 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) { -- 1.9.1 From 5fed83c4827869ed0bdfa16bd32796f4464ab21c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Feb 2015 11:45:45 +1300 Subject: [PATCH 15/31] torture-backupkey: Assert dcerpc_bkrp_BackupKey_r call was successful Signed-off-by: Andrew Bartlett --- 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); -- 1.9.1 From dae5410c0aeecb92d226c0b08b922cc67bc94e2b Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Thu, 5 Feb 2015 11:07:30 +1300 Subject: [PATCH 16/31] backupkey: begin by factoring out the server wrap functions --- 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 */ -- 1.9.1 From 41a74c0591b4a26df9ce1fb38d89b666c48d3a10 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Thu, 5 Feb 2015 18:17:58 +1300 Subject: [PATCH 17/31] backupkey: Improve IDL --- 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 { -- 1.9.1 From 90728cff6f3e94ab3d43e9441f112b24f3a6c7f8 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 15:48:06 +1300 Subject: [PATCH 18/31] backupkey: Move SID comparison to inside get_and_verify_access_check() Signed-off-by: Andrew Bartlett --- 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 -- 1.9.1 From d1877a6631d17ff547b0a56d28365bc3601ceeb5 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 15:50:15 +1300 Subject: [PATCH 19/31] backupkey: Improve function names and comments for clarity --- 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), -- 1.9.1 From bcc2d179e6446711dc3231842f2c22a613d87f35 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 16:02:00 +1300 Subject: [PATCH 20/31] backupkey: Implement ServerWrap Encrypt protocol BUG: https://bugzilla.samba.org/attachment.cgi?bugid=11097 Signed-off-by: Andrew Bartlett --- 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 */ -- 1.9.1 From d4bc76676114208d13eede67c6260a33376fb5f6 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 16:16:20 +1300 Subject: [PATCH 21/31] 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 --- 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; -- 1.9.1 From ee87aaadb0bbc28b1f035d62faeccaffbac89f68 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 16:23:17 +1300 Subject: [PATCH 22/31] 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 --- 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; } -- 1.9.1 From 731cf24940fd0cc70555c734c27f31458fa7545e Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 10 Feb 2015 16:26:23 +1300 Subject: [PATCH 23/31] 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. --- 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); -- 1.9.1 From e2d223f17c20b53eb01ac35931752425608d3201 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Feb 2015 09:53:58 +1300 Subject: [PATCH 24/31] 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 --- 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); } -- 1.9.1 From 6ba863f6fde89c0f073e9aaa2aa46c044f781762 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Feb 2015 13:37:16 +1300 Subject: [PATCH 25/31] 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 --- 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), -- 1.9.1 From 4fa5d3e6d02e9e6fda74e41813f2d41a1c7f85d9 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Feb 2015 17:46:42 +1300 Subject: [PATCH 26/31] backupkey: Add tests for ServerWrap protocol --- 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; } -- 1.9.1 From b5905f3be4bbcb67a472c877da5cd712314e25a7 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 12 Feb 2015 16:15:41 +1300 Subject: [PATCH 27/31] backupkey: Better handling for different wrap version headers --- 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); } -- 1.9.1 From d24b833d277f6cd6889deb8eea1aeff9ffd3ec0d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 13 Feb 2015 12:59:45 +1300 Subject: [PATCH 28/31] 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 --- 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; } -- 1.9.1 From 6a8ee822a2d7e78822813a1876c23f14b8124ba9 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Fri, 13 Feb 2015 16:49:58 +1300 Subject: [PATCH 29/31] 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 --- 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') -- 1.9.1 From 428d8fe2161decc04c0ae37d0372c50450fe9312 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Fri, 13 Feb 2015 09:54:50 +1300 Subject: [PATCH 30/31] 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 --- 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, -- 1.9.1 From 73c3d4c05da31a51213387c8bb377f125898f9f3 Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Fri, 13 Feb 2015 16:55:07 +1300 Subject: [PATCH 31/31] torture-backupkey: Check the dcerpc call return code before calling ndr pull Signed-off-by: Garming Sam --- 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); -- 1.9.1