From 9a0b1978f0890cee0d8ee8508ac039a8937c6431 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 13:29:10 +0200 Subject: [PATCH 01/58] krb5_wrap: add smb_krb5_free_data_contents() compat define (for v4-5) 4.6 and higher have renamed kerberos_free_data_contents() into smb_krb5_free_data_contents() in commit e8632e2af50588dd47dc00fb72e85a398c844622. But here we don't want to backport that commit, while making it easy to backports patches from master. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher --- lib/krb5_wrap/krb5_samba.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/krb5_wrap/krb5_samba.h b/lib/krb5_wrap/krb5_samba.h index 2d31619..2d0f51d 100644 --- a/lib/krb5_wrap/krb5_samba.h +++ b/lib/krb5_wrap/krb5_samba.h @@ -408,4 +408,6 @@ int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx, time_t *tgs_expire, const char *impersonate_princ_s); +#define smb_krb5_free_data_contents(a, b) kerberos_free_data_contents(a, b) + #endif /* _KRB5_SAMBA_H */ -- 1.9.1 From c512c2b57ecbc9262a5554712b247d06784bf499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Deschner?= Date: Thu, 3 Dec 2015 15:24:39 +0100 Subject: [PATCH 02/58] werror: replace WERR_SETUP_NOT_JOINED with WERR_NERR_SETUPNOTJOINED in source3/libnet/libnet_join.c Guenther Signed-off-by: Guenther Deschner Reviewed-by: Jeremy Allison BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 (cherry picked from commit 3bb394f3d62aaeda5c71cf1d508a7b67fd6e742d) --- source3/libnet/libnet_join.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 8f8baff..0eee286 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -1692,7 +1692,7 @@ static WERROR libnet_join_post_verify(TALLOC_CTX *mem_ctx, libnet_join_set_error_string(mem_ctx, r, "failed to verify domain membership after joining: %s", get_friendly_nt_error_msg(status)); - return WERR_SETUP_NOT_JOINED; + return WERR_NERR_SETUPNOTJOINED; } return WERR_OK; @@ -2596,7 +2596,7 @@ static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx, } if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) { - werr = WERR_SETUP_NOT_JOINED; + werr = WERR_NERR_SETUPNOTJOINED; goto done; } @@ -2700,7 +2700,7 @@ static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx, if (!secrets_fetch_domain_sid(lp_workgroup(), &sid)) { libnet_unjoin_set_error_string(mem_ctx, r, "Unable to fetch domain sid: are we joined?"); - return WERR_SETUP_NOT_JOINED; + return WERR_NERR_SETUPNOTJOINED; } r->in.domain_sid = dom_sid_dup(mem_ctx, &sid); W_ERROR_HAVE_NO_MEMORY(r->in.domain_sid); @@ -2775,7 +2775,7 @@ static WERROR libnet_DomainUnjoin(TALLOC_CTX *mem_ctx, "failed to disable machine account via rpc: %s", get_friendly_nt_error_msg(status)); if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) { - return WERR_SETUP_NOT_JOINED; + return WERR_NERR_SETUPNOTJOINED; } return ntstatus_to_werror(status); } -- 1.9.1 From 4d6221d1a2c67f19f81a4bcff5f1dd53a0438af1 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 12 Jun 2017 18:58:49 +0200 Subject: [PATCH 03/58] pidl:NDR/Parser: add missing {start,end}_flags() to ParseElementPrint() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 81bbfb010599b65308aca89cc50532372ca4cb00) --- pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm b/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm index 94f4855..044e1d1 100644 --- a/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm +++ b/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm @@ -839,8 +839,10 @@ sub ParseElementPrint($$$$$) my $cur_depth = 0; my $ignore_depth = 0xFFFF; + $self->start_flags($e, $ndr); if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { $self->pidl("ndr_print_$e->{REPRESENTATION_TYPE}($ndr, \"$e->{NAME}\", $var_name);"); + $self->end_flags($e, $ndr); return; } @@ -935,6 +937,8 @@ sub ParseElementPrint($$$$$) $self->pidl("$ndr->depth--;"); } } + + $self->end_flags($e, $ndr); } ##################################################################### -- 1.9.1 From c2f56a2692aa54294dd3a5361687fb715a37c89a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 12 Jun 2017 15:22:42 +0200 Subject: [PATCH 04/58] librpc/ndr: align the definition of LIBNDR_STRING_FLAGS with currently defined flags The range included the unused (1<<14) before. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 91d8272e8604b5d87bcc0ce365b553bc760c8ed3) --- librpc/ndr/libndr.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/librpc/ndr/libndr.h b/librpc/ndr/libndr.h index 0c3c55a..db13c05 100644 --- a/librpc/ndr/libndr.h +++ b/librpc/ndr/libndr.h @@ -124,7 +124,19 @@ struct ndr_print { #define LIBNDR_FLAG_STR_CHARLEN (1<<11) #define LIBNDR_FLAG_STR_UTF8 (1<<12) #define LIBNDR_FLAG_STR_RAW8 (1<<13) -#define LIBNDR_STRING_FLAGS (0x7FFC) +#define LIBNDR_STRING_FLAGS (0 | \ + LIBNDR_FLAG_STR_ASCII | \ + LIBNDR_FLAG_STR_LEN4 | \ + LIBNDR_FLAG_STR_SIZE4 | \ + LIBNDR_FLAG_STR_NOTERM | \ + LIBNDR_FLAG_STR_NULLTERM | \ + LIBNDR_FLAG_STR_SIZE2 | \ + LIBNDR_FLAG_STR_BYTESIZE | \ + LIBNDR_FLAG_STR_CONFORMANT | \ + LIBNDR_FLAG_STR_CHARLEN | \ + LIBNDR_FLAG_STR_UTF8 | \ + LIBNDR_FLAG_STR_RAW8 | \ + 0) /* Disable string token compression */ #define LIBNDR_FLAG_NO_COMPRESSION (1<<15) -- 1.9.1 From 09bda4115925bebd1d7e745c1427f4b6f52b41c0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 12 Jun 2017 17:58:20 +0200 Subject: [PATCH 05/58] librpc/ndr: add LIBNDR_FLAG_IS_SECRET handling BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 32aa3a199dfd61eb5982e158008964b4747599b8) --- librpc/ndr/libndr.h | 10 ++++++++++ librpc/ndr/ndr.c | 23 +++++++++++++++++++++++ librpc/ndr/ndr_basic.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/librpc/ndr/libndr.h b/librpc/ndr/libndr.h index db13c05..38e4313 100644 --- a/librpc/ndr/libndr.h +++ b/librpc/ndr/libndr.h @@ -108,6 +108,7 @@ struct ndr_print { void (*print)(struct ndr_print *, const char *, ...) PRINTF_ATTRIBUTE(2,3); void *private_data; bool no_newline; + bool print_secrets; }; #define LIBNDR_FLAG_BIGENDIAN (1<<0) @@ -138,6 +139,12 @@ struct ndr_print { LIBNDR_FLAG_STR_RAW8 | \ 0) +/* + * Mark an element as SECRET, it won't be printed by + * via ndr_print* unless NDR_PRINT_SECRETS is specified. + */ +#define LIBNDR_FLAG_IS_SECRET (1<<14) + /* Disable string token compression */ #define LIBNDR_FLAG_NO_COMPRESSION (1<<15) @@ -209,6 +216,9 @@ struct ndr_print { #define NDR_PRINT_OUT_STRING(ctx, type, p) NDR_PRINT_FUNCTION_STRING(ctx, type, NDR_OUT, p) #define NDR_PRINT_IN_STRING(ctx, type, p) NDR_PRINT_FUNCTION_STRING(ctx, type, NDR_IN | NDR_SET_VALUES, p) +#define NDR_HIDE_SECRET(ndr) \ + (unlikely(((ndr)->flags & LIBNDR_FLAG_IS_SECRET) && !(ndr)->print_secrets)) + #define NDR_BE(ndr) (unlikely(((ndr)->flags & (LIBNDR_FLAG_BIGENDIAN|LIBNDR_FLAG_LITTLE_ENDIAN)) == LIBNDR_FLAG_BIGENDIAN)) enum ndr_err_code { diff --git a/librpc/ndr/ndr.c b/librpc/ndr/ndr.c index 22c4d76..17e4def 100644 --- a/librpc/ndr/ndr.c +++ b/librpc/ndr/ndr.c @@ -399,6 +399,12 @@ _PUBLIC_ void ndr_print_debugc(int dbgc_class, ndr_print_fn_t fn, const char *na ndr->print = ndr_print_debugc_helper; ndr->depth = 1; ndr->flags = 0; +#ifdef DEBUG_PASSWORD + if (CHECK_DEBUGLVL(100)) { + ndr->print_secrets = true; + } +#endif + fn(ndr, name, ptr); talloc_free(ndr); } @@ -417,6 +423,12 @@ _PUBLIC_ void ndr_print_debug(ndr_print_fn_t fn, const char *name, void *ptr) ndr->print = ndr_print_debug_helper; ndr->depth = 1; ndr->flags = 0; +#ifdef DEBUG_PASSWORD + if (CHECK_DEBUGLVL(100)) { + ndr->print_secrets = true; + } +#endif + fn(ndr, name, ptr); talloc_free(ndr); } @@ -435,6 +447,12 @@ _PUBLIC_ void ndr_print_union_debug(ndr_print_fn_t fn, const char *name, uint32_ ndr->print = ndr_print_debug_helper; ndr->depth = 1; ndr->flags = 0; +#ifdef DEBUG_PASSWORD + if (CHECK_DEBUGLVL(100)) { + ndr->print_secrets = true; + } +#endif + ndr_print_set_switch_value(ndr, ptr, level); fn(ndr, name, ptr); talloc_free(ndr); @@ -454,6 +472,11 @@ _PUBLIC_ void ndr_print_function_debug(ndr_print_function_t fn, const char *name ndr->print = ndr_print_debug_helper; ndr->depth = 1; ndr->flags = 0; +#ifdef DEBUG_PASSWORD + if (CHECK_DEBUGLVL(100)) { + ndr->print_secrets = true; + } +#endif fn(ndr, name, flags, ptr); talloc_free(ndr); diff --git a/librpc/ndr/ndr_basic.c b/librpc/ndr/ndr_basic.c index b532cc5..c874f34 100644 --- a/librpc/ndr/ndr_basic.c +++ b/librpc/ndr/ndr_basic.c @@ -1064,41 +1064,73 @@ _PUBLIC_ void ndr_print_bitmap_flag(struct ndr_print *ndr, size_t size, const ch _PUBLIC_ void ndr_print_int8(struct ndr_print *ndr, const char *name, int8_t v) { + if (NDR_HIDE_SECRET(ndr)) { + ndr->print(ndr, "%-25s: ", name); + return; + } ndr->print(ndr, "%-25s: %d", name, v); } _PUBLIC_ void ndr_print_uint8(struct ndr_print *ndr, const char *name, uint8_t v) { + if (NDR_HIDE_SECRET(ndr)) { + ndr->print(ndr, "%-25s: ", name); + return; + } ndr->print(ndr, "%-25s: 0x%02x (%u)", name, v, v); } _PUBLIC_ void ndr_print_int16(struct ndr_print *ndr, const char *name, int16_t v) { + if (NDR_HIDE_SECRET(ndr)) { + ndr->print(ndr, "%-25s: ", name); + return; + } ndr->print(ndr, "%-25s: %d", name, v); } _PUBLIC_ void ndr_print_uint16(struct ndr_print *ndr, const char *name, uint16_t v) { + if (NDR_HIDE_SECRET(ndr)) { + ndr->print(ndr, "%-25s: ", name); + return; + } ndr->print(ndr, "%-25s: 0x%04x (%u)", name, v, v); } _PUBLIC_ void ndr_print_int32(struct ndr_print *ndr, const char *name, int32_t v) { + if (NDR_HIDE_SECRET(ndr)) { + ndr->print(ndr, "%-25s: ", name); + return; + } ndr->print(ndr, "%-25s: %d", name, v); } _PUBLIC_ void ndr_print_uint32(struct ndr_print *ndr, const char *name, uint32_t v) { + if (NDR_HIDE_SECRET(ndr)) { + ndr->print(ndr, "%-25s: ", name); + return; + } ndr->print(ndr, "%-25s: 0x%08x (%u)", name, v, v); } _PUBLIC_ void ndr_print_int3264(struct ndr_print *ndr, const char *name, int32_t v) { + if (NDR_HIDE_SECRET(ndr)) { + ndr->print(ndr, "%-25s: ", name); + return; + } ndr->print(ndr, "%-25s: %d", name, v); } _PUBLIC_ void ndr_print_uint3264(struct ndr_print *ndr, const char *name, uint32_t v) { + if (NDR_HIDE_SECRET(ndr)) { + ndr->print(ndr, "%-25s: ", name); + return; + } ndr->print(ndr, "%-25s: 0x%08x (%u)", name, v, v); } @@ -1114,6 +1146,10 @@ _PUBLIC_ void ndr_print_udlongr(struct ndr_print *ndr, const char *name, uint64_ _PUBLIC_ void ndr_print_dlong(struct ndr_print *ndr, const char *name, int64_t v) { + if (NDR_HIDE_SECRET(ndr)) { + ndr->print(ndr, "%-25s: ", name); + return; + } ndr->print(ndr, "%-25s: 0x%016llx (%lld)", name, (unsigned long long)v, (long long)v); } @@ -1203,6 +1239,11 @@ _PUBLIC_ void ndr_print_array_uint8(struct ndr_print *ndr, const char *name, return; } + if (NDR_HIDE_SECRET(ndr)) { + ndr->print(ndr, "%s: ARRAY(%d): ", name, count); + return; + } + if (count <= _ONELINE_LIMIT && (ndr->flags & LIBNDR_PRINT_ARRAY_HEX)) { char s[(_ONELINE_LIMIT + 1) * 2]; for (i=0;ino_newline = true; dump_data_cb(buf, len, true, ndr_print_dump_data_cb, ndr); ndr->no_newline = false; -- 1.9.1 From 0c617124d3be3a2c49bdf8982f90d2c02f5813b7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 12 Jun 2017 17:58:46 +0200 Subject: [PATCH 06/58] idl_types.h: add NDR_SECRET shortcut BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 969ab12c56cd12dcc0e63e9b662397c1604a0cc0) --- librpc/idl/idl_types.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/librpc/idl/idl_types.h b/librpc/idl/idl_types.h index 72a5d85..2d063de 100644 --- a/librpc/idl/idl_types.h +++ b/librpc/idl/idl_types.h @@ -52,6 +52,12 @@ */ #define NDR_PAHEX LIBNDR_PRINT_ARRAY_HEX +/* + * Mark an element as SECRET, it won't be printed by + * via ndr_print* unless NDR_PRINT_SECRETS is specified. + */ +#define NDR_SECRET LIBNDR_FLAG_IS_SECRET + #define NDR_RELATIVE_REVERSE LIBNDR_FLAG_RELATIVE_REVERSE #define NDR_NO_RELATIVE_REVERSE LIBNDR_FLAG_NO_RELATIVE_REVERSE -- 1.9.1 From e8366ecf7b2fc8d1ca6708fedb9ce0ec5c83435a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 15:05:51 +0200 Subject: [PATCH 07/58] s3:librpc: let NDR_SECRETS depend on NDR_SECURITY BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 4260b52a399667bcdbaa375a20952237ff68449c) --- source3/librpc/wscript_build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/librpc/wscript_build b/source3/librpc/wscript_build index 2445859..1d8c17b 100644 --- a/source3/librpc/wscript_build +++ b/source3/librpc/wscript_build @@ -27,7 +27,7 @@ bld.SAMBA3_SUBSYSTEM('NDR_LEASES_DB', bld.SAMBA3_SUBSYSTEM('NDR_SECRETS', source='gen_ndr/ndr_secrets.c', - public_deps='ndr' + public_deps='ndr NDR_SECURITY' ) bld.SAMBA3_SUBSYSTEM('NDR_PERFCOUNT', -- 1.9.1 From 359062ff34801a570eee8b12dd698b45fd24f896 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 16:13:37 +0200 Subject: [PATCH 08/58] s3:libads: remove unused kerberos_secrets_store_salting_principal() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit c56043a94a10c76a220ce3c7eb7cb8cf2e992cab) --- source3/libads/kerberos.c | 72 ----------------------------------------- source3/libads/kerberos_proto.h | 3 -- 2 files changed, 75 deletions(-) diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c index 2872b00..cd3291a 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -523,78 +523,6 @@ int create_kerberos_key_from_string(krb5_context context, } /************************************************************************ - Routine to set the salting principal for this service. Active - Directory may use a non-obvious principal name to generate the salt - when it determines the key to use for encrypting tickets for a service, - and hopefully we detected that when we joined the domain. - Setting principal to NULL deletes this entry. - ************************************************************************/ - -bool kerberos_secrets_store_salting_principal(const char *service, - int enctype, - const char *principal) -{ - char *key = NULL; - bool ret = False; - krb5_context context = NULL; - krb5_principal princ = NULL; - char *princ_s = NULL; - char *unparsed_name = NULL; - krb5_error_code code; - - if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) { - DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n", - error_message(code))); - return False; - } - if (strchr_m(service, '@')) { - if (asprintf(&princ_s, "%s", service) == -1) { - goto out; - } - } else { - if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) { - goto out; - } - } - - if (smb_krb5_parse_name(context, princ_s, &princ) != 0) { - goto out; - } - if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) { - goto out; - } - - if (asprintf(&key, "%s/%s/enctype=%d", - SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype) - == -1) { - goto out; - } - - if ((principal != NULL) && (strlen(principal) > 0)) { - ret = secrets_store(key, principal, strlen(principal) + 1); - } else { - ret = secrets_delete(key); - } - - out: - - SAFE_FREE(key); - SAFE_FREE(princ_s); - TALLOC_FREE(unparsed_name); - - if (princ) { - krb5_free_principal(context, princ); - } - - if (context) { - krb5_free_context(context); - } - - return ret; -} - - -/************************************************************************ ************************************************************************/ int kerberos_kinit_password(const char *principal, diff --git a/source3/libads/kerberos_proto.h b/source3/libads/kerberos_proto.h index 7c56672..8917d63 100644 --- a/source3/libads/kerberos_proto.h +++ b/source3/libads/kerberos_proto.h @@ -62,9 +62,6 @@ char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context, const char *host_princ_s, int enctype); -bool kerberos_secrets_store_salting_principal(const char *service, - int enctype, - const char *principal); int kerberos_kinit_password(const char *principal, const char *password, int time_offset, -- 1.9.1 From 30a582a635948bf24f371c19754bd9e18f51a002 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 17:13:02 +0200 Subject: [PATCH 09/58] krb5_wrap: add smb_krb5_salt_principal() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 5df46700cfb0a15fec2d366e12728cd497188741) --- lib/krb5_wrap/krb5_samba.c | 120 +++++++++++++++++++++++++++++++++++++++++++++ lib/krb5_wrap/krb5_samba.h | 6 +++ 2 files changed, 126 insertions(+) diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c index 76e8795..38547ec 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -324,6 +324,126 @@ int smb_krb5_get_pw_salt(krb5_context context, #error UNKNOWN_SALT_FUNCTIONS #endif +/** + * @brief This constructs the salt principal used by active directory + * + * Most Kerberos encryption types require a salt in order to + * calculate the long term private key for user/computer object + * based on a password. + * + * The returned _salt_principal is a string in forms like this: + * - host/somehost.example.com@EXAMPLE.COM + * - SomeAccount@EXAMPLE.COM + * - SomePrincipal@EXAMPLE.COM + * + * This is not the form that's used as salt, it's just + * the human readable form. + * + * @param[in] realm The realm the user/computer is added too. + * + * @param[in] sAMAccountName The sAMAccountName attribute of the object. + * + * @param[in] userPrincipalName The userPrincipalName attribute of the object + * or NULL is not available. + * + * @param[in] is_computer The indication of the object includes + * objectClass=computer. + * + * @param[in] mem_ctx The TALLOC_CTX to allocate _salt_principal. + * + * @param[out] _salt_principal The resulting principal as string. + * + * @retval 0 Success; otherwise - Kerberos error codes + */ +int smb_krb5_salt_principal(const char *realm, + const char *sAMAccountName, + const char *userPrincipalName, + bool is_computer, + TALLOC_CTX *mem_ctx, + char **_salt_principal) +{ + TALLOC_CTX *frame = talloc_stackframe(); + char *upper_realm = NULL; + const char *principal = NULL; + int principal_len = 0; + + *_salt_principal = NULL; + + if (sAMAccountName == NULL) { + TALLOC_FREE(frame); + return EINVAL; + } + + if (realm == NULL) { + TALLOC_FREE(frame); + return EINVAL; + } + + upper_realm = strupper_talloc(frame, realm); + if (upper_realm == NULL) { + TALLOC_FREE(frame); + return ENOMEM; + } + + /* Many, many thanks to lukeh@padl.com for this + * algorithm, described in his Nov 10 2004 mail to + * samba-technical@lists.samba.org */ + + /* + * Determine a salting principal + */ + if (is_computer) { + int computer_len = 0; + char *tmp = NULL; + + computer_len = strlen(sAMAccountName); + if (sAMAccountName[computer_len-1] == '$') { + computer_len -= 1; + } + + tmp = talloc_asprintf(frame, "host/%*.*s.%s", + computer_len, computer_len, + sAMAccountName, realm); + if (tmp == NULL) { + TALLOC_FREE(frame); + return ENOMEM; + } + + principal = strlower_talloc(frame, tmp); + TALLOC_FREE(tmp); + if (principal == NULL) { + TALLOC_FREE(frame); + return ENOMEM; + } + principal_len = strlen(principal); + + } else if (userPrincipalName != NULL) { + char *p; + + principal = userPrincipalName; + p = strchr(principal, '@'); + if (p != NULL) { + principal_len = PTR_DIFF(p, principal); + } else { + principal_len = strlen(principal); + } + } else { + principal = sAMAccountName; + principal_len = strlen(principal); + } + + *_salt_principal = talloc_asprintf(mem_ctx, "%*.*s@%s", + principal_len, principal_len, + principal, upper_realm); + if (*_salt_principal == NULL) { + TALLOC_FREE(frame); + return ENOMEM; + } + + TALLOC_FREE(frame); + return 0; +} + #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES) krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes) diff --git a/lib/krb5_wrap/krb5_samba.h b/lib/krb5_wrap/krb5_samba.h index 2d0f51d..31974e0 100644 --- a/lib/krb5_wrap/krb5_samba.h +++ b/lib/krb5_wrap/krb5_samba.h @@ -362,6 +362,12 @@ krb5_error_code ms_suptypes_to_ietf_enctypes(TALLOC_CTX *mem_ctx, int smb_krb5_get_pw_salt(krb5_context context, krb5_const_principal host_princ, krb5_data *psalt); +int smb_krb5_salt_principal(const char *realm, + const char *sAMAccountName, + const char *userPrincipalName, + bool is_computer, + TALLOC_CTX *mem_ctx, + char **_salt_principal); int smb_krb5_create_key_from_string(krb5_context context, krb5_const_principal host_princ, -- 1.9.1 From 15213ef0e3bb1ae133b5ca186f7a73b2468b5eae Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 11:32:46 +0200 Subject: [PATCH 10/58] krb5_wrap: add smb_krb5_salt_principal2data() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit ec2da944d304852d76137e8f9d234462bc807c6b) --- lib/krb5_wrap/krb5_samba.c | 69 +++++++++++++++++++++++++++++++++++++++++++++- lib/krb5_wrap/krb5_samba.h | 4 +++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c index 38547ec..fe29386 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -337,7 +337,8 @@ int smb_krb5_get_pw_salt(krb5_context context, * - SomePrincipal@EXAMPLE.COM * * This is not the form that's used as salt, it's just - * the human readable form. + * the human readable form. It needs to be converted by + * smb_krb5_salt_principal2data(). * * @param[in] realm The realm the user/computer is added too. * @@ -354,6 +355,8 @@ int smb_krb5_get_pw_salt(krb5_context context, * @param[out] _salt_principal The resulting principal as string. * * @retval 0 Success; otherwise - Kerberos error codes + * + * @see smb_krb5_salt_principal2data */ int smb_krb5_salt_principal(const char *realm, const char *sAMAccountName, @@ -444,6 +447,70 @@ int smb_krb5_salt_principal(const char *realm, return 0; } +/** + * @brief Converts the salt principal string into the salt data blob + * + * This function takes a salt_principal as string in forms like this: + * - host/somehost.example.com@EXAMPLE.COM + * - SomeAccount@EXAMPLE.COM + * - SomePrincipal@EXAMPLE.COM + * + * It generates values like: + * - EXAMPLE.COMhost/somehost.example.com + * - EXAMPLE.COMSomeAccount + * - EXAMPLE.COMSomePrincipal + * + * @param[in] realm The realm the user/computer is added too. + * + * @param[in] sAMAccountName The sAMAccountName attribute of the object. + * + * @param[in] userPrincipalName The userPrincipalName attribute of the object + * or NULL is not available. + * + * @param[in] is_computer The indication of the object includes + * objectClass=computer. + * + * @param[in] mem_ctx The TALLOC_CTX to allocate _salt_principal. + * + * @param[out] _salt_principal The resulting principal as string. + * + * @retval 0 Success; otherwise - Kerberos error codes + * + * @see smb_krb5_salt_principal + */ +int smb_krb5_salt_principal2data(krb5_context context, + const char *salt_principal, + TALLOC_CTX *mem_ctx, + char **_salt_data) +{ + krb5_error_code ret; + krb5_principal salt_princ = NULL; + krb5_data salt; + + *_salt_data = NULL; + + ret = krb5_parse_name(context, salt_principal, &salt_princ); + if (ret != 0) { + return ret; + } + + ret = smb_krb5_get_pw_salt(context, salt_princ, &salt); + krb5_free_principal(context, salt_princ); + if (ret != 0) { + return ret; + } + + *_salt_data = talloc_strndup(mem_ctx, + (char *)salt.data, + salt.length); + smb_krb5_free_data_contents(context, &salt); + if (*_salt_data == NULL) { + return ENOMEM; + } + + return 0; +} + #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES) krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes) diff --git a/lib/krb5_wrap/krb5_samba.h b/lib/krb5_wrap/krb5_samba.h index 31974e0..116bffc 100644 --- a/lib/krb5_wrap/krb5_samba.h +++ b/lib/krb5_wrap/krb5_samba.h @@ -368,6 +368,10 @@ int smb_krb5_salt_principal(const char *realm, bool is_computer, TALLOC_CTX *mem_ctx, char **_salt_principal); +int smb_krb5_salt_principal2data(krb5_context context, + const char *salt_principal, + TALLOC_CTX *mem_ctx, + char **_salt_data); int smb_krb5_create_key_from_string(krb5_context context, krb5_const_principal host_princ, -- 1.9.1 From be29075588a54ff313057f299f295e00dc9769d7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 12:42:04 +0200 Subject: [PATCH 11/58] s3:libnet_join: remove dead code from libnet_join_connect_ads() username[strlen(username)] is *always* '\0'! BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 5958c6790fbceb39065353c07fe25f74ddf09ef0) --- source3/libnet/libnet_join.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 0eee286..5089f25 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -195,16 +195,11 @@ static ADS_STATUS libnet_join_connect_ads(TALLOC_CTX *mem_ctx, r->in.machine_password == NULL) { return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); } - username = talloc_strdup(mem_ctx, r->in.machine_name); + username = talloc_asprintf(mem_ctx, "%s$", + r->in.machine_name); if (username == NULL) { return ADS_ERROR(LDAP_NO_MEMORY); } - if (username[strlen(username)] != '$') { - username = talloc_asprintf(username, "%s$", username); - if (username == NULL) { - return ADS_ERROR(LDAP_NO_MEMORY); - } - } password = r->in.machine_password; ccname = "MEMORY:libnet_join_machine_creds"; } else { -- 1.9.1 From 979cf67eae97d395e0b8169b19d6c1b06e89c9fe Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 13:53:19 +0200 Subject: [PATCH 12/58] s3:libnet_join: calculate r->out.account_name in libnet_join_pre_processing() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 826223cc8d36871c2bcb37fe23241f1dbe99a0db) --- source3/libnet/libnet_join.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 5089f25..b16a0c0 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -1122,7 +1122,6 @@ static NTSTATUS libnet_join_joindomain_rpc_unsecure(TALLOC_CTX *mem_ctx, struct rpc_pipe_client *netlogon_pipe = NULL; struct netlogon_creds_cli_context *netlogon_creds = NULL; struct samr_Password current_nt_hash; - const char *account_name = NULL; NTSTATUS status; status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon, @@ -1147,16 +1146,9 @@ static NTSTATUS libnet_join_joindomain_rpc_unsecure(TALLOC_CTX *mem_ctx, /* according to WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED */ E_md4hash(r->in.admin_password, current_nt_hash.hash); - account_name = talloc_asprintf(frame, "%s$", - r->in.machine_name); - if (account_name == NULL) { - TALLOC_FREE(frame); - return NT_STATUS_NO_MEMORY; - } - status = rpccli_create_netlogon_creds(netlogon_pipe->desthost, r->in.domain_name, - account_name, + r->out.account_name, r->in.secure_channel_type, r->in.msg_ctx, frame, @@ -2119,6 +2111,14 @@ static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx, return WERR_INVALID_PARAM; } + r->out.account_name = talloc_asprintf(mem_ctx, "%s$", + r->in.machine_name); + if (r->out.account_name == NULL) { + libnet_join_set_error_string(mem_ctx, r, + "Unable to construct r->out.account_name"); + return WERR_NOT_ENOUGH_MEMORY; + } + if (!libnet_parse_domain_dc(mem_ctx, r->in.domain_name, &r->in.domain_name, &r->in.dc_name)) { -- 1.9.1 From 946d54630a61eb58f6541304d3ad91ead52f8407 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 15:45:22 +0200 Subject: [PATCH 13/58] s3:libnet_join.idl: return the domain_guid in libnet_JoinCtx BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 03e455f5a815ce2134e216dc28929646a964384f) --- source3/librpc/idl/libnet_join.idl | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/librpc/idl/libnet_join.idl b/source3/librpc/idl/libnet_join.idl index 63ea1df..fc7b66c 100644 --- a/source3/librpc/idl/libnet_join.idl +++ b/source3/librpc/idl/libnet_join.idl @@ -49,6 +49,7 @@ interface libnetjoin [out] string dns_domain_name, [out] string forest_name, [out] string dn, + [out] GUID domain_guid, [out] dom_sid *domain_sid, [out] boolean8 modified_config, [out] string error_string, -- 1.9.1 From 6ce1be4e2702379f72af45c4a4811869456cc799 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 15:45:22 +0200 Subject: [PATCH 14/58] s3:libnet_join: remember the domain_guid for AD domains BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit fc2bad0cf34fca5e65fba7e036acf1d8c61f05c0) --- source3/libnet/libnet_join.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index b16a0c0..5010571 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -1080,6 +1080,7 @@ static NTSTATUS libnet_join_lookup_dc_rpc(TALLOC_CTX *mem_ctx, r->out.netbios_domain_name = info->dns.name.string; r->out.dns_domain_name = info->dns.dns_domain.string; r->out.forest_name = info->dns.dns_forest.string; + r->out.domain_guid = info->dns.domain_guid; r->out.domain_sid = dom_sid_dup(mem_ctx, info->dns.sid); NT_STATUS_HAVE_NO_MEMORY(r->out.domain_sid); } -- 1.9.1 From 45816241017895652a6a05a5e764840f05370b0f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:38:26 +0200 Subject: [PATCH 15/58] s3:libnet_join.idl: add krb5_salt to libnet_JoinCtx BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 3b13e4d2d0f73c6374ffdae57528cd1a7f333792) --- source3/librpc/idl/libnet_join.idl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/librpc/idl/libnet_join.idl b/source3/librpc/idl/libnet_join.idl index fc7b66c..e45034d 100644 --- a/source3/librpc/idl/libnet_join.idl +++ b/source3/librpc/idl/libnet_join.idl @@ -54,7 +54,8 @@ interface libnetjoin [out] boolean8 modified_config, [out] string error_string, [out] boolean8 domain_is_ad, - [out] uint32 set_encryption_types + [out] uint32 set_encryption_types, + [out] string krb5_salt ); [nopush,nopull,noopnum] WERROR libnet_UnjoinCtx( -- 1.9.1 From 05af52fa82bd4c5519e4ae90fd154d48f2fb1732 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:40:25 +0200 Subject: [PATCH 16/58] s3:libnet_join: remember r->out.krb5_salt in libnet_join_derive_salting_principal() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 549c9d9a07d3002442cbbb7a90d0a7fef4a92bff) --- source3/libnet/libnet_join.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 5010571..4d5c030 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -864,6 +864,7 @@ static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx, } } + r->out.krb5_salt = salt; return kerberos_secrets_store_des_salt(salt); } -- 1.9.1 From 57f38dbad527d043310b43642f56663690c17e94 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:48:49 +0200 Subject: [PATCH 17/58] s3:libnet_join: move kerberos_secrets_store_des_salt() out of libnet_join_derive_salting_principal() We should separate the calculation and the storing steps. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 0c65d5f41023076fd201c3a179df77dd615cdb01) --- source3/libnet/libnet_join.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 4d5c030..0150b73 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -865,7 +865,7 @@ static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx, } r->out.krb5_salt = salt; - return kerberos_secrets_store_des_salt(salt); + return true; } /**************************************************************** @@ -964,6 +964,17 @@ static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx, return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); } + if (r->out.krb5_salt != NULL) { + bool ok; + + ok = kerberos_secrets_store_des_salt(r->out.krb5_salt); + if (!ok) { + libnet_join_set_error_string(mem_ctx, r, + "failed to store krb5_salt"); + return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); + } + } + if (!libnet_join_create_keytab(mem_ctx, r)) { libnet_join_set_error_string(mem_ctx, r, "failed to create kerberos keytab"); -- 1.9.1 From 146998f4b34d1b957ff8e8cae5443fd488d214cc Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:50:49 +0200 Subject: [PATCH 18/58] s3:libnet_join: split libnet_join_post_processing_ads() into modify/sync BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 0ab7944a2b00df4aa155a239c86f97e4e731b864) --- source3/libnet/libnet_join.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 0150b73..fd1c5b3 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -871,8 +871,8 @@ static bool libnet_join_derive_salting_principal(TALLOC_CTX *mem_ctx, /**************************************************************** ****************************************************************/ -static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx, - struct libnet_JoinCtx *r) +static ADS_STATUS libnet_join_post_processing_ads_modify(TALLOC_CTX *mem_ctx, + struct libnet_JoinCtx *r) { ADS_STATUS status; bool need_etype_update = false; @@ -964,6 +964,12 @@ static ADS_STATUS libnet_join_post_processing_ads(TALLOC_CTX *mem_ctx, return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); } + return ADS_SUCCESS; +} + +static ADS_STATUS libnet_join_post_processing_ads_sync(TALLOC_CTX *mem_ctx, + struct libnet_JoinCtx *r) +{ if (r->out.krb5_salt != NULL) { bool ok; @@ -2212,6 +2218,18 @@ static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx, return WERR_OK; } +#ifdef HAVE_ADS + if (r->out.domain_is_ad && + !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) { + ADS_STATUS ads_status; + + ads_status = libnet_join_post_processing_ads_modify(mem_ctx, r); + if (!ADS_ERR_OK(ads_status)) { + return WERR_GEN_FAILURE; + } + } +#endif /* HAVE_ADS */ + saf_join_store(r->out.netbios_domain_name, r->in.dc_name); if (r->out.dns_domain_name) { saf_join_store(r->out.dns_domain_name, r->in.dc_name); @@ -2222,7 +2240,7 @@ static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx, !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) { ADS_STATUS ads_status; - ads_status = libnet_join_post_processing_ads(mem_ctx, r); + ads_status = libnet_join_post_processing_ads_sync(mem_ctx, r); if (!ADS_ERR_OK(ads_status)) { return WERR_GENERAL_FAILURE; } -- 1.9.1 From 85a39a30fd2399c81e8cd00290d28065ad85f3aa Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:52:59 +0200 Subject: [PATCH 19/58] s3:libnet_join: call do_JoinConfig() after we did remote changes on the server BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 559de1e7236fd4a38f2a1f9980216db95d0430ce) --- source3/libnet/libnet_join.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index fd1c5b3..403b119 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -2209,12 +2209,12 @@ static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx, return r->out.result; } - werr = do_JoinConfig(r); - if (!W_ERROR_IS_OK(werr)) { - return werr; - } - if (!(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE)) { + werr = do_JoinConfig(r); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + return WERR_OK; } @@ -2235,6 +2235,11 @@ static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx, saf_join_store(r->out.dns_domain_name, r->in.dc_name); } + werr = do_JoinConfig(r); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + #ifdef HAVE_ADS if (r->out.domain_is_ad && !(r->in.join_flags & WKSSVC_JOIN_FLAGS_JOIN_UNSECURE)) { -- 1.9.1 From 153bbb0d5e4d468c5861603ceef25c8c70a54ea5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:59:00 +0200 Subject: [PATCH 20/58] s3:libnet_join: move libnet_join_joindomain_store_secrets() to libnet_join_post_processing() We should not store the secrets before we did all remote changes (except the optional dns updates). BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit a922e01baeccedc3ffc8a893f1d6072bb203220f) --- source3/libnet/libnet_join.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 403b119..375e9d7 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -2235,6 +2235,10 @@ static WERROR libnet_join_post_processing(TALLOC_CTX *mem_ctx, saf_join_store(r->out.dns_domain_name, r->in.dc_name); } + if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) { + return WERR_NERR_SETUPNOTJOINED; + } + werr = do_JoinConfig(r); if (!W_ERROR_IS_OK(werr)) { return werr; @@ -2626,11 +2630,6 @@ static WERROR libnet_DomainJoin(TALLOC_CTX *mem_ctx, goto done; } - if (!libnet_join_joindomain_store_secrets(mem_ctx, r)) { - werr = WERR_NERR_SETUPNOTJOINED; - goto done; - } - werr = WERR_OK; done: -- 1.9.1 From 44a61641efdd9d1612ac70851ef4c8d2ff653d25 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 16:02:44 +0200 Subject: [PATCH 21/58] s3:libnet_join: move kerberos_secrets_store_des_salt() to libnet_join_joindomain_store_secrets() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 7d2eea39112fd69d2b710181b23301562efea387) --- source3/libnet/libnet_join.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 375e9d7..9a6ba13 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -970,17 +970,6 @@ static ADS_STATUS libnet_join_post_processing_ads_modify(TALLOC_CTX *mem_ctx, static ADS_STATUS libnet_join_post_processing_ads_sync(TALLOC_CTX *mem_ctx, struct libnet_JoinCtx *r) { - if (r->out.krb5_salt != NULL) { - bool ok; - - ok = kerberos_secrets_store_des_salt(r->out.krb5_salt); - if (!ok) { - libnet_join_set_error_string(mem_ctx, r, - "failed to store krb5_salt"); - return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); - } - } - if (!libnet_join_create_keytab(mem_ctx, r)) { libnet_join_set_error_string(mem_ctx, r, "failed to create kerberos keytab"); @@ -1013,6 +1002,16 @@ static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx, return false; } + if (r->out.krb5_salt != NULL) { + bool ok; + + ok = kerberos_secrets_store_des_salt(r->out.krb5_salt); + if (!ok) { + DEBUG(1,("Failed to save krb5 salt\n")); + return false; + } + } + return true; } -- 1.9.1 From 9328d516003af6ec4bf3a7bee191f5c2a0d3b3f3 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 16:01:55 +0200 Subject: [PATCH 22/58] s3:libads: remove kerberos_secrets_fetch_salting_principal() fallback The handling for per encryption type salts was removed in Samba 3.0.23a (Jul 21, 2006). It's very unlikely that someone has such an installation that got constantly upgraded over 10 years with an automatic password change nor rejoin. It also means that the KDC only has salt-less arcfour-hmac-md5 key together with the salted des keys. So there would only be a problem if the client whould try to use a des key to contact the smb server. Having this legacy code adds quite some complexity for no good reason. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 487b4717b58a6f1ba913708ce8419145b7f4fac8) --- source3/libads/kerberos.c | 37 ++++--------------------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c index cd3291a..690d699 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -362,27 +362,6 @@ int ads_kdestroy(const char *cc_name) } /************************************************************************ - Routine to fetch the salting principal for a service. Active - Directory may use a non-obvious principal name to generate the salt - when it determines the key to use for encrypting tickets for a service, - and hopefully we detected that when we joined the domain. - ************************************************************************/ - -static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype) -{ - char *key = NULL; - char *ret = NULL; - - if (asprintf(&key, "%s/%s/enctype=%d", - SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) { - return NULL; - } - ret = (char *)secrets_fetch(key, NULL); - SAFE_FREE(key); - return ret; -} - -/************************************************************************ Return the standard DES salt key ************************************************************************/ @@ -461,10 +440,8 @@ char* kerberos_secrets_fetch_des_salt( void ) } /************************************************************************ - Routine to get the salting principal for this service. This is - maintained for backwards compatibilty with releases prior to 3.0.24. - Since we store the salting principal string only at join, we may have - to look for the older tdb keys. Caller must free if return is not null. + Routine to get the salting principal for this service. + Caller must free if return is not null. ************************************************************************/ char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context, @@ -476,14 +453,8 @@ char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context, salt_princ_s = kerberos_secrets_fetch_des_salt(); if (salt_princ_s == NULL) { - - /* look under the old key. If this fails, just use the standard key */ - salt_princ_s = kerberos_secrets_fetch_salting_principal(host_princ_s, - enctype); - if (salt_princ_s == NULL) { - /* fall back to host/machine.realm@REALM */ - salt_princ_s = kerberos_standard_des_salt(); - } + /* fall back to host/machine.realm@REALM */ + salt_princ_s = kerberos_standard_des_salt(); } return salt_princ_s; -- 1.9.1 From 5ef7018cd94874524e7455fe4823d3d95a6a8f61 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 16:15:34 +0200 Subject: [PATCH 23/58] s3:libads: provide a simpler kerberos_fetch_salt_princ() function BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 5fe939e32cdaf7bb5b6dac67e7b0118ce65846be) --- source3/libads/kerberos.c | 11 ++++++++--- source3/libads/kerberos_proto.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c index 690d699..7868bf3 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -444,9 +444,7 @@ char* kerberos_secrets_fetch_des_salt( void ) Caller must free if return is not null. ************************************************************************/ -char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context, - const char *host_princ_s, - int enctype) +char *kerberos_secrets_fetch_salt_princ(void) { char *salt_princ_s; /* lookup new key first */ @@ -460,6 +458,13 @@ char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context, return salt_princ_s; } +char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context, + const char *host_princ_s, + int enctype) +{ + return kerberos_secrets_fetch_salt_princ(); +} + int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_principal salt_princ, diff --git a/source3/libads/kerberos_proto.h b/source3/libads/kerberos_proto.h index 8917d63..6a6e269 100644 --- a/source3/libads/kerberos_proto.h +++ b/source3/libads/kerberos_proto.h @@ -61,6 +61,7 @@ bool kerberos_secrets_store_des_salt( const char* salt ); char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context, const char *host_princ_s, int enctype); +char *kerberos_secrets_fetch_salt_princ(void); int kerberos_kinit_password(const char *principal, const char *password, -- 1.9.1 From e596fc39b503965ecc48e034f7336d24c2498204 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 16:28:42 +0200 Subject: [PATCH 24/58] s3:gse_krb5: simplify fill_keytab_from_password() by using kerberos_fetch_salt_princ() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 1d1cf9792f9227e65857c85ff66a961331e3c16e) --- source3/librpc/crypto/gse_krb5.c | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/source3/librpc/crypto/gse_krb5.c b/source3/librpc/crypto/gse_krb5.c index 1667916..c5317aa 100644 --- a/source3/librpc/crypto/gse_krb5.c +++ b/source3/librpc/crypto/gse_krb5.c @@ -122,6 +122,8 @@ static krb5_error_code fill_keytab_from_password(krb5_context krbctx, krb5_enctype *enctypes; krb5_keytab_entry kt_entry; unsigned int i; + krb5_principal salt_princ = NULL; + char *salt_princ_s = NULL; ret = get_kerberos_allowed_etypes(krbctx, &enctypes); if (ret) { @@ -130,11 +132,19 @@ static krb5_error_code fill_keytab_from_password(krb5_context krbctx, return ret; } + salt_princ_s = kerberos_secrets_fetch_salt_princ(); + if (salt_princ_s == NULL) { + ret = ENOMEM; + goto out; + } + ret = krb5_parse_name(krbctx, salt_princ_s, &salt_princ); + SAFE_FREE(salt_princ_s); + if (ret != 0) { + goto out; + } + for (i = 0; enctypes[i]; i++) { krb5_keyblock *key = NULL; - krb5_principal salt_princ = NULL; - char *salt_princ_s; - char *princ_s; int rc; if (!(key = SMB_MALLOC_P(krb5_keyblock))) { @@ -142,28 +152,6 @@ static krb5_error_code fill_keytab_from_password(krb5_context krbctx, goto out; } - ret = krb5_unparse_name(krbctx, princ, &princ_s); - if (ret != 0) { - SAFE_FREE(key); - continue; - } - - salt_princ_s = kerberos_fetch_salt_princ_for_host_princ(krbctx, - princ_s, - enctypes[i]); - SAFE_FREE(princ_s); - if (salt_princ_s == NULL) { - SAFE_FREE(key); - continue; - } - - ret = krb5_parse_name(krbctx, salt_princ_s, &salt_princ); - SAFE_FREE(salt_princ_s); - if (ret != 0) { - SAFE_FREE(key); - continue; - } - rc = create_kerberos_key_from_string(krbctx, princ, salt_princ, @@ -171,7 +159,6 @@ static krb5_error_code fill_keytab_from_password(krb5_context krbctx, key, enctypes[i], false); - krb5_free_principal(krbctx, salt_princ); if (rc != 0) { DEBUG(10, ("Failed to create key for enctype %d " "(error: %s)\n", @@ -199,6 +186,7 @@ static krb5_error_code fill_keytab_from_password(krb5_context krbctx, ret = 0; out: + krb5_free_principal(krbctx, salt_princ); SAFE_FREE(enctypes); return ret; } -- 1.9.1 From ace755acc06a98b774080d1bcbaf8d85be9e86c5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 17:04:36 +0200 Subject: [PATCH 25/58] s3:libnet: make use of kerberos_secrets_fetch_salt_princ() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 51ae7b42d4d52016b39b79447a3e28d473e676cb) --- source3/libnet/libnet_keytab.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source3/libnet/libnet_keytab.c b/source3/libnet/libnet_keytab.c index 2446808..0f6ec49 100644 --- a/source3/libnet/libnet_keytab.c +++ b/source3/libnet/libnet_keytab.c @@ -241,9 +241,7 @@ static krb5_error_code libnet_keytab_add_entry(krb5_context context, keyp = KRB5_KT_KEY(&kt_entry); - salt_princ_s = kerberos_fetch_salt_princ_for_host_princ(context, - princ_s, - enctype); + salt_princ_s = kerberos_secrets_fetch_salt_princ(); if (salt_princ_s == NULL) { ret = KRB5KRB_ERR_GENERIC; goto done; -- 1.9.1 From c05d5f14cdf431fef5c8194508de81bc030952f6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 17:08:24 +0200 Subject: [PATCH 26/58] s3:libads: make use of kerberos_secrets_fetch_salt_princ() in ads_keytab_add_entry() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit b0928a2687a9ffe92ebdce7b5252781d62e7e02d) --- source3/libads/kerberos_keytab.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/source3/libads/kerberos_keytab.c b/source3/libads/kerberos_keytab.c index 160b5839..d4aa910 100644 --- a/source3/libads/kerberos_keytab.c +++ b/source3/libads/kerberos_keytab.c @@ -188,10 +188,14 @@ int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc) goto out; } + salt_princ_s = kerberos_secrets_fetch_salt_princ(); + if (salt_princ_s == NULL) { + DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n"); + ret = -1; + goto out; + } + for (i = 0; enctypes[i]; i++) { - salt_princ_s = kerberos_fetch_salt_princ_for_host_princ(context, - princ_s, - enctypes[i]); /* add the fqdn principal to the keytab */ ret = smb_krb5_kt_add_entry(context, @@ -205,7 +209,6 @@ int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc) false); if (ret) { DEBUG(1, (__location__ ": Failed to add entry to keytab\n")); - SAFE_FREE(salt_princ_s); goto out; } @@ -223,14 +226,13 @@ int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc) if (ret) { DEBUG(1, (__location__ ": Failed to add short entry to keytab\n")); - SAFE_FREE(salt_princ_s); goto out; } } - SAFE_FREE(salt_princ_s); } out: + SAFE_FREE(salt_princ_s); TALLOC_FREE(tmpctx); if (keytab) { -- 1.9.1 From 9a9a6284c0dcc0417f4ddd309ec61cb2f33f96b0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 17:09:20 +0200 Subject: [PATCH 27/58] s3:libads: remove unused kerberos_fetch_salt_princ_for_host_princ() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 1a26805ad9f19f02a52d9eaa4f2f11ff20ee76ac) --- source3/libads/kerberos.c | 7 ------- source3/libads/kerberos_proto.h | 3 --- 2 files changed, 10 deletions(-) diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c index 7868bf3..a7082f2 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -458,13 +458,6 @@ char *kerberos_secrets_fetch_salt_princ(void) return salt_princ_s; } -char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context, - const char *host_princ_s, - int enctype) -{ - return kerberos_secrets_fetch_salt_princ(); -} - int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_principal salt_princ, diff --git a/source3/libads/kerberos_proto.h b/source3/libads/kerberos_proto.h index 6a6e269..e481d1d 100644 --- a/source3/libads/kerberos_proto.h +++ b/source3/libads/kerberos_proto.h @@ -58,9 +58,6 @@ int kerberos_kinit_password_ext(const char *principal, int ads_kdestroy(const char *cc_name); char* kerberos_standard_des_salt( void ); bool kerberos_secrets_store_des_salt( const char* salt ); -char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context, - const char *host_princ_s, - int enctype); char *kerberos_secrets_fetch_salt_princ(void); int kerberos_kinit_password(const char *principal, -- 1.9.1 From 78514d5019a38bd00142a725ebc54be0621732e6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 17:17:00 +0200 Subject: [PATCH 28/58] s3:secrets: move kerberos_secrets_*salt related functions to machine_account_secrets.c These don't use any krb5_context related functions and they just work on secrets.tdb, so they really belong to machine_account_secrets.c. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 504b446d8dc7410ad63eba9d214e9cf271cf3b2f) --- source3/include/secrets.h | 4 ++ source3/libads/kerberos.c | 97 -------------------------------- source3/libads/kerberos_proto.h | 3 - source3/libnet/libnet_keytab.c | 1 + source3/passdb/machine_account_secrets.c | 96 +++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 100 deletions(-) diff --git a/source3/include/secrets.h b/source3/include/secrets.h index f397129..c40a951 100644 --- a/source3/include/secrets.h +++ b/source3/include/secrets.h @@ -133,6 +133,10 @@ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const uint32_t secure_channel, bool delete_join); +char* kerberos_standard_des_salt( void ); +bool kerberos_secrets_store_des_salt( const char* salt ); +char *kerberos_secrets_fetch_salt_princ(void); + /* The following definitions come from passdb/secrets_lsa.c */ NTSTATUS lsa_secret_get(TALLOC_CTX *mem_ctx, const char *secret_name, diff --git a/source3/libads/kerberos.c b/source3/libads/kerberos.c index a7082f2..276ecd8 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -361,103 +361,6 @@ int ads_kdestroy(const char *cc_name) return code; } -/************************************************************************ - Return the standard DES salt key -************************************************************************/ - -char* kerberos_standard_des_salt( void ) -{ - fstring salt; - - fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() ); - (void)strlower_m( salt ); - fstrcat( salt, lp_realm() ); - - return SMB_STRDUP( salt ); -} - -/************************************************************************ -************************************************************************/ - -static char* des_salt_key( void ) -{ - char *key; - - if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, - lp_realm()) == -1) { - return NULL; - } - - return key; -} - -/************************************************************************ -************************************************************************/ - -bool kerberos_secrets_store_des_salt( const char* salt ) -{ - char* key; - bool ret; - - if ( (key = des_salt_key()) == NULL ) { - DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n")); - return False; - } - - if ( !salt ) { - DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n")); - secrets_delete( key ); - return True; - } - - DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt)); - - ret = secrets_store( key, salt, strlen(salt)+1 ); - - SAFE_FREE( key ); - - return ret; -} - -/************************************************************************ -************************************************************************/ - -static -char* kerberos_secrets_fetch_des_salt( void ) -{ - char *salt, *key; - - if ( (key = des_salt_key()) == NULL ) { - DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n")); - return NULL; - } - - salt = (char*)secrets_fetch( key, NULL ); - - SAFE_FREE( key ); - - return salt; -} - -/************************************************************************ - Routine to get the salting principal for this service. - Caller must free if return is not null. - ************************************************************************/ - -char *kerberos_secrets_fetch_salt_princ(void) -{ - char *salt_princ_s; - /* lookup new key first */ - - salt_princ_s = kerberos_secrets_fetch_des_salt(); - if (salt_princ_s == NULL) { - /* fall back to host/machine.realm@REALM */ - salt_princ_s = kerberos_standard_des_salt(); - } - - return salt_princ_s; -} - int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_principal salt_princ, diff --git a/source3/libads/kerberos_proto.h b/source3/libads/kerberos_proto.h index e481d1d..f92cabd 100644 --- a/source3/libads/kerberos_proto.h +++ b/source3/libads/kerberos_proto.h @@ -56,9 +56,6 @@ int kerberos_kinit_password_ext(const char *principal, time_t renewable_time, NTSTATUS *ntstatus); int ads_kdestroy(const char *cc_name); -char* kerberos_standard_des_salt( void ); -bool kerberos_secrets_store_des_salt( const char* salt ); -char *kerberos_secrets_fetch_salt_princ(void); int kerberos_kinit_password(const char *principal, const char *password, diff --git a/source3/libnet/libnet_keytab.c b/source3/libnet/libnet_keytab.c index 0f6ec49..ad7371c 100644 --- a/source3/libnet/libnet_keytab.c +++ b/source3/libnet/libnet_keytab.c @@ -22,6 +22,7 @@ #include "includes.h" #include "smb_krb5.h" #include "ads.h" +#include "secrets.h" #include "libnet/libnet_keytab.h" #ifdef HAVE_KRB5 diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 3f097ab..3f6d6b6 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -553,6 +553,102 @@ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const return ret; } +/************************************************************************ + Return the standard DES salt key +************************************************************************/ + +char* kerberos_standard_des_salt( void ) +{ + fstring salt; + + fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() ); + (void)strlower_m( salt ); + fstrcat( salt, lp_realm() ); + + return SMB_STRDUP( salt ); +} + +/************************************************************************ +************************************************************************/ + +static char* des_salt_key( void ) +{ + char *key; + + if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, + lp_realm()) == -1) { + return NULL; + } + + return key; +} + +/************************************************************************ +************************************************************************/ + +bool kerberos_secrets_store_des_salt( const char* salt ) +{ + char* key; + bool ret; + + if ( (key = des_salt_key()) == NULL ) { + DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n")); + return False; + } + + if ( !salt ) { + DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n")); + secrets_delete( key ); + return True; + } + + DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt)); + + ret = secrets_store( key, salt, strlen(salt)+1 ); + + SAFE_FREE( key ); + + return ret; +} + +/************************************************************************ +************************************************************************/ + +static +char* kerberos_secrets_fetch_des_salt( void ) +{ + char *salt, *key; + + if ( (key = des_salt_key()) == NULL ) { + DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n")); + return NULL; + } + + salt = (char*)secrets_fetch( key, NULL ); + + SAFE_FREE( key ); + + return salt; +} + +/************************************************************************ + Routine to get the salting principal for this service. + Caller must free if return is not null. + ************************************************************************/ + +char *kerberos_secrets_fetch_salt_princ(void) +{ + char *salt_princ_s; + /* lookup new key first */ + + salt_princ_s = kerberos_secrets_fetch_des_salt(); + if (salt_princ_s == NULL) { + /* fall back to host/machine.realm@REALM */ + salt_princ_s = kerberos_standard_des_salt(); + } + + return salt_princ_s; +} /************************************************************************ Routine to fetch the previous plaintext machine account password for a realm -- 1.9.1 From 39472b957e058156ed3a73a2c625ab6ea0423384 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 11:38:12 +0200 Subject: [PATCH 29/58] s3:secrets: rework des_salt_key() to take the realm as argument BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 072dd87e639d7dbfc583ede5ddf6559d9d433b8b) --- source3/passdb/machine_account_secrets.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 3f6d6b6..114bed6 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -571,16 +571,15 @@ char* kerberos_standard_des_salt( void ) /************************************************************************ ************************************************************************/ -static char* des_salt_key( void ) +static char *des_salt_key(const char *realm) { - char *key; - - if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, - lp_realm()) == -1) { - return NULL; - } + char *keystr; - return key; + keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/DES/%s", + SECRETS_SALTING_PRINCIPAL, + realm); + SMB_ASSERT(keystr != NULL); + return keystr; } /************************************************************************ @@ -591,7 +590,8 @@ bool kerberos_secrets_store_des_salt( const char* salt ) char* key; bool ret; - if ( (key = des_salt_key()) == NULL ) { + key = des_salt_key(lp_realm()); + if (key == NULL) { DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n")); return False; } @@ -606,7 +606,7 @@ bool kerberos_secrets_store_des_salt( const char* salt ) ret = secrets_store( key, salt, strlen(salt)+1 ); - SAFE_FREE( key ); + TALLOC_FREE(key); return ret; } @@ -619,14 +619,15 @@ char* kerberos_secrets_fetch_des_salt( void ) { char *salt, *key; - if ( (key = des_salt_key()) == NULL ) { + key = des_salt_key(lp_realm()); + if (key == NULL) { DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n")); return NULL; } salt = (char*)secrets_fetch( key, NULL ); - SAFE_FREE( key ); + TALLOC_FREE(key); return salt; } -- 1.9.1 From b257dd9006d039f9af34b5de73373cf3f4b8b904 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:10:45 +0200 Subject: [PATCH 30/58] s3:secrets: split out a domain_guid_keystr() function BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit d37e30cef7906b7b2b14351ad81d0d884811557b) --- source3/passdb/machine_account_secrets.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 114bed6..060babf 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -51,6 +51,16 @@ static const char *domain_sid_keystr(const char *domain) return keystr; } +static const char *domain_guid_keystr(const char *domain) +{ + char *keystr; + + keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", + SECRETS_DOMAIN_GUID, domain); + SMB_ASSERT(keystr != NULL); + return keystr; +} + static const char *protect_ids_keystr(const char *domain) { char *keystr; @@ -139,7 +149,7 @@ bool secrets_fetch_domain_sid(const char *domain, struct dom_sid *sid) bool secrets_store_domain_guid(const char *domain, struct GUID *guid) { char *protect_ids; - fstring key; + const char *key; protect_ids = secrets_fetch(protect_ids_keystr(domain), NULL); if (protect_ids) { @@ -152,24 +162,18 @@ bool secrets_store_domain_guid(const char *domain, struct GUID *guid) } SAFE_FREE(protect_ids); - slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_GUID, domain); - if (!strupper_m(key)) { - return false; - } + key = domain_guid_keystr(domain); return secrets_store(key, guid, sizeof(struct GUID)); } bool secrets_fetch_domain_guid(const char *domain, struct GUID *guid) { struct GUID *dyn_guid; - fstring key; + const char *key; size_t size = 0; struct GUID new_guid; - slprintf(key, sizeof(key)-1, "%s/%s", SECRETS_DOMAIN_GUID, domain); - if (!strupper_m(key)) { - return false; - } + key = domain_guid_keystr(domain); dyn_guid = (struct GUID *)secrets_fetch(key, &size); if (!dyn_guid) { -- 1.9.1 From 72856baa6d89db3e9dbe1b351be7ef7ebc86207d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 21 Jun 2017 19:38:15 +0200 Subject: [PATCH 31/58] s3:secrets: add some const to secrets_store_domain_guid() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 99013685a1114829579e420df3625ed79eb7ee94) --- source3/include/secrets.h | 2 +- source3/passdb/machine_account_secrets.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source3/include/secrets.h b/source3/include/secrets.h index c40a951..6f74494 100644 --- a/source3/include/secrets.h +++ b/source3/include/secrets.h @@ -95,7 +95,7 @@ bool secrets_mark_domain_protected(const char *domain); bool secrets_clear_domain_protection(const char *domain); bool secrets_store_domain_sid(const char *domain, const struct dom_sid *sid); bool secrets_fetch_domain_sid(const char *domain, struct dom_sid *sid); -bool secrets_store_domain_guid(const char *domain, struct GUID *guid); +bool secrets_store_domain_guid(const char *domain, const struct GUID *guid); bool secrets_fetch_domain_guid(const char *domain, struct GUID *guid); enum netr_SchannelType get_default_sec_channel(void); bool secrets_fetch_trust_account_password_legacy(const char *domain, diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 060babf..7d31734 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -146,7 +146,7 @@ bool secrets_fetch_domain_sid(const char *domain, struct dom_sid *sid) return True; } -bool secrets_store_domain_guid(const char *domain, struct GUID *guid) +bool secrets_store_domain_guid(const char *domain, const struct GUID *guid) { char *protect_ids; const char *key; -- 1.9.1 From fb204e2476e794c24a3b30535219b5379cd5aa02 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:18:33 +0200 Subject: [PATCH 32/58] s3:secrets: make use of des_salt_key() in secrets_store_machine_pw_sync() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 4e37d7805b345d80ca6e8a598e39fc81f72a27ce) --- source3/passdb/machine_account_secrets.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 7d31734..369f774 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -34,6 +34,8 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_PASSDB +static char *des_salt_key(const char *realm); + /** * Form a key for fetching the domain sid * @@ -545,11 +547,7 @@ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const } if (realm && salting_principal) { - char *key = talloc_asprintf(frame, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, realm); - if (!key) { - TALLOC_FREE(frame); - return false; - } + char *key = des_salt_key(realm); ret = secrets_store(key, salting_principal, strlen(salting_principal)+1 ); } -- 1.9.1 From 67949cfc70fdf6f1e66a45036b2166acc6733405 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 20 Jun 2017 13:07:15 +0200 Subject: [PATCH 33/58] s3:secrets: rename secrets_delete() to secrets_delete_entry() secrets_delete_entry() fails if the key doesn't exist. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit cd1e888773c4fd3db63ce38a496fc3d54eb8e021) --- source3/include/secrets.h | 2 +- source3/passdb/machine_account_secrets.c | 16 ++++++++-------- source3/passdb/secrets.c | 6 +++--- source3/passdb/secrets_lsa.c | 2 +- source3/utils/net.c | 6 +++--- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/source3/include/secrets.h b/source3/include/secrets.h index 6f74494..e7f87a9 100644 --- a/source3/include/secrets.h +++ b/source3/include/secrets.h @@ -88,7 +88,7 @@ struct db_context *secrets_db_ctx(void); void secrets_shutdown(void); void *secrets_fetch(const char *key, size_t *size); bool secrets_store(const char *key, const void *data, size_t size); -bool secrets_delete(const char *key); +bool secrets_delete_entry(const char *key); /* The following definitions come from passdb/machine_account_secrets.c */ bool secrets_mark_domain_protected(const char *domain); diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 369f774..7f19c65 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -92,7 +92,7 @@ bool secrets_clear_domain_protection(const char *domain) if (protection) { SAFE_FREE(protection); - ret = secrets_delete(protect_ids_keystr(domain)); + ret = secrets_delete_entry(protect_ids_keystr(domain)); if (!ret) { DEBUG(0, ("Failed to remove Domain IDs protection\n")); } @@ -381,7 +381,7 @@ static bool secrets_delete_prev_machine_password(const char *domain) return true; } SAFE_FREE(oldpass); - return secrets_delete(machine_prev_password_keystr(domain)); + return secrets_delete_entry(machine_prev_password_keystr(domain)); } /************************************************************************ @@ -394,13 +394,13 @@ bool secrets_delete_machine_password_ex(const char *domain) if (!secrets_delete_prev_machine_password(domain)) { return false; } - if (!secrets_delete(machine_password_keystr(domain))) { + if (!secrets_delete_entry(machine_password_keystr(domain))) { return false; } - if (!secrets_delete(machine_sec_channel_type_keystr(domain))) { + if (!secrets_delete_entry(machine_sec_channel_type_keystr(domain))) { return false; } - return secrets_delete(machine_last_change_time_keystr(domain)); + return secrets_delete_entry(machine_last_change_time_keystr(domain)); } /************************************************************************ @@ -409,7 +409,7 @@ bool secrets_delete_machine_password_ex(const char *domain) bool secrets_delete_domain_sid(const char *domain) { - return secrets_delete(domain_sid_keystr(domain)); + return secrets_delete_entry(domain_sid_keystr(domain)); } /************************************************************************ @@ -514,7 +514,7 @@ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const value = secrets_fetch(machine_sec_channel_type_keystr(domain), NULL); if (value) { SAFE_FREE(value); - ret = secrets_delete(machine_sec_channel_type_keystr(domain)); + ret = secrets_delete_entry(machine_sec_channel_type_keystr(domain)); if (!ret) { TALLOC_FREE(frame); return ret; @@ -600,7 +600,7 @@ bool kerberos_secrets_store_des_salt( const char* salt ) if ( !salt ) { DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n")); - secrets_delete( key ); + secrets_delete_entry( key ); return True; } diff --git a/source3/passdb/secrets.c b/source3/passdb/secrets.c index 4372c63..ae00936 100644 --- a/source3/passdb/secrets.c +++ b/source3/passdb/secrets.c @@ -146,7 +146,7 @@ bool secrets_store(const char *key, const void *data, size_t size) /* delete a secets database entry */ -bool secrets_delete(const char *key) +bool secrets_delete_entry(const char *key) { NTSTATUS status; if (!secrets_init()) { @@ -277,7 +277,7 @@ bool secrets_store_trusted_domain_password(const char* domain, const char* pwd, bool trusted_domain_password_delete(const char *domain) { - return secrets_delete(trustdom_keystr(domain)); + return secrets_delete_entry(trustdom_keystr(domain)); } bool secrets_store_ldap_pw(const char* dn, char* pw) @@ -352,7 +352,7 @@ bool fetch_ldap_pw(char **dn, char** pw) SAFE_FREE(*dn); return False; } - if (!secrets_delete(old_style_key)) { + if (!secrets_delete_entry(old_style_key)) { DEBUG(0,("fetch_ldap_pw: old ldap secret could not be deleted!\n")); } diff --git a/source3/passdb/secrets_lsa.c b/source3/passdb/secrets_lsa.c index a40942c..3ebaac4 100644 --- a/source3/passdb/secrets_lsa.c +++ b/source3/passdb/secrets_lsa.c @@ -223,7 +223,7 @@ NTSTATUS lsa_secret_delete(const char *secret_name) return status; } - if (!secrets_delete(key)) { + if (!secrets_delete_entry(key)) { talloc_free(key); return NT_STATUS_ACCESS_DENIED; } diff --git a/source3/utils/net.c b/source3/utils/net.c index 3d0940d..1bf31f2 100644 --- a/source3/utils/net.c +++ b/source3/utils/net.c @@ -156,9 +156,9 @@ static int net_setauthuser(struct net_context *c, int argc, const char **argv) " Delete the auth user setting.\n")); return 1; } - secrets_delete(SECRETS_AUTH_USER); - secrets_delete(SECRETS_AUTH_DOMAIN); - secrets_delete(SECRETS_AUTH_PASSWORD); + secrets_delete_entry(SECRETS_AUTH_USER); + secrets_delete_entry(SECRETS_AUTH_DOMAIN); + secrets_delete_entry(SECRETS_AUTH_PASSWORD); return 0; } -- 1.9.1 From c9c3fb9da8141062dc80002f22d6bb99e265a9f8 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:21:37 +0200 Subject: [PATCH 34/58] s3:secrets: re-add secrets_delete() helper to simplify deleting optional keys BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit fde4af1c329655d7ef3f55727632b3f026a3ea73) --- source3/include/secrets.h | 1 + source3/passdb/secrets.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/source3/include/secrets.h b/source3/include/secrets.h index e7f87a9..548003f 100644 --- a/source3/include/secrets.h +++ b/source3/include/secrets.h @@ -89,6 +89,7 @@ void secrets_shutdown(void); void *secrets_fetch(const char *key, size_t *size); bool secrets_store(const char *key, const void *data, size_t size); bool secrets_delete_entry(const char *key); +bool secrets_delete(const char *key); /* The following definitions come from passdb/machine_account_secrets.c */ bool secrets_mark_domain_protected(const char *domain); diff --git a/source3/passdb/secrets.c b/source3/passdb/secrets.c index ae00936..d843b60 100644 --- a/source3/passdb/secrets.c +++ b/source3/passdb/secrets.c @@ -158,6 +158,25 @@ bool secrets_delete_entry(const char *key) return NT_STATUS_IS_OK(status); } +/* + * Deletes the key if it exists. + */ +bool secrets_delete(const char *key) +{ + bool exists; + + if (!secrets_init()) { + return false; + } + + exists = dbwrap_exists(db_ctx, string_tdb_data(key)); + if (!exists) { + return true; + } + + return secrets_delete_entry(key); +} + /** * Form a key for fetching a trusted domain password * -- 1.9.1 From 22a24cb25e88dd2f4d69a0982e1b8c00a540dabc Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:21:37 +0200 Subject: [PATCH 35/58] s3:secrets: make use of secrets_delete() in secrets_store_machine_pw_sync() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit c5ded1123797b2bd152b0989e24eba7cae6a5792) --- source3/passdb/machine_account_secrets.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 7f19c65..6b89e25 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -478,7 +478,6 @@ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const uint8_t last_change_time_store[4]; TALLOC_CTX *frame = talloc_stackframe(); uint8_t sec_channel_bytes[4]; - void *value; if (delete_join) { secrets_delete_machine_password_ex(domain); @@ -496,11 +495,7 @@ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const if (oldpass) { ret = secrets_store(machine_prev_password_keystr(domain), oldpass, strlen(oldpass)+1); } else { - value = secrets_fetch_prev_machine_password(domain); - if (value) { - SAFE_FREE(value); - ret = secrets_delete_prev_machine_password(domain); - } + ret = secrets_delete(machine_prev_password_keystr(domain)); } if (!ret) { TALLOC_FREE(frame); @@ -511,14 +506,10 @@ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const /* We delete this and instead have the read code fall back to * a default based on server role, as our caller can't specify * this with any more certainty */ - value = secrets_fetch(machine_sec_channel_type_keystr(domain), NULL); - if (value) { - SAFE_FREE(value); - ret = secrets_delete_entry(machine_sec_channel_type_keystr(domain)); - if (!ret) { - TALLOC_FREE(frame); - return ret; - } + ret = secrets_delete(machine_sec_channel_type_keystr(domain)); + if (!ret) { + TALLOC_FREE(frame); + return ret; } } else { SIVAL(&sec_channel_bytes, 0, secure_channel_type); -- 1.9.1 From 88234eef26e4aeeb0f1422d5adae7f025abff93c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:27:45 +0200 Subject: [PATCH 36/58] s3:secrets: let secrets_store_machine_pw_sync() delete the des_salt_key when there's no value BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 45eea321a6faa6db1c9c706a27527cc0766dc831) --- source3/passdb/machine_account_secrets.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 6b89e25..c3a760b 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -537,9 +537,16 @@ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const return ret; } - if (realm && salting_principal) { + if (realm != NULL) { char *key = des_salt_key(realm); - ret = secrets_store(key, salting_principal, strlen(salting_principal)+1 ); + + if (salting_principal != NULL) { + ret = secrets_store(key, + salting_principal, + strlen(salting_principal)+1); + } else { + ret = secrets_delete(key); + } } TALLOC_FREE(frame); -- 1.9.1 From ea91f968c80a8fc259e04211326fbc20c2635c2e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 24 May 2017 06:44:32 +0200 Subject: [PATCH 37/58] s3:secrets: replace secrets_delete_prev_machine_password() by secrets_delete() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 5b95cb74e7b2838d228f9773c0e20982b81d1e7d) --- source3/passdb/machine_account_secrets.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index c3a760b..2457ac7 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -371,27 +371,13 @@ bool secrets_fetch_trust_account_password(const char *domain, uint8_t ret_pwd[16 } /************************************************************************ - Routine to delete the old plaintext machine account password if any -************************************************************************/ - -static bool secrets_delete_prev_machine_password(const char *domain) -{ - char *oldpass = (char *)secrets_fetch(machine_prev_password_keystr(domain), NULL); - if (oldpass == NULL) { - return true; - } - SAFE_FREE(oldpass); - return secrets_delete_entry(machine_prev_password_keystr(domain)); -} - -/************************************************************************ Routine to delete the plaintext machine account password, old password, sec channel type and last change time from secrets database ************************************************************************/ bool secrets_delete_machine_password_ex(const char *domain) { - if (!secrets_delete_prev_machine_password(domain)) { + if (!secrets_delete(machine_prev_password_keystr(domain))) { return false; } if (!secrets_delete_entry(machine_password_keystr(domain))) { -- 1.9.1 From 63bba5af345d56680a0dfed705a76ba528eeccb6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:31:01 +0200 Subject: [PATCH 38/58] s3:secrets: rewrite secrets_delete_machine_password_ex() using helper variables BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 5bc2764fe517748c03a57b61f2f7ef889c92825d) --- source3/passdb/machine_account_secrets.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 2457ac7..56a9442 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -377,16 +377,34 @@ bool secrets_fetch_trust_account_password(const char *domain, uint8_t ret_pwd[16 bool secrets_delete_machine_password_ex(const char *domain) { - if (!secrets_delete(machine_prev_password_keystr(domain))) { + const char *tmpkey = NULL; + bool ok; + + tmpkey = machine_prev_password_keystr(domain); + ok = secrets_delete(tmpkey); + if (!ok) { + return false; + } + + tmpkey = machine_password_keystr(domain); + ok = secrets_delete_entry(tmpkey); + if (!ok) { return false; } - if (!secrets_delete_entry(machine_password_keystr(domain))) { + + tmpkey = machine_sec_channel_type_keystr(domain); + ok = secrets_delete_entry(tmpkey); + if (!ok) { return false; } - if (!secrets_delete_entry(machine_sec_channel_type_keystr(domain))) { + + tmpkey = machine_last_change_time_keystr(domain); + ok = secrets_delete_entry(tmpkey); + if (!ok) { return false; } - return secrets_delete_entry(machine_last_change_time_keystr(domain)); + + return true; } /************************************************************************ -- 1.9.1 From 7c3af02aa64624876f7fae0b8631d9083b921d0d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:40:05 +0200 Subject: [PATCH 39/58] s3:secrets: let secrets_delete_machine_password_ex() remove SID and GUID too BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit cf8a4646fe71a974b6a5ee13ae7d7751a5a0adc9) --- source3/libnet/libnet_join.c | 4 ---- source3/passdb/machine_account_secrets.c | 16 +++++++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 9a6ba13..58d7a16 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -1713,10 +1713,6 @@ static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx, return false; } - if (!secrets_delete_domain_sid(lp_workgroup())) { - return false; - } - return true; } diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 56a9442..06e42f2 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -371,8 +371,7 @@ bool secrets_fetch_trust_account_password(const char *domain, uint8_t ret_pwd[16 } /************************************************************************ - Routine to delete the plaintext machine account password, old password, - sec channel type and last change time from secrets database + Routine to delete all information related to the domain joined machine. ************************************************************************/ bool secrets_delete_machine_password_ex(const char *domain) @@ -380,6 +379,12 @@ bool secrets_delete_machine_password_ex(const char *domain) const char *tmpkey = NULL; bool ok; + tmpkey = domain_guid_keystr(domain); + ok = secrets_delete(tmpkey); + if (!ok) { + return false; + } + tmpkey = machine_prev_password_keystr(domain); ok = secrets_delete(tmpkey); if (!ok) { @@ -404,6 +409,12 @@ bool secrets_delete_machine_password_ex(const char *domain) return false; } + tmpkey = domain_sid_keystr(domain); + ok = secrets_delete_entry(tmpkey); + if (!ok) { + return false; + } + return true; } @@ -485,7 +496,6 @@ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const if (delete_join) { secrets_delete_machine_password_ex(domain); - secrets_delete_domain_sid(domain); TALLOC_FREE(frame); return true; } -- 1.9.1 From db272b7760b0fce51965e930f7c7f87b01203854 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:44:31 +0200 Subject: [PATCH 40/58] s3:secrets: let secrets_delete_machine_password_ex() also remove the des_salt key BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit dfaadc81925e313901c9b30cd98a4b4fd2404f9d) --- source3/include/secrets.h | 2 +- source3/libnet/libnet_join.c | 9 ++++----- source3/passdb/machine_account_secrets.c | 12 ++++++++++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/source3/include/secrets.h b/source3/include/secrets.h index 548003f..fc8e118 100644 --- a/source3/include/secrets.h +++ b/source3/include/secrets.h @@ -110,7 +110,7 @@ bool secrets_fetch_trusted_domain_password(const char *domain, char** pwd, struct dom_sid *sid, time_t *pass_last_set_time); bool secrets_store_trusted_domain_password(const char* domain, const char* pwd, const struct dom_sid *sid); -bool secrets_delete_machine_password_ex(const char *domain); +bool secrets_delete_machine_password_ex(const char *domain, const char *realm); bool secrets_delete_domain_sid(const char *domain); bool secrets_store_machine_password(const char *pass, const char *domain, enum netr_SchannelType sec_channel); char *secrets_fetch_prev_machine_password(const char *domain); diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 58d7a16..c13ac15 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -1709,11 +1709,10 @@ static WERROR libnet_join_post_verify(TALLOC_CTX *mem_ctx, static bool libnet_join_unjoindomain_remove_secrets(TALLOC_CTX *mem_ctx, struct libnet_UnjoinCtx *r) { - if (!secrets_delete_machine_password_ex(lp_workgroup())) { - return false; - } - - return true; + /* + * TODO: use values from 'struct libnet_UnjoinCtx' ? + */ + return secrets_delete_machine_password_ex(lp_workgroup(), lp_realm()); } /**************************************************************** diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 06e42f2..70a8277 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -374,11 +374,19 @@ bool secrets_fetch_trust_account_password(const char *domain, uint8_t ret_pwd[16 Routine to delete all information related to the domain joined machine. ************************************************************************/ -bool secrets_delete_machine_password_ex(const char *domain) +bool secrets_delete_machine_password_ex(const char *domain, const char *realm) { const char *tmpkey = NULL; bool ok; + if (realm != NULL) { + tmpkey = des_salt_key(domain); + ok = secrets_delete(tmpkey); + if (!ok) { + return false; + } + } + tmpkey = domain_guid_keystr(domain); ok = secrets_delete(tmpkey); if (!ok) { @@ -495,7 +503,7 @@ bool secrets_store_machine_pw_sync(const char *pass, const char *oldpass, const uint8_t sec_channel_bytes[4]; if (delete_join) { - secrets_delete_machine_password_ex(domain); + secrets_delete_machine_password_ex(domain, realm); TALLOC_FREE(frame); return true; } -- 1.9.1 From 15ae220dd30a81b5983c44567f0150588af39194 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 24 May 2017 05:56:32 +0200 Subject: [PATCH 41/58] s3:secrets: use secrets_delete for all keys in secrets_delete_machine_password_ex() We just want all values to be removed at the end, it doesn't matter if they didn't existed before. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit bfe35abc1fb15e70a99fa74d064051a1ad541ed0) --- source3/passdb/machine_account_secrets.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 70a8277..9a96a3f 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -400,25 +400,25 @@ bool secrets_delete_machine_password_ex(const char *domain, const char *realm) } tmpkey = machine_password_keystr(domain); - ok = secrets_delete_entry(tmpkey); + ok = secrets_delete(tmpkey); if (!ok) { return false; } tmpkey = machine_sec_channel_type_keystr(domain); - ok = secrets_delete_entry(tmpkey); + ok = secrets_delete(tmpkey); if (!ok) { return false; } tmpkey = machine_last_change_time_keystr(domain); - ok = secrets_delete_entry(tmpkey); + ok = secrets_delete(tmpkey); if (!ok) { return false; } tmpkey = domain_sid_keystr(domain); - ok = secrets_delete_entry(tmpkey); + ok = secrets_delete(tmpkey); if (!ok) { return false; } -- 1.9.1 From cc10cedaa0b6010cfe41ca588058215bceeb9b89 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 15:36:29 +0200 Subject: [PATCH 42/58] s3:trusts_util: pass dcname to trust_pw_change() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 1421abfc733247a6b71eefd819dfeae7151a6d78) --- source3/include/proto.h | 1 + source3/libsmb/trusts_util.c | 1 + source3/rpcclient/cmd_netlogon.c | 2 ++ source3/utils/net_rpc.c | 8 ++++++++ source3/winbindd/winbindd_dual.c | 1 + source3/winbindd/winbindd_dual_srv.c | 2 ++ 6 files changed, 15 insertions(+) diff --git a/source3/include/proto.h b/source3/include/proto.h index 25f43f5..97be40b 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -887,6 +887,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, struct messaging_context *msg_ctx, struct dcerpc_binding_handle *b, const char *domain, + const char *dcname, bool force); /* The following definitions come from param/loadparm.c */ diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c index 2cc6264..47b79b7 100644 --- a/source3/libsmb/trusts_util.c +++ b/source3/libsmb/trusts_util.c @@ -107,6 +107,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, struct messaging_context *msg_ctx, struct dcerpc_binding_handle *b, const char *domain, + const char *dcname, bool force) { TALLOC_CTX *frame = talloc_stackframe(); diff --git a/source3/rpcclient/cmd_netlogon.c b/source3/rpcclient/cmd_netlogon.c index 744361c..77f5885 100644 --- a/source3/rpcclient/cmd_netlogon.c +++ b/source3/rpcclient/cmd_netlogon.c @@ -831,6 +831,7 @@ static NTSTATUS cmd_netlogon_change_trust_pw(struct rpc_pipe_client *cli, const char **argv) { NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + const char *dcname = cli->desthost; /* Check arguments */ @@ -843,6 +844,7 @@ static NTSTATUS cmd_netlogon_change_trust_pw(struct rpc_pipe_client *cli, rpcclient_msg_ctx, cli->binding_handle, lp_workgroup(), + dcname, true); /* force */ if (!NT_STATUS_IS_OK(result)) goto done; diff --git a/source3/utils/net_rpc.c b/source3/utils/net_rpc.c index 1e3e286..805995c 100644 --- a/source3/utils/net_rpc.c +++ b/source3/utils/net_rpc.c @@ -279,11 +279,19 @@ static NTSTATUS rpc_changetrustpw_internals(struct net_context *c, const char **argv) { NTSTATUS status; + const char *dcname = NULL; + + if (cli == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + dcname = smbXcli_conn_remote_name(cli->conn); status = trust_pw_change(c->netlogon_creds, c->msg_ctx, pipe_hnd->binding_handle, c->opt_target_workgroup, + dcname, true); /* force */ if (!NT_STATUS_IS_OK(status)) { d_fprintf(stderr, _("Failed to change machine account password: %s\n"), diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c index c16b94e..ab0aae9 100644 --- a/source3/winbindd/winbindd_dual.c +++ b/source3/winbindd/winbindd_dual.c @@ -1097,6 +1097,7 @@ static void machine_password_change_handler(struct tevent_context *ctx, msg_ctx, netlogon_pipe->binding_handle, child->domain->name, + child->domain->dcname, false); /* force */ DEBUG(10, ("machine_password_change_handler: " diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c index b386d75..371b2bf 100644 --- a/source3/winbindd/winbindd_dual_srv.c +++ b/source3/winbindd/winbindd_dual_srv.c @@ -718,6 +718,7 @@ NTSTATUS _wbint_ChangeMachineAccount(struct pipes_struct *p, msg_ctx, netlogon_pipe->binding_handle, domain->name, + domain->dcname, true); /* force */ /* Pass back result code - zero for success, other values for @@ -1394,6 +1395,7 @@ reconnect: status = trust_pw_change(domain->conn.netlogon_creds, msg_ctx, b, domain->name, + domain->dcname, true); /* force */ if (!NT_STATUS_IS_OK(status)) { if (!retry && dcerpc_binding_handle_is_connected(b)) { -- 1.9.1 From d3358927d5a0d4359417628722d196a83b7a9ee1 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 20:44:40 +0200 Subject: [PATCH 43/58] libcli/auth: pass an array of nt_hashes to netlogon_creds_cli_auth*() This way the caller can pass more than 2 hashes and can only know which hash was used for a successful connection. We allow up to 4 hashes (next, current, old, older). BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit ddd7ac68ccae8b4df6c6a65b3dad20e21924f538) --- libcli/auth/netlogon_creds_cli.c | 58 +++++++++++++++++++++++++-------------- libcli/auth/netlogon_creds_cli.h | 12 ++++---- source3/libsmb/trusts_util.c | 19 +++++++++---- source3/rpc_client/cli_netlogon.c | 15 ++++++++-- 4 files changed, 71 insertions(+), 33 deletions(-) diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c index d55142e..ba8e063 100644 --- a/libcli/auth/netlogon_creds_cli.c +++ b/libcli/auth/netlogon_creds_cli.c @@ -942,9 +942,10 @@ struct netlogon_creds_cli_auth_state { struct tevent_context *ev; struct netlogon_creds_cli_context *context; struct dcerpc_binding_handle *binding_handle; - struct samr_Password current_nt_hash; - struct samr_Password previous_nt_hash; - struct samr_Password used_nt_hash; + uint8_t num_nt_hashes; + uint8_t idx_nt_hashes; + const struct samr_Password * const *nt_hashes; + const struct samr_Password *used_nt_hash; char *srv_name_slash; uint32_t current_flags; struct netr_Credential client_challenge; @@ -956,7 +957,6 @@ struct netlogon_creds_cli_auth_state { bool try_auth3; bool try_auth2; bool require_auth2; - bool try_previous_nt_hash; struct netlogon_creds_cli_locked_state *locked_state; }; @@ -967,8 +967,8 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - struct samr_Password current_nt_hash, - const struct samr_Password *previous_nt_hash) + uint8_t num_nt_hashes, + const struct samr_Password * const *nt_hashes) { struct tevent_req *req; struct netlogon_creds_cli_auth_state *state; @@ -984,12 +984,19 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx, state->ev = ev; state->context = context; state->binding_handle = b; - state->current_nt_hash = current_nt_hash; - if (previous_nt_hash != NULL) { - state->previous_nt_hash = *previous_nt_hash; - state->try_previous_nt_hash = true; + if (num_nt_hashes < 1) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); + return tevent_req_post(req, ev); + } + if (num_nt_hashes > 4) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); + return tevent_req_post(req, ev); } + state->num_nt_hashes = num_nt_hashes; + state->idx_nt_hashes = 0; + state->nt_hashes = nt_hashes; + if (context->db.locked_state != NULL) { tevent_req_nterror(req, NT_STATUS_LOCK_NOT_GRANTED); return tevent_req_post(req, ev); @@ -1019,7 +1026,7 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx, state->require_auth2 = true; } - state->used_nt_hash = state->current_nt_hash; + state->used_nt_hash = state->nt_hashes[state->idx_nt_hashes]; state->current_flags = context->client.proposed_flags; if (context->db.g_ctx != NULL) { @@ -1141,7 +1148,7 @@ static void netlogon_creds_cli_auth_challenge_done(struct tevent_req *subreq) state->context->client.type, &state->client_challenge, &state->server_challenge, - &state->used_nt_hash, + state->used_nt_hash, &state->client_credential, state->current_flags); if (tevent_req_nomem(state->creds, req)) { @@ -1283,7 +1290,8 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq) return; } - if (!state->try_previous_nt_hash) { + state->idx_nt_hashes += 1; + if (state->idx_nt_hashes >= state->num_nt_hashes) { /* * we already retried, giving up... */ @@ -1294,8 +1302,7 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq) /* * lets retry with the old nt hash. */ - state->try_previous_nt_hash = false; - state->used_nt_hash = state->previous_nt_hash; + state->used_nt_hash = state->nt_hashes[state->idx_nt_hashes]; state->current_flags = state->context->client.proposed_flags; netlogon_creds_cli_auth_challenge_start(req); return; @@ -1330,43 +1337,52 @@ static void netlogon_creds_cli_auth_srvauth_done(struct tevent_req *subreq) tevent_req_done(req); } -NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req) +NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req, + uint8_t *idx_nt_hashes) { + struct netlogon_creds_cli_auth_state *state = + tevent_req_data(req, + struct netlogon_creds_cli_auth_state); NTSTATUS status; + *idx_nt_hashes = 0; + if (tevent_req_is_nterror(req, &status)) { tevent_req_received(req); return status; } + *idx_nt_hashes = state->idx_nt_hashes; tevent_req_received(req); return NT_STATUS_OK; } NTSTATUS netlogon_creds_cli_auth(struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - struct samr_Password current_nt_hash, - const struct samr_Password *previous_nt_hash) + uint8_t num_nt_hashes, + const struct samr_Password * const *nt_hashes, + uint8_t *idx_nt_hashes) { TALLOC_CTX *frame = talloc_stackframe(); struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_NO_MEMORY; + *idx_nt_hashes = 0; + ev = samba_tevent_context_init(frame); if (ev == NULL) { goto fail; } req = netlogon_creds_cli_auth_send(frame, ev, context, b, - current_nt_hash, - previous_nt_hash); + num_nt_hashes, nt_hashes); if (req == NULL) { goto fail; } if (!tevent_req_poll_ntstatus(req, ev, &status)) { goto fail; } - status = netlogon_creds_cli_auth_recv(req); + status = netlogon_creds_cli_auth_recv(req, idx_nt_hashes); fail: TALLOC_FREE(frame); return status; diff --git a/libcli/auth/netlogon_creds_cli.h b/libcli/auth/netlogon_creds_cli.h index 949e03b..4cedb3b 100644 --- a/libcli/auth/netlogon_creds_cli.h +++ b/libcli/auth/netlogon_creds_cli.h @@ -84,13 +84,15 @@ struct tevent_req *netlogon_creds_cli_auth_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - struct samr_Password current_nt_hash, - const struct samr_Password *previous_nt_hash); -NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req); + uint8_t num_nt_hashes, + const struct samr_Password * const *nt_hashes); +NTSTATUS netlogon_creds_cli_auth_recv(struct tevent_req *req, + uint8_t *idx_nt_hashes); NTSTATUS netlogon_creds_cli_auth(struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - struct samr_Password current_nt_hash, - const struct samr_Password *previous_nt_hash); + uint8_t num_nt_hashes, + const struct samr_Password * const *nt_hashes, + uint8_t *idx_nt_hashes); struct tevent_req *netlogon_creds_cli_check_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c index 47b79b7..128beb7 100644 --- a/source3/libsmb/trusts_util.c +++ b/source3/libsmb/trusts_util.c @@ -115,7 +115,9 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, struct trust_pw_change_state *state; struct cli_credentials *creds = NULL; const struct samr_Password *current_nt_hash = NULL; - const struct samr_Password *previous_nt_hash = NULL; + uint8_t num_nt_hashes = 0; + const struct samr_Password *nt_hashes[1] = { NULL, }; + uint8_t idx_nt_hashes = 0; enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL; time_t pass_last_set_time; uint32_t old_version = 0; @@ -245,6 +247,9 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, return NT_STATUS_NO_MEMORY; } + nt_hashes[0] = current_nt_hash; + num_nt_hashes = 1; + /* * We could use cli_credentials_get_old_nt_hash(creds, frame) to * set previous_nt_hash. @@ -259,8 +264,9 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, * local secrets before doing the change. */ status = netlogon_creds_cli_auth(context, b, - *current_nt_hash, - previous_nt_hash); + num_nt_hashes, + nt_hashes, + &idx_nt_hashes); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old password - %s!\n", context_name, nt_errstr(status))); @@ -349,9 +355,12 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, /* * Now we verify the new password. */ + nt_hashes[0] = current_nt_hash; + num_nt_hashes = 1; status = netlogon_creds_cli_auth(context, b, - *current_nt_hash, - NULL); /* previous_nt_hash */ + num_nt_hashes, + nt_hashes, + &idx_nt_hashes); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n", context_name, nt_errstr(status))); diff --git a/source3/rpc_client/cli_netlogon.c b/source3/rpc_client/cli_netlogon.c index 166f318..7c39f46 100644 --- a/source3/rpc_client/cli_netlogon.c +++ b/source3/rpc_client/cli_netlogon.c @@ -160,6 +160,9 @@ NTSTATUS rpccli_setup_netlogon_creds(struct cli_state *cli, TALLOC_CTX *frame = talloc_stackframe(); struct rpc_pipe_client *netlogon_pipe = NULL; struct netlogon_creds_CredentialState *creds = NULL; + uint8_t num_nt_hashes = 0; + const struct samr_Password *nt_hashes[2] = { NULL, NULL }; + uint8_t idx_nt_hashes = 0; NTSTATUS status; status = netlogon_creds_cli_get(netlogon_creds, @@ -196,10 +199,18 @@ NTSTATUS rpccli_setup_netlogon_creds(struct cli_state *cli, } talloc_steal(frame, netlogon_pipe); + nt_hashes[0] = ¤t_nt_hash; + num_nt_hashes = 1; + if (previous_nt_hash != NULL) { + nt_hashes[1] = previous_nt_hash; + num_nt_hashes = 2; + } + status = netlogon_creds_cli_auth(netlogon_creds, netlogon_pipe->binding_handle, - current_nt_hash, - previous_nt_hash); + num_nt_hashes, + nt_hashes, + &idx_nt_hashes); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return status; -- 1.9.1 From bb55597a9598ebe77be76c597345207b820cf6a2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Jun 2017 11:17:03 +0200 Subject: [PATCH 44/58] libcli/auth: add const to set_pw_in_buffer() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 1b48c8515ed8fd29204c82cc47f958f4636cd494) --- libcli/auth/proto.h | 2 +- libcli/auth/smbencrypt.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libcli/auth/proto.h b/libcli/auth/proto.h index cc9ae33..a03f45e 100644 --- a/libcli/auth/proto.h +++ b/libcli/auth/proto.h @@ -187,7 +187,7 @@ void encode_or_decode_arc4_passwd_buffer(unsigned char pw_buf[532], const DATA_B encode a password buffer with an already unicode password. The rest of the buffer is filled with random data to make it harder to attack. ************************************************************/ -bool set_pw_in_buffer(uint8_t buffer[516], DATA_BLOB *password); +bool set_pw_in_buffer(uint8_t buffer[516], const DATA_BLOB *password); /*********************************************************** decode a password buffer diff --git a/libcli/auth/smbencrypt.c b/libcli/auth/smbencrypt.c index ebf6812..afd9286 100644 --- a/libcli/auth/smbencrypt.c +++ b/libcli/auth/smbencrypt.c @@ -804,7 +804,7 @@ void encode_or_decode_arc4_passwd_buffer(unsigned char pw_buf[532], const DATA_B encode a password buffer with an already unicode password. The rest of the buffer is filled with random data to make it harder to attack. ************************************************************/ -bool set_pw_in_buffer(uint8_t buffer[516], DATA_BLOB *password) +bool set_pw_in_buffer(uint8_t buffer[516], const DATA_BLOB *password) { if (password->length > 512) { return false; -- 1.9.1 From 473111e72c8a309b88de988119c009b40749a791 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Jun 2017 11:18:37 +0200 Subject: [PATCH 45/58] libcli/auth: pass the cleartext blob to netlogon_creds_cli_ServerPasswordSet*() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 0f5945a06df4bef501ca5085c621294057007225) --- libcli/auth/netlogon_creds_cli.c | 20 +++++++++++--------- libcli/auth/netlogon_creds_cli.h | 4 ++-- source3/libnet/libnet_join.c | 19 ++++++++++++++++++- source3/libsmb/trusts_util.c | 39 ++++++++++++++++++++++++++++++++------- 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/libcli/auth/netlogon_creds_cli.c b/libcli/auth/netlogon_creds_cli.c index ba8e063..29baae4 100644 --- a/libcli/auth/netlogon_creds_cli.c +++ b/libcli/auth/netlogon_creds_cli.c @@ -36,6 +36,7 @@ #include "source3/include/messages.h" #include "source3/include/g_lock.h" #include "libds/common/roles.h" +#include "lib/crypto/crypto.h" struct netlogon_creds_cli_locked_state; @@ -1750,7 +1751,7 @@ struct tevent_req *netlogon_creds_cli_ServerPasswordSet_send(TALLOC_CTX *mem_ctx struct tevent_context *ev, struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - const char *new_password, + const DATA_BLOB *new_password, const uint32_t *new_version) { struct tevent_req *req; @@ -1768,20 +1769,21 @@ struct tevent_req *netlogon_creds_cli_ServerPasswordSet_send(TALLOC_CTX *mem_ctx state->context = context; state->binding_handle = b; - /* - * netr_ServerPasswordSet - */ - ok = E_md4hash(new_password, state->samr_password.hash); - if (!ok) { + if (new_password->length < 14) { tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); return tevent_req_post(req, ev); } /* + * netr_ServerPasswordSet + */ + mdfour(state->samr_password.hash, new_password->data, new_password->length); + + /* * netr_ServerPasswordSet2 */ - ok = encode_pw_buffer(state->samr_crypt_password.data, - new_password, STR_UNICODE); + ok = set_pw_in_buffer(state->samr_crypt_password.data, + new_password); if (!ok) { tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); return tevent_req_post(req, ev); @@ -2051,7 +2053,7 @@ NTSTATUS netlogon_creds_cli_ServerPasswordSet_recv(struct tevent_req *req) NTSTATUS netlogon_creds_cli_ServerPasswordSet( struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - const char *new_password, + const DATA_BLOB *new_password, const uint32_t *new_version) { TALLOC_CTX *frame = talloc_stackframe(); diff --git a/libcli/auth/netlogon_creds_cli.h b/libcli/auth/netlogon_creds_cli.h index 4cedb3b..a7fd48c 100644 --- a/libcli/auth/netlogon_creds_cli.h +++ b/libcli/auth/netlogon_creds_cli.h @@ -106,13 +106,13 @@ struct tevent_req *netlogon_creds_cli_ServerPasswordSet_send(TALLOC_CTX *mem_ctx struct tevent_context *ev, struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - const char *new_password, + const DATA_BLOB *new_password, const uint32_t *new_version); NTSTATUS netlogon_creds_cli_ServerPasswordSet_recv(struct tevent_req *req); NTSTATUS netlogon_creds_cli_ServerPasswordSet( struct netlogon_creds_cli_context *context, struct dcerpc_binding_handle *b, - const char *new_password, + const DATA_BLOB *new_password, const uint32_t *new_version); struct tevent_req *netlogon_creds_cli_LogonSamLogon_send(TALLOC_CTX *mem_ctx, diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index c13ac15..6f497d4 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -1140,6 +1140,9 @@ static NTSTATUS libnet_join_joindomain_rpc_unsecure(TALLOC_CTX *mem_ctx, struct rpc_pipe_client *netlogon_pipe = NULL; struct netlogon_creds_cli_context *netlogon_creds = NULL; struct samr_Password current_nt_hash; + size_t len = 0; + bool ok; + DATA_BLOB new_trust_blob = data_blob_null; NTSTATUS status; status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon, @@ -1186,9 +1189,23 @@ static NTSTATUS libnet_join_joindomain_rpc_unsecure(TALLOC_CTX *mem_ctx, return status; } + len = strlen(r->in.machine_password); + ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16, + r->in.machine_password, len, + (void **)&new_trust_blob.data, + &new_trust_blob.length); + if (!ok) { + status = NT_STATUS_UNMAPPABLE_CHARACTER; + if (errno == ENOMEM) { + status = NT_STATUS_NO_MEMORY; + } + TALLOC_FREE(frame); + return status; + } + status = netlogon_creds_cli_ServerPasswordSet(netlogon_creds, netlogon_pipe->binding_handle, - r->in.machine_password, + &new_trust_blob, NULL); /* new_version */ if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c index 128beb7..5bc8005 100644 --- a/source3/libsmb/trusts_util.c +++ b/source3/libsmb/trusts_util.c @@ -125,7 +125,9 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, struct timeval g_timeout = { 0, }; int timeout = 0; struct timeval tv = { 0, }; - char *new_trust_passwd = NULL; + char *new_trust_pw_str = NULL; + size_t len = 0; + DATA_BLOB new_trust_pw_blob = data_blob_null; uint32_t new_version = 0; uint32_t *new_trust_version = NULL; NTSTATUS status; @@ -239,14 +241,31 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, * We create a random buffer and convert that to utf8. * This is similar to what windows is doing. */ - new_trust_passwd = trust_pw_new_value(frame, sec_channel_type, + new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type, lp_security()); - if (new_trust_passwd == NULL) { + if (new_trust_pw_str == NULL) { DEBUG(0, ("trust_pw_new_value() failed\n")); TALLOC_FREE(frame); return NT_STATUS_NO_MEMORY; } + len = strlen(new_trust_pw_str); + ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16, + new_trust_pw_str, len, + (void **)&new_trust_pw_blob.data, + &new_trust_pw_blob.length); + if (!ok) { + status = NT_STATUS_UNMAPPABLE_CHARACTER; + if (errno == ENOMEM) { + status = NT_STATUS_NO_MEMORY; + } + DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) " + "failed for of %s - %s\n", + domain, nt_errstr(status)); + TALLOC_FREE(frame); + return status; + } + nt_hashes[0] = current_nt_hash; num_nt_hashes = 1; @@ -287,13 +306,16 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, case SEC_CHAN_WKSTA: case SEC_CHAN_BDC: - ok = secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type); + ok = secrets_store_machine_password(new_trust_pw_str, + domain, + sec_channel_type); if (!ok) { DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n", domain)); TALLOC_FREE(frame); return NT_STATUS_INTERNAL_DB_CORRUPTION; } + TALLOC_FREE(new_trust_pw_str); break; case SEC_CHAN_DNS_DOMAIN: @@ -302,7 +324,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, * we need to get the sid first for the * pdb_set_trusteddom_pw call */ - ok = pdb_set_trusteddom_pw(domain, new_trust_passwd, + ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str, &td->security_identifier); if (!ok) { DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n", @@ -310,6 +332,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, TALLOC_FREE(frame); return NT_STATUS_INTERNAL_DB_CORRUPTION; } + TALLOC_FREE(new_trust_pw_str); break; default: @@ -321,7 +344,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, current_timestring(talloc_tos(), false), __func__, domain)); status = netlogon_creds_cli_ServerPasswordSet(context, b, - new_trust_passwd, + &new_trust_pw_blob, new_trust_version); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("%s : %s(%s) remote password change set with %s failed - %s\n", @@ -336,7 +359,9 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, current_timestring(talloc_tos(), false), __func__, domain, context_name)); - ok = cli_credentials_set_password(creds, new_trust_passwd, CRED_SPECIFIED); + ok = cli_credentials_set_utf16_password(creds, + &new_trust_pw_blob, + CRED_SPECIFIED); if (!ok) { DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n", domain)); -- 1.9.1 From a34857e22f20ce73921f1655f96e4eef1532afea Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 21 Jun 2017 21:30:39 +0200 Subject: [PATCH 46/58] s3:trusts_util: also pass the previous_nt_hash to netlogon_creds_cli_auth() Even in the case where only the password is known to the server, we should try to leave a valid authentication behind. We have better ways to indentify which password worked than only using the current one. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit d60404b032eca5384d889352f52b9b129861b4af) --- source3/libsmb/trusts_util.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c index 5bc8005..ff7f256 100644 --- a/source3/libsmb/trusts_util.c +++ b/source3/libsmb/trusts_util.c @@ -115,9 +115,12 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, struct trust_pw_change_state *state; struct cli_credentials *creds = NULL; const struct samr_Password *current_nt_hash = NULL; + const struct samr_Password *previous_nt_hash = NULL; uint8_t num_nt_hashes = 0; - const struct samr_Password *nt_hashes[1] = { NULL, }; + uint8_t idx = 0; + const struct samr_Password *nt_hashes[1+1] = { NULL, }; uint8_t idx_nt_hashes = 0; + uint8_t idx_current = UINT8_MAX; enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL; time_t pass_last_set_time; uint32_t old_version = 0; @@ -181,6 +184,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, TALLOC_FREE(frame); return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; } + previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame); old_version = cli_credentials_get_kvno(creds); pass_last_set_time = cli_credentials_get_password_last_changed_time(creds); @@ -266,15 +270,19 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, return status; } - nt_hashes[0] = current_nt_hash; - num_nt_hashes = 1; + idx_current = idx; + nt_hashes[idx++] = current_nt_hash; + if (previous_nt_hash != NULL) { + nt_hashes[idx++] = previous_nt_hash; + } + num_nt_hashes = idx; + + DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n", + current_timestring(talloc_tos(), false), + __func__, domain, context_name)); /* - * We could use cli_credentials_get_old_nt_hash(creds, frame) to - * set previous_nt_hash. - * - * But we want to check if the dc has our current password and only do - * a change if that's the case. So we keep previous_nt_hash = NULL. + * Check which password the dc knows about. * * TODO: * If the previous password is the only password in common with the dc, @@ -287,12 +295,21 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, nt_hashes, &idx_nt_hashes); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old password - %s!\n", - context_name, nt_errstr(status))); + DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n", + context_name, num_nt_hashes, nt_errstr(status))); TALLOC_FREE(frame); return status; } + if (idx_nt_hashes != idx_current) { + DEBUG(0,("%s : %s(%s): Verified older password remotely " + "skip changing %s\n", + current_timestring(talloc_tos(), false), + __func__, domain, context_name)); + TALLOC_FREE(frame); + return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; + } + DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n", current_timestring(talloc_tos(), false), __func__, domain, context_name)); @@ -380,8 +397,10 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, /* * Now we verify the new password. */ - nt_hashes[0] = current_nt_hash; - num_nt_hashes = 1; + idx = 0; + idx_current = idx; + nt_hashes[idx++] = current_nt_hash; + num_nt_hashes = idx; status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes, -- 1.9.1 From 723d44cac469107f2fe4fb8355915d3e8b2a9e7d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 11:35:20 +0200 Subject: [PATCH 47/58] lsa.idl: make lsa_DnsDomainInfo [public] BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit ea0798881a7aaf5897a3a3806149536d3d54fc3b) --- librpc/idl/lsa.idl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librpc/idl/lsa.idl b/librpc/idl/lsa.idl index 0823707..cceb17b 100644 --- a/librpc/idl/lsa.idl +++ b/librpc/idl/lsa.idl @@ -367,7 +367,7 @@ import "misc.idl", "security.idl"; uint8 log_is_full; } lsa_AuditFullQueryInfo; - typedef struct { + typedef [public] struct { /* it's important that we use the lsa_StringLarge here, * because otherwise windows clients result with such dns hostnames * e.g. w2k3-client.samba4.samba.orgsamba4.samba.org -- 1.9.1 From 5e05687c16df58954e5a1275a119c9611ad88ee9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 11:35:37 +0200 Subject: [PATCH 48/58] netlogon.idl: make netr_TrustFlags [public] BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 60274475332dafdfb829a7c086ea09cd9ed00540) --- librpc/idl/netlogon.idl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librpc/idl/netlogon.idl b/librpc/idl/netlogon.idl index 621d537..50ce15c 100644 --- a/librpc/idl/netlogon.idl +++ b/librpc/idl/netlogon.idl @@ -1261,7 +1261,7 @@ interface netlogon /****************/ /* Function 0x1d */ - typedef [bitmap32bit] bitmap { + typedef [public,bitmap32bit] bitmap { NETR_TRUST_FLAG_IN_FOREST = 0x00000001, NETR_TRUST_FLAG_OUTBOUND = 0x00000002, NETR_TRUST_FLAG_TREEROOT = 0x00000004, -- 1.9.1 From 2c20fe626d7e26e2cfcd417c756da64f637ae5e6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 10:09:01 +0200 Subject: [PATCH 49/58] netlogon.idl: use lsa_TrustType and lsa_TrustAttributes in netr_trust_extension BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 28ac10503476de3c000b3deee2c1f67e0b305578) --- librpc/idl/netlogon.idl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/librpc/idl/netlogon.idl b/librpc/idl/netlogon.idl index 50ce15c..b56c825 100644 --- a/librpc/idl/netlogon.idl +++ b/librpc/idl/netlogon.idl @@ -1369,8 +1369,8 @@ interface netlogon [value(8)] uint32 size; netr_TrustFlags flags; uint32 parent_index; - uint32 trust_type; - uint32 trust_attributes; + lsa_TrustType trust_type; + lsa_TrustAttributes trust_attributes; } netr_trust_extension; typedef struct { -- 1.9.1 From 242ffe3b3f5f5ad38f9e55c0b5ff2254c91c455d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 10:11:18 +0200 Subject: [PATCH 50/58] secrets.idl: add secrets_domain_info that will be used in secrets.tdb for machine account trusts This blob will be store in secrets.tdb. It makes it possible to store much more useful details about the workstation trust. The key feature that that triggered this change is the ability to store details for the next password change before doing the remote change. This will allow us to recover from failures. While being there I also thought about possible new features, which we may implement in the near future. We also store the raw UTF16 like cleartext buffer as well as derived keys like the NTHASH (arcfour-hmac-md5 key) and other kerberos keys. This will allow us to avoid recalculating the keys for an in memory keytab in future. I also added pointer to an optional lsa_ForestTrustInformation structure, which might be useful to implement multi-tenancy in future. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit a59c9cba31a801d90db06b767cfd44776f4ede77) --- source3/librpc/idl/secrets.idl | 92 +++++++++++++++++++++++++++++++++++++++++- source3/librpc/wscript_build | 2 +- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/source3/librpc/idl/secrets.idl b/source3/librpc/idl/secrets.idl index 1d0ba19..2c06fa6 100644 --- a/source3/librpc/idl/secrets.idl +++ b/source3/librpc/idl/secrets.idl @@ -1,6 +1,6 @@ #include "idl_types.h" -import "security.idl"; +import "misc.idl", "samr.idl", "lsa.idl", "netlogon.idl", "security.idl"; /* IDL structures for secrets code @@ -37,5 +37,95 @@ import "security.idl"; security_descriptor *sd; } lsa_secret; + /* + * This is the on-disc format the workstation trust. + * + * DO NOT CHANGE + * without changing secrets_domain_info_version + * and adding glue code. Discuss on samba-technical + * first! + */ + typedef struct { + uint32 keytype; + uint32 iteration_count; + [flag(NDR_SECRET)] DATA_BLOB value; + } secrets_domain_info1_kerberos_key; + + typedef struct { + NTTIME change_time; + [string,charset(UTF16)] uint16 change_server[]; + + [flag(NDR_SECRET)] DATA_BLOB cleartext_blob; + [flag(NDR_SECRET)] samr_Password nt_hash; + + [string,charset(UTF16)] uint16 *salt_data; + uint32 default_iteration_count; + uint16 num_keys; + secrets_domain_info1_kerberos_key keys[num_keys]; + } secrets_domain_info1_password; + + typedef struct { + NTSTATUS local_status; + NTSTATUS remote_status; + NTTIME change_time; + [string,charset(UTF16)] uint16 change_server[]; + [ref] secrets_domain_info1_password *password; + } secrets_domain_info1_change; + + typedef [public] struct { + [value(0)] hyper reserved_flags; + + NTTIME join_time; + + [string,charset(UTF16)] uint16 computer_name[]; + [string,charset(UTF16)] uint16 account_name[]; + netr_SchannelType secure_channel_type; + + lsa_DnsDomainInfo domain_info; + netr_TrustFlags trust_flags; + lsa_TrustType trust_type; + lsa_TrustAttributes trust_attributes; + + /* + * This is unused currently, it might + * be useful to implement multi-tenancy (joining multiple domains) + * in future. + * + * Or we could use it to do other filtering of domains. + */ + [value(NULL)] lsa_ForestTrustInformation *reserved_routing; + + kerb_EncTypes supported_enc_types; + [string,charset(UTF16)] uint16 *salt_principal; + + NTTIME password_last_change; + hyper password_changes; + secrets_domain_info1_change *next_change; + + [ref] secrets_domain_info1_password *password; + secrets_domain_info1_password *old_password; + secrets_domain_info1_password *older_password; + } secrets_domain_info1; + + typedef [v1_enum] enum { + SECRETS_DOMAIN_INFO_VERSION_1 = 0x00000001 + } secrets_domain_info_version; + + /* + * If we ever need to change this we need to + * change secrets_domain_info into + * secrets_domain_info_v1 + */ + typedef union { + [case(SECRETS_DOMAIN_INFO_VERSION_1)] + secrets_domain_info1 *info1; + [default]; + } secrets_domain_infoU; + + typedef [public] struct { + secrets_domain_info_version version; + [value(0)] uint32 reserved; + [switch_is(version)] secrets_domain_infoU info; + } secrets_domain_infoB; } diff --git a/source3/librpc/wscript_build b/source3/librpc/wscript_build index 1d8c17b..9260350 100644 --- a/source3/librpc/wscript_build +++ b/source3/librpc/wscript_build @@ -27,7 +27,7 @@ bld.SAMBA3_SUBSYSTEM('NDR_LEASES_DB', bld.SAMBA3_SUBSYSTEM('NDR_SECRETS', source='gen_ndr/ndr_secrets.c', - public_deps='ndr NDR_SECURITY' + public_deps='ndr NDR_SAMR NDR_LSA NDR_NETLOGON NDR_SECURITY' ) bld.SAMBA3_SUBSYSTEM('NDR_PERFCOUNT', -- 1.9.1 From f554d730c214d79fc1250c21b03aceff31e25b16 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 16:28:17 +0200 Subject: [PATCH 51/58] s3:secrets: add infrastructure to use secrets_domain_infoB to store credentials We now store various hashed keys at change time and maintain a lot of details that will help debugging failed password changes. We keep storing the legacy values: SECRETS/SID/ SECRETS/DOMGUID/ SECRETS/MACHINE_LAST_CHANGE_TIME/ SECRETS/MACHINE_PASSWORD/ SECRETS/MACHINE_PASSWORD.PREV/ SECRETS/SALTING_PRINCIPAL/DES/ This allows downgrades to older Samba versions. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 5f0038fba612afd7fc15b7ab321df979891170d8) --- source3/include/secrets.h | 28 + source3/passdb/machine_account_secrets.c | 1391 ++++++++++++++++++++++++++++++ 2 files changed, 1419 insertions(+) diff --git a/source3/include/secrets.h b/source3/include/secrets.h index fc8e118..0363b6b 100644 --- a/source3/include/secrets.h +++ b/source3/include/secrets.h @@ -29,6 +29,7 @@ #define SECRETS_MACHINE_LAST_CHANGE_TIME "SECRETS/MACHINE_LAST_CHANGE_TIME" #define SECRETS_MACHINE_SEC_CHANNEL_TYPE "SECRETS/MACHINE_SEC_CHANNEL_TYPE" #define SECRETS_MACHINE_TRUST_ACCOUNT_NAME "SECRETS/SECRETS_MACHINE_TRUST_ACCOUNT_NAME" +#define SECRETS_MACHINE_DOMAIN_INFO "SECRETS/MACHINE_DOMAIN_INFO" /* this one is for storing trusted domain account password */ #define SECRETS_DOMTRUST_ACCT_PASS "SECRETS/$DOMTRUST.ACC" @@ -110,6 +111,33 @@ bool secrets_fetch_trusted_domain_password(const char *domain, char** pwd, struct dom_sid *sid, time_t *pass_last_set_time); bool secrets_store_trusted_domain_password(const char* domain, const char* pwd, const struct dom_sid *sid); +struct libnet_JoinCtx; +NTSTATUS secrets_store_JoinCtx(const struct libnet_JoinCtx *r); +struct secrets_domain_info1; +struct secrets_domain_info1_change; +void secrets_debug_domain_info(int lvl, const struct secrets_domain_info1 *info, + const char *name); +char *secrets_domain_info_string(TALLOC_CTX *mem_ctx, const struct secrets_domain_info1 *info1, + const char *name, bool include_secrets); +NTSTATUS secrets_fetch_or_upgrade_domain_info(const char *domain, + TALLOC_CTX *mem_ctx, + struct secrets_domain_info1 **pinfo); +NTSTATUS secrets_prepare_password_change(const char *domain, const char *dcname, + const char *cleartext_unix, + TALLOC_CTX *mem_ctx, + struct secrets_domain_info1 **pinfo, + struct secrets_domain_info1_change **pprev); +NTSTATUS secrets_failed_password_change(const char *change_server, + NTSTATUS local_status, + NTSTATUS remote_status, + const struct secrets_domain_info1 *info); +NTSTATUS secrets_defer_password_change(const char *change_server, + NTSTATUS local_status, + NTSTATUS remote_status, + const struct secrets_domain_info1 *info); +NTSTATUS secrets_finish_password_change(const char *change_server, + NTTIME change_time, + const struct secrets_domain_info1 *info); bool secrets_delete_machine_password_ex(const char *domain, const char *realm); bool secrets_delete_domain_sid(const char *domain); bool secrets_store_machine_password(const char *pass, const char *domain, enum netr_SchannelType sec_channel); diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 9a96a3f..b88fbe9 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -31,9 +31,17 @@ #include "util_tdb.h" #include "libcli/security/security.h" +#include "librpc/gen_ndr/libnet_join.h" +#include "librpc/gen_ndr/ndr_secrets.h" +#include "lib/crypto/crypto.h" +#include "lib/krb5_wrap/krb5_samba.h" +#include "lib/util/time_basic.h" + #undef DBGC_CLASS #define DBGC_CLASS DBGC_PASSDB +static char *domain_info_keystr(const char *domain); + static char *des_salt_key(const char *realm); /** @@ -379,6 +387,12 @@ bool secrets_delete_machine_password_ex(const char *domain, const char *realm) const char *tmpkey = NULL; bool ok; + tmpkey = domain_info_keystr(domain); + ok = secrets_delete(tmpkey); + if (!ok) { + return false; + } + if (realm != NULL) { tmpkey = des_salt_key(domain); ok = secrets_delete(tmpkey); @@ -735,3 +749,1380 @@ char *secrets_fetch_machine_password(const char *domain, return ret; } + +static char *domain_info_keystr(const char *domain) +{ + char *keystr; + + keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s", + SECRETS_MACHINE_DOMAIN_INFO, + domain); + SMB_ASSERT(keystr != NULL); + return keystr; +} + +/************************************************************************ + Routine to get account password to trusted domain +************************************************************************/ + +static NTSTATUS secrets_fetch_domain_info1_by_key(const char *key, + TALLOC_CTX *mem_ctx, + struct secrets_domain_info1 **_info1) +{ + struct secrets_domain_infoB sdib = { .version = 0, }; + enum ndr_err_code ndr_err; + /* unpacking structures */ + DATA_BLOB blob; + + /* fetching trusted domain password structure */ + blob.data = (uint8_t *)secrets_fetch(key, &blob.length); + if (blob.data == NULL) { + DBG_NOTICE("secrets_fetch failed!\n"); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* unpack trusted domain password */ + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &sdib, + (ndr_pull_flags_fn_t)ndr_pull_secrets_domain_infoB); + SAFE_FREE(blob.data); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DBG_ERR("ndr_pull_struct_blob failed - %s!\n", + ndr_errstr(ndr_err)); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + if (sdib.version != SECRETS_DOMAIN_INFO_VERSION_1) { + DBG_ERR("sdib.version = %u\n", (unsigned)sdib.version); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + *_info1 = sdib.info.info1; + return NT_STATUS_OK;; +} + +static NTSTATUS secrets_fetch_domain_info(const char *domain, + TALLOC_CTX *mem_ctx, + struct secrets_domain_info1 **pinfo) +{ + char *key = domain_info_keystr(domain); + return secrets_fetch_domain_info1_by_key(key, mem_ctx, pinfo); +} + +void secrets_debug_domain_info(int lvl, const struct secrets_domain_info1 *info1, + const char *name) +{ + struct secrets_domain_infoB sdib = { + .version = SECRETS_DOMAIN_INFO_VERSION_1, + }; + + sdib.info.info1 = discard_const_p(struct secrets_domain_info1, info1); + + ndr_print_debug((ndr_print_fn_t)ndr_print_secrets_domain_infoB, + name, &sdib); +} + +char *secrets_domain_info_string(TALLOC_CTX *mem_ctx, const struct secrets_domain_info1 *info1, + const char *name, bool include_secrets) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct secrets_domain_infoB sdib = { + .version = SECRETS_DOMAIN_INFO_VERSION_1, + }; + struct ndr_print *ndr = NULL; + char *ret = NULL; + + sdib.info.info1 = discard_const_p(struct secrets_domain_info1, info1); + + ndr = talloc_zero(frame, struct ndr_print); + if (ndr == NULL) { + TALLOC_FREE(frame); + return NULL; + } + ndr->private_data = talloc_strdup(ndr, ""); + if (ndr->private_data == NULL) { + TALLOC_FREE(frame); + return NULL; + } + ndr->print = ndr_print_string_helper; + ndr->depth = 1; + ndr->print_secrets = include_secrets; + + ndr_print_secrets_domain_infoB(ndr, name, &sdib); + ret = talloc_steal(mem_ctx, (char *)ndr->private_data); + TALLOC_FREE(frame); + return ret; +} + +static NTSTATUS secrets_store_domain_info1_by_key(const char *key, + const struct secrets_domain_info1 *info1) +{ + struct secrets_domain_infoB sdib = { + .version = SECRETS_DOMAIN_INFO_VERSION_1, + }; + /* packing structures */ + DATA_BLOB blob; + enum ndr_err_code ndr_err; + bool ok; + + sdib.info.info1 = discard_const_p(struct secrets_domain_info1, info1); + + ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &sdib, + (ndr_push_flags_fn_t)ndr_push_secrets_domain_infoB); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ndr_map_error2ntstatus(ndr_err); + } + + ok = secrets_store(key, blob.data, blob.length); + data_blob_clear_free(&blob); + if (!ok) { + return NT_STATUS_INTERNAL_DB_ERROR; + } + + return NT_STATUS_OK; +} + +static NTSTATUS secrets_store_domain_info(const struct secrets_domain_info1 *info) +{ + TALLOC_CTX *frame = talloc_stackframe(); + const char *domain = info->domain_info.name.string; + const char *realm = info->domain_info.dns_domain.string; + char *key = domain_info_keystr(domain); + struct db_context *db = NULL; + struct timeval last_change_tv; + const DATA_BLOB *cleartext_blob = NULL; + DATA_BLOB pw_blob = data_blob_null; + DATA_BLOB old_pw_blob = data_blob_null; + const char *pw = NULL; + const char *old_pw = NULL; + bool ok; + NTSTATUS status; + int ret; + int role = lp_server_role(); + + switch (info->secure_channel_type) { + case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: + if (role >= ROLE_ACTIVE_DIRECTORY_DC) { + DBG_ERR("AD_DC not supported for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_ERROR; + } + + break; + default: + DBG_ERR("SEC_CHAN_* not supported for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_ERROR; + } + + db = secrets_db_ctx(); + + ret = dbwrap_transaction_start(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_start() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + ok = secrets_clear_domain_protection(domain); + if (!ok) { + DBG_ERR("secrets_clear_domain_protection(%s) failed\n", + domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + ok = secrets_delete_machine_password_ex(domain, realm); + if (!ok) { + DBG_ERR("secrets_delete_machine_password_ex(%s) failed\n", + domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + status = secrets_store_domain_info1_by_key(key, info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_store_domain_info1_by_key() failed " + "for %s - %s\n", domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + /* + * We use info->password_last_change instead + * of info->password.change_time because + * we may want to defer the next change approach + * if the server rejected the change the last time, + * e.g. due to RefusePasswordChange=1. + */ + nttime_to_timeval(&last_change_tv, info->password_last_change); + + cleartext_blob = &info->password->cleartext_blob; + ok = convert_string_talloc(frame, CH_UTF16MUNGED, CH_UNIX, + cleartext_blob->data, + cleartext_blob->length, + (void **)&pw_blob.data, + &pw_blob.length); + if (!ok) { + status = NT_STATUS_UNMAPPABLE_CHARACTER; + if (errno == ENOMEM) { + status = NT_STATUS_NO_MEMORY; + } + DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) " + "failed for pw of %s - %s\n", + domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + pw = (const char *)pw_blob.data; + if (info->old_password != NULL) { + cleartext_blob = &info->old_password->cleartext_blob; + ok = convert_string_talloc(frame, CH_UTF16MUNGED, CH_UNIX, + cleartext_blob->data, + cleartext_blob->length, + (void **)&old_pw_blob.data, + &old_pw_blob.length); + if (!ok) { + status = NT_STATUS_UNMAPPABLE_CHARACTER; + if (errno == ENOMEM) { + status = NT_STATUS_NO_MEMORY; + } + DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) " + "failed for old_pw of %s - %s\n", + domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + data_blob_clear_free(&pw_blob); + TALLOC_FREE(frame); + return status; + } + old_pw = (const char *)old_pw_blob.data; + } + + ok = secrets_store_machine_pw_sync(pw, old_pw, + domain, realm, + info->salt_principal, + info->supported_enc_types, + info->domain_info.sid, + last_change_tv.tv_sec, + info->secure_channel_type, + false); /* delete_join */ + data_blob_clear_free(&pw_blob); + data_blob_clear_free(&old_pw_blob); + if (!ok) { + DBG_ERR("secrets_store_machine_pw_sync(%s) failed\n", + domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + if (!GUID_all_zero(&info->domain_info.domain_guid)) { + ok = secrets_store_domain_guid(domain, + &info->domain_info.domain_guid); + if (!ok) { + DBG_ERR("secrets_store_domain_guid(%s) failed\n", + domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + } + + ok = secrets_mark_domain_protected(domain); + if (!ok) { + DBG_ERR("secrets_mark_domain_protected(%s) failed\n", + domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + ret = dbwrap_transaction_commit(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_commit() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} + +static int secrets_domain_info_kerberos_keys(struct secrets_domain_info1_password *p, + const char *salt_principal) +{ +#ifdef HAVE_ADS + krb5_error_code krb5_ret; + krb5_context krb5_ctx = NULL; + DATA_BLOB cleartext_utf8_b = data_blob_null; + krb5_data cleartext_utf8; + krb5_data salt; + krb5_keyblock key; + DATA_BLOB aes_256_b = data_blob_null; + DATA_BLOB aes_128_b = data_blob_null; + DATA_BLOB des_md5_b = data_blob_null; + bool ok; +#endif /* HAVE_ADS */ + DATA_BLOB arc4_b = data_blob_null; + const uint16_t max_keys = 4; + struct secrets_domain_info1_kerberos_key *keys = NULL; + uint16_t idx = 0; + char *salt_data = NULL; + + /* + * We calculate: + * ENCTYPE_AES256_CTS_HMAC_SHA1_96 + * ENCTYPE_AES128_CTS_HMAC_SHA1_96 + * ENCTYPE_ARCFOUR_HMAC + * ENCTYPE_DES_CBC_MD5 + * + * We don't include ENCTYPE_DES_CBC_CRC + * as W2008R2 also doesn't store it anymore. + * + * Note we store all enctypes we support, + * including the weak encryption types, + * but that's no problem as we also + * store the cleartext password anyway. + * + * Which values are then used to construct + * a keytab is configured at runtime and the + * configuration of msDS-SupportedEncryptionTypes. + * + * If we don't have kerberos support or no + * salt, we only generate an entry for arcfour-hmac-md5. + */ + keys = talloc_zero_array(p, + struct secrets_domain_info1_kerberos_key, + max_keys); + if (keys == NULL) { + return ENOMEM; + } + + arc4_b = data_blob_talloc(keys, + p->nt_hash.hash, + sizeof(p->nt_hash.hash)); + if (arc4_b.data == NULL) { + DBG_ERR("data_blob_talloc failed for arcfour-hmac-md5.\n"); + TALLOC_FREE(keys); + return ENOMEM; + } + +#ifdef HAVE_ADS + if (salt_principal == NULL) { + goto no_kerberos; + } + + initialize_krb5_error_table(); + krb5_ret = krb5_init_context(&krb5_ctx); + if (krb5_ret != 0) { + TALLOC_FREE(keys); + return krb5_ret; + } + + krb5_ret = smb_krb5_salt_principal2data(krb5_ctx, salt_principal, + p, &salt_data); + if (krb5_ret != 0) { + DBG_ERR("smb_krb5_salt_principal2data(%s) failed: %s\n", + salt_principal, + smb_get_krb5_error_message(krb5_ctx, krb5_ret, keys)); + krb5_free_context(krb5_ctx); + TALLOC_FREE(keys); + return krb5_ret; + } + + salt.data = discard_const(salt_data); + salt.length = strlen(salt_data); + + ok = convert_string_talloc(keys, CH_UTF16MUNGED, CH_UTF8, + p->cleartext_blob.data, + p->cleartext_blob.length, + (void **)&cleartext_utf8_b.data, + &cleartext_utf8_b.length); + if (!ok) { + if (errno != 0) { + krb5_ret = errno; + } else { + krb5_ret = EINVAL; + } + krb5_free_context(krb5_ctx); + TALLOC_FREE(keys); + return krb5_ret; + } + cleartext_utf8.data = (void *)cleartext_utf8_b.data; + cleartext_utf8.length = cleartext_utf8_b.length; + + krb5_ret = smb_krb5_create_key_from_string(krb5_ctx, + NULL, + &salt, + &cleartext_utf8, + ENCTYPE_AES256_CTS_HMAC_SHA1_96, + &key); + if (krb5_ret != 0) { + DBG_ERR("generation of a aes256-cts-hmac-sha1-96 key failed: %s\n", + smb_get_krb5_error_message(krb5_ctx, krb5_ret, keys)); + krb5_free_context(krb5_ctx); + TALLOC_FREE(keys); + TALLOC_FREE(salt_data); + return krb5_ret; + } + aes_256_b = data_blob_talloc(keys, + KRB5_KEY_DATA(&key), + KRB5_KEY_LENGTH(&key)); + krb5_free_keyblock_contents(krb5_ctx, &key); + if (aes_256_b.data == NULL) { + DBG_ERR("data_blob_talloc failed for aes-256.\n"); + krb5_free_context(krb5_ctx); + TALLOC_FREE(keys); + TALLOC_FREE(salt_data); + return ENOMEM; + } + + krb5_ret = smb_krb5_create_key_from_string(krb5_ctx, + NULL, + &salt, + &cleartext_utf8, + ENCTYPE_AES128_CTS_HMAC_SHA1_96, + &key); + if (krb5_ret != 0) { + DBG_ERR("generation of a aes128-cts-hmac-sha1-96 key failed: %s\n", + smb_get_krb5_error_message(krb5_ctx, krb5_ret, keys)); + krb5_free_context(krb5_ctx); + TALLOC_FREE(keys); + TALLOC_FREE(salt_data); + return krb5_ret; + } + aes_128_b = data_blob_talloc(keys, + KRB5_KEY_DATA(&key), + KRB5_KEY_LENGTH(&key)); + krb5_free_keyblock_contents(krb5_ctx, &key); + if (aes_128_b.data == NULL) { + DBG_ERR("data_blob_talloc failed for aes-128.\n"); + krb5_free_context(krb5_ctx); + TALLOC_FREE(keys); + TALLOC_FREE(salt_data); + return ENOMEM; + } + + krb5_ret = smb_krb5_create_key_from_string(krb5_ctx, + NULL, + &salt, + &cleartext_utf8, + ENCTYPE_DES_CBC_MD5, + &key); + if (krb5_ret != 0) { + DBG_ERR("generation of a des-cbc-md5 key failed: %s\n", + smb_get_krb5_error_message(krb5_ctx, krb5_ret, keys)); + krb5_free_context(krb5_ctx); + TALLOC_FREE(keys); + TALLOC_FREE(salt_data); + return krb5_ret; + } + des_md5_b = data_blob_talloc(keys, + KRB5_KEY_DATA(&key), + KRB5_KEY_LENGTH(&key)); + krb5_free_keyblock_contents(krb5_ctx, &key); + if (des_md5_b.data == NULL) { + DBG_ERR("data_blob_talloc failed for des-cbc-md5.\n"); + krb5_free_context(krb5_ctx); + TALLOC_FREE(keys); + TALLOC_FREE(salt_data); + return ENOMEM; + } + + krb5_free_context(krb5_ctx); +no_kerberos: + + if (aes_256_b.length != 0) { + keys[idx].keytype = ENCTYPE_AES256_CTS_HMAC_SHA1_96; + keys[idx].iteration_count = 4096; + keys[idx].value = aes_256_b; + idx += 1; + } + + if (aes_128_b.length != 0) { + keys[idx].keytype = ENCTYPE_AES128_CTS_HMAC_SHA1_96; + keys[idx].iteration_count = 4096; + keys[idx].value = aes_128_b; + idx += 1; + } + +#endif /* HAVE_ADS */ + + keys[idx].keytype = ENCTYPE_ARCFOUR_HMAC; + keys[idx].iteration_count = 4096; + keys[idx].value = arc4_b; + idx += 1; + +#ifdef HAVE_ADS + if (des_md5_b.length != 0) { + keys[idx].keytype = ENCTYPE_DES_CBC_MD5; + keys[idx].iteration_count = 4096; + keys[idx].value = des_md5_b; + idx += 1; + } +#endif /* HAVE_ADS */ + + p->salt_data = salt_data; + p->default_iteration_count = 4096; + p->num_keys = idx; + p->keys = keys; + return 0; +} + +static NTSTATUS secrets_domain_info_password_create(TALLOC_CTX *mem_ctx, + const char *cleartext_unix, + const char *salt_principal, + NTTIME change_time, + const char *change_server, + struct secrets_domain_info1_password **_p) +{ + struct secrets_domain_info1_password *p = NULL; + bool ok; + size_t len; + int ret; + + if (change_server == NULL) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + + p = talloc_zero(mem_ctx, struct secrets_domain_info1_password); + if (p == NULL) { + return NT_STATUS_NO_MEMORY; + } + p->change_time = change_time; + p->change_server = talloc_strdup(p, change_server); + if (p->change_server == NULL) { + TALLOC_FREE(p); + return NT_STATUS_NO_MEMORY; + } + len = strlen(cleartext_unix); + ok = convert_string_talloc(p, CH_UNIX, CH_UTF16, + cleartext_unix, len, + (void **)&p->cleartext_blob.data, + &p->cleartext_blob.length); + if (!ok) { + NTSTATUS status = NT_STATUS_UNMAPPABLE_CHARACTER; + if (errno == ENOMEM) { + status = NT_STATUS_NO_MEMORY; + } + TALLOC_FREE(p); + return status; + } + mdfour(p->nt_hash.hash, + p->cleartext_blob.data, + p->cleartext_blob.length); + + ret = secrets_domain_info_kerberos_keys(p, salt_principal); + if (ret != 0) { + NTSTATUS status = krb5_to_nt_status(ret); + TALLOC_FREE(p); + return status; + } + + *_p = p; + return NT_STATUS_OK; +} + +NTSTATUS secrets_fetch_or_upgrade_domain_info(const char *domain, + TALLOC_CTX *mem_ctx, + struct secrets_domain_info1 **pinfo) +{ + TALLOC_CTX *frame = NULL; + struct secrets_domain_info1 *old = NULL; + struct secrets_domain_info1 *info = NULL; + const char *dns_domain = NULL; + const char *server = NULL; + struct db_context *db = NULL; + time_t last_set_time; + NTTIME last_set_nt; + enum netr_SchannelType channel; + char *pw = NULL; + char *old_pw = NULL; + struct dom_sid domain_sid; + struct GUID domain_guid; + bool ok; + NTSTATUS status; + int ret; + + ok = strequal(domain, lp_workgroup()); + if (ok) { + dns_domain = lp_dnsdomain(); + + if (dns_domain != NULL && dns_domain[0] == '\0') { + dns_domain = NULL; + } + } + + last_set_time = secrets_fetch_pass_last_set_time(domain); + if (last_set_time == 0) { + return NT_STATUS_OK; + } + unix_to_nt_time(&last_set_nt, last_set_time); + + frame = talloc_stackframe(); + + status = secrets_fetch_domain_info(domain, frame, &old); + if (NT_STATUS_IS_OK(status)) { + if (old->password_last_change >= last_set_nt) { + *pinfo = talloc_move(mem_ctx, &old); + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + TALLOC_FREE(old); + } + + info = talloc_zero(frame, struct secrets_domain_info1); + if (info == NULL) { + DBG_ERR("talloc_zero failed\n"); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + db = secrets_db_ctx(); + + ret = dbwrap_transaction_start(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_start() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + pw = secrets_fetch_machine_password(domain, + &last_set_time, + &channel); + if (pw == NULL) { + DBG_ERR("secrets_fetch_machine_password(%s) failed\n", + domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + unix_to_nt_time(&last_set_nt, last_set_time); + + old_pw = secrets_fetch_prev_machine_password(domain); + + ok = secrets_fetch_domain_sid(domain, &domain_sid); + if (!ok) { + DBG_ERR("secrets_fetch_domain_sid(%s) failed\n", + domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + ok = secrets_fetch_domain_guid(domain, &domain_guid); + if (!ok) { + domain_guid = GUID_zero(); + } + + info->computer_name = lp_netbios_name(); + info->account_name = talloc_asprintf(frame, "%s$", info->computer_name); + if (info->account_name == NULL) { + DBG_ERR("talloc_asprintf(%s$) failed\n", info->computer_name); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + info->secure_channel_type = channel; + + info->domain_info.name.string = domain; + info->domain_info.dns_domain.string = dns_domain; + info->domain_info.dns_forest.string = dns_domain; + info->domain_info.domain_guid = domain_guid; + info->domain_info.sid = &domain_sid; + + info->trust_flags = NETR_TRUST_FLAG_PRIMARY; + info->trust_flags |= NETR_TRUST_FLAG_OUTBOUND; + + if (dns_domain != NULL) { + /* + * We just assume all AD domains are + * NETR_TRUST_FLAG_NATIVE these days. + * + * This isn't used anyway for now. + */ + info->trust_flags |= NETR_TRUST_FLAG_NATIVE; + + info->trust_type = LSA_TRUST_TYPE_UPLEVEL; + + server = info->domain_info.dns_domain.string; + } else { + info->trust_type = LSA_TRUST_TYPE_DOWNLEVEL; + + server = talloc_asprintf(info, + "%s#%02X", + domain, + NBT_NAME_PDC); + if (server == NULL) { + DBG_ERR("talloc_asprintf(%s#%02X) failed\n", + domain, NBT_NAME_PDC); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + info->trust_attributes = LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL; + + info->join_time = 0; + + /* + * We don't have enough information about the configured + * enctypes. + */ + info->supported_enc_types = 0; + info->salt_principal = NULL; + if (info->trust_type == LSA_TRUST_TYPE_UPLEVEL) { + char *p = NULL; + + p = kerberos_secrets_fetch_salt_princ(); + if (p == NULL) { + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_ERROR; + } + info->salt_principal = talloc_strdup(info, p); + SAFE_FREE(p); + if (info->salt_principal == NULL) { + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + + info->password_last_change = last_set_nt; + info->password_changes = 1; + info->next_change = NULL; + + status = secrets_domain_info_password_create(info, + pw, + info->salt_principal, + last_set_nt, server, + &info->password); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_domain_info_password_create(pw) failed " + "for %s - %s\n", domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + /* + * After a join we don't have old passwords. + */ + if (old_pw != NULL) { + status = secrets_domain_info_password_create(info, + old_pw, + info->salt_principal, + 0, server, + &info->old_password); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_domain_info_password_create(old) failed " + "for %s - %s\n", domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + info->password_changes += 1; + } else { + info->old_password = NULL; + } + info->older_password = NULL; + + secrets_debug_domain_info(DBGLVL_INFO, info, "upgrade"); + + status = secrets_store_domain_info(info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_store_domain_info() failed " + "for %s - %s\n", domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + /* + * We now reparse it. + */ + status = secrets_fetch_domain_info(domain, frame, &info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_fetch_domain_info() failed " + "for %s - %s\n", domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + ret = dbwrap_transaction_commit(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_commit() failed for %s\n", + domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + *pinfo = talloc_move(mem_ctx, &info); + TALLOC_FREE(frame); + return NT_STATUS_OK; +} + +NTSTATUS secrets_store_JoinCtx(const struct libnet_JoinCtx *r) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct secrets_domain_info1 *old = NULL; + struct secrets_domain_info1 *info = NULL; + struct db_context *db = NULL; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); + const char *domain = r->out.netbios_domain_name; + NTSTATUS status; + int ret; + + info = talloc_zero(frame, struct secrets_domain_info1); + if (info == NULL) { + DBG_ERR("talloc_zero failed\n"); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + info->computer_name = r->in.machine_name; + info->account_name = r->out.account_name; + info->secure_channel_type = r->in.secure_channel_type; + + info->domain_info.name.string = + r->out.netbios_domain_name; + info->domain_info.dns_domain.string = + r->out.dns_domain_name; + info->domain_info.dns_forest.string = + r->out.forest_name; + info->domain_info.domain_guid = r->out.domain_guid; + info->domain_info.sid = r->out.domain_sid; + + info->trust_flags = NETR_TRUST_FLAG_PRIMARY; + info->trust_flags |= NETR_TRUST_FLAG_OUTBOUND; + if (r->out.domain_is_ad) { + /* + * We just assume all AD domains are + * NETR_TRUST_FLAG_NATIVE these days. + * + * This isn't used anyway for now. + */ + info->trust_flags |= NETR_TRUST_FLAG_NATIVE; + + info->trust_type = LSA_TRUST_TYPE_UPLEVEL; + } else { + info->trust_type = LSA_TRUST_TYPE_DOWNLEVEL; + } + info->trust_attributes = LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL; + + info->join_time = now; + + info->supported_enc_types = r->out.set_encryption_types; + info->salt_principal = r->out.krb5_salt; + + if (info->salt_principal == NULL && r->out.domain_is_ad) { + char *p = NULL; + + ret = smb_krb5_salt_principal(info->domain_info.dns_domain.string, + info->account_name, + NULL /* userPrincipalName */, + true /* is_computer */, + info, &p); + if (ret != 0) { + status = krb5_to_nt_status(ret); + DBG_ERR("smb_krb5_salt_principal() failed " + "for %s - %s\n", domain, nt_errstr(status)); + TALLOC_FREE(frame); + return status; + } + info->salt_principal = p; + } + + info->password_last_change = now; + info->password_changes = 1; + info->next_change = NULL; + + status = secrets_domain_info_password_create(info, + r->in.machine_password, + info->salt_principal, + now, r->in.dc_name, + &info->password); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_domain_info_password_create(pw) failed " + "for %s - %s\n", domain, nt_errstr(status)); + TALLOC_FREE(frame); + return status; + } + + db = secrets_db_ctx(); + + ret = dbwrap_transaction_start(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_start() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + status = secrets_fetch_or_upgrade_domain_info(domain, frame, &old); + if (NT_STATUS_EQUAL(status, NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) { + DBG_DEBUG("no old join for domain(%s) available\n", + domain); + old = NULL; + } else if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_fetch_or_upgrade_domain_info(%s) failed\n", + domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + /* + * We reuse values from an old join, so that + * we still accept already granted kerberos tickets. + */ + if (old != NULL) { + info->old_password = old->password; + info->older_password = old->old_password; + } + + secrets_debug_domain_info(DBGLVL_INFO, info, "join"); + + status = secrets_store_domain_info(info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_store_domain_info() failed " + "for %s - %s\n", domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + ret = dbwrap_transaction_commit(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_commit() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} + +NTSTATUS secrets_prepare_password_change(const char *domain, const char *dcname, + const char *cleartext_unix, + TALLOC_CTX *mem_ctx, + struct secrets_domain_info1 **pinfo, + struct secrets_domain_info1_change **pprev) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct db_context *db = NULL; + struct secrets_domain_info1 *info = NULL; + struct secrets_domain_info1_change *prev = NULL; + struct secrets_domain_info1_change *next = NULL; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); + NTSTATUS status; + int ret; + + db = secrets_db_ctx(); + + ret = dbwrap_transaction_start(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_start() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + status = secrets_fetch_or_upgrade_domain_info(domain, frame, &info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_fetch_or_upgrade_domain_info(%s) failed\n", + domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + prev = info->next_change; + info->next_change = NULL; + + next = talloc_zero(frame, struct secrets_domain_info1_change); + if (next == NULL) { + DBG_ERR("talloc_zero failed\n"); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + if (prev != NULL) { + *next = *prev; + } else { + status = secrets_domain_info_password_create(next, + cleartext_unix, + info->salt_principal, + now, dcname, + &next->password); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_domain_info_password_create(next) failed " + "for %s - %s\n", domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + } + + next->local_status = NT_STATUS_OK; + next->remote_status = NT_STATUS_NOT_COMMITTED; + next->change_time = now; + next->change_server = dcname; + + info->next_change = next; + + secrets_debug_domain_info(DBGLVL_INFO, info, "prepare_change"); + + status = secrets_store_domain_info(info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_store_domain_info() failed " + "for %s - %s\n", domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + /* + * We now reparse it. + */ + status = secrets_fetch_domain_info(domain, frame, &info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_fetch_domain_info(%s) failed\n", domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + ret = dbwrap_transaction_commit(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_commit() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + *pinfo = talloc_move(mem_ctx, &info); + if (prev != NULL) { + *pprev = talloc_move(mem_ctx, &prev); + } else { + *pprev = NULL; + } + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} + +static NTSTATUS secrets_check_password_change(const struct secrets_domain_info1 *cookie, + TALLOC_CTX *mem_ctx, + struct secrets_domain_info1 **pstored) +{ + const char *domain = cookie->domain_info.name.string; + struct secrets_domain_info1 *stored = NULL; + struct secrets_domain_info1_change *sn = NULL; + struct secrets_domain_info1_change *cn = NULL; + NTSTATUS status; + int cmp; + + if (cookie->next_change == NULL) { + DBG_ERR("cookie->next_change == NULL for %s.\n", domain); + return NT_STATUS_INTERNAL_ERROR; + } + + if (cookie->next_change->password == NULL) { + DBG_ERR("cookie->next_change->password == NULL for %s.\n", domain); + return NT_STATUS_INTERNAL_ERROR; + } + + if (cookie->password == NULL) { + DBG_ERR("cookie->password == NULL for %s.\n", domain); + return NT_STATUS_INTERNAL_ERROR; + } + + /* + * Here we check that the given strucure still contains the + * same secrets_domain_info1_change as currently stored. + * + * There's always a gap between secrets_prepare_password_change() + * and the callers of secrets_check_password_change(). + */ + + status = secrets_fetch_domain_info(domain, mem_ctx, &stored); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_fetch_domain_info(%s) failed\n", domain); + return status; + } + + if (stored->next_change == NULL) { + /* + * We hit a race..., the administrator + * rejoined or something similar happened. + */ + DBG_ERR("stored->next_change == NULL for %s.\n", domain); + TALLOC_FREE(stored); + return NT_STATUS_NETWORK_CREDENTIAL_CONFLICT; + } + + if (stored->password_last_change != cookie->password_last_change) { + struct timeval store_tv; + struct timeval_buf store_buf; + struct timeval cookie_tv; + struct timeval_buf cookie_buf; + + nttime_to_timeval(&store_tv, stored->password_last_change); + nttime_to_timeval(&cookie_tv, cookie->password_last_change); + + DBG_ERR("password_last_change differs %s != %s for %s.\n", + timeval_str_buf(&store_tv, false, false, &store_buf), + timeval_str_buf(&cookie_tv, false, false, &cookie_buf), + domain); + TALLOC_FREE(stored); + return NT_STATUS_NETWORK_CREDENTIAL_CONFLICT; + } + + sn = stored->next_change; + cn = cookie->next_change; + + if (sn->change_time != cn->change_time) { + struct timeval store_tv; + struct timeval_buf store_buf; + struct timeval cookie_tv; + struct timeval_buf cookie_buf; + + nttime_to_timeval(&store_tv, sn->change_time); + nttime_to_timeval(&cookie_tv, cn->change_time); + + DBG_ERR("next change_time differs %s != %s for %s.\n", + timeval_str_buf(&store_tv, false, false, &store_buf), + timeval_str_buf(&cookie_tv, false, false, &cookie_buf), + domain); + TALLOC_FREE(stored); + return NT_STATUS_NETWORK_CREDENTIAL_CONFLICT; + } + + if (sn->password->change_time != cn->password->change_time) { + struct timeval store_tv; + struct timeval_buf store_buf; + struct timeval cookie_tv; + struct timeval_buf cookie_buf; + + nttime_to_timeval(&store_tv, sn->password->change_time); + nttime_to_timeval(&cookie_tv, cn->password->change_time); + + DBG_ERR("next password.change_time differs %s != %s for %s.\n", + timeval_str_buf(&store_tv, false, false, &store_buf), + timeval_str_buf(&cookie_tv, false, false, &cookie_buf), + domain); + TALLOC_FREE(stored); + return NT_STATUS_NETWORK_CREDENTIAL_CONFLICT; + } + + cmp = memcmp(sn->password->nt_hash.hash, + cn->password->nt_hash.hash, + 16); + if (cmp != 0) { + DBG_ERR("next password.nt_hash differs for %s.\n", + domain); + TALLOC_FREE(stored); + return NT_STATUS_NETWORK_CREDENTIAL_CONFLICT; + } + + cmp = memcmp(stored->password->nt_hash.hash, + cookie->password->nt_hash.hash, + 16); + if (cmp != 0) { + DBG_ERR("password.nt_hash differs for %s.\n", + domain); + TALLOC_FREE(stored); + return NT_STATUS_NETWORK_CREDENTIAL_CONFLICT; + } + + *pstored = stored; + return NT_STATUS_OK; +} + +static NTSTATUS secrets_abort_password_change(const char *change_server, + NTSTATUS local_status, + NTSTATUS remote_status, + const struct secrets_domain_info1 *cookie, + bool defer) +{ + const char *domain = cookie->domain_info.name.string; + TALLOC_CTX *frame = talloc_stackframe(); + struct db_context *db = NULL; + struct secrets_domain_info1 *info = NULL; + const char *reason = defer ? "defer_change" : "failed_change"; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); + NTSTATUS status; + int ret; + + db = secrets_db_ctx(); + + ret = dbwrap_transaction_start(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_start() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + /* + * secrets_check_password_change() + * checks that cookie->next_change + * is valid and the same as store + * in the database. + */ + status = secrets_check_password_change(cookie, frame, &info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_check_password_change(%s) failed\n", domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + /* + * Remember the last server and error. + */ + info->next_change->change_server = change_server; + info->next_change->change_time = now; + info->next_change->local_status = local_status; + info->next_change->remote_status = remote_status; + + /* + * Make sure the next automatic change is deferred. + */ + if (defer) { + info->password_last_change = now; + } + + secrets_debug_domain_info(DBGLVL_WARNING, info, reason); + + status = secrets_store_domain_info(info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_store_domain_info() failed " + "for %s - %s\n", domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + ret = dbwrap_transaction_commit(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_commit() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} + +NTSTATUS secrets_failed_password_change(const char *change_server, + NTSTATUS local_status, + NTSTATUS remote_status, + const struct secrets_domain_info1 *cookie) +{ + static const bool defer = false; + return secrets_abort_password_change(change_server, + local_status, + remote_status, + cookie, defer); +} + +NTSTATUS secrets_defer_password_change(const char *change_server, + NTSTATUS local_status, + NTSTATUS remote_status, + const struct secrets_domain_info1 *cookie) +{ + static const bool defer = true; + return secrets_abort_password_change(change_server, + local_status, + remote_status, + cookie, defer); +} + +NTSTATUS secrets_finish_password_change(const char *change_server, + NTTIME change_time, + const struct secrets_domain_info1 *cookie) +{ + const char *domain = cookie->domain_info.name.string; + TALLOC_CTX *frame = talloc_stackframe(); + struct db_context *db = NULL; + struct secrets_domain_info1 *info = NULL; + struct secrets_domain_info1_change *nc = NULL; + NTSTATUS status; + int ret; + + db = secrets_db_ctx(); + + ret = dbwrap_transaction_start(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_start() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + /* + * secrets_check_password_change() checks that cookie->next_change is + * valid and the same as store in the database. + */ + status = secrets_check_password_change(cookie, frame, &info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_check_password_change(%s) failed\n", domain); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + nc = info->next_change; + + nc->password->change_server = change_server; + nc->password->change_time = change_time; + + info->password_last_change = change_time; + info->password_changes += 1; + info->next_change = NULL; + + info->older_password = info->old_password; + info->old_password = info->password; + info->password = nc->password; + + secrets_debug_domain_info(DBGLVL_WARNING, info, "finish_change"); + + status = secrets_store_domain_info(info); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_store_domain_info() failed " + "for %s - %s\n", domain, nt_errstr(status)); + dbwrap_transaction_cancel(db); + TALLOC_FREE(frame); + return status; + } + + ret = dbwrap_transaction_commit(db); + if (ret != 0) { + DBG_ERR("dbwrap_transaction_commit() failed for %s\n", + domain); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_ERROR; + } + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} -- 1.9.1 From 7b5e86e0d5ec0675e2e8bbf690981ca5dd4d67d0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 24 May 2017 18:05:40 +0200 Subject: [PATCH 52/58] net: add "net primarytrust dumpinfo" command that dumps the details of the workstation trust BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit c7c17d9f503d6037aa8ed0bd7ab7cf52f5f28382) --- source3/utils/net.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/source3/utils/net.c b/source3/utils/net.c index 1bf31f2..fcf9942 100644 --- a/source3/utils/net.c +++ b/source3/utils/net.c @@ -91,6 +91,83 @@ static void set_line_buffering(FILE *f) setvbuf(f, NULL, _IOLBF, 0); } +static int net_primarytrust_dumpinfo(struct net_context *c, int argc, + const char **argv) +{ + int role = lp_server_role(); + const char *domain = lp_workgroup(); + struct secrets_domain_info1 *info = NULL; + bool include_secrets = c->opt_force; + char *str = NULL; + NTSTATUS status; + + if (role >= ROLE_ACTIVE_DIRECTORY_DC) { + d_printf(_("net primarytrust dumpinfo is only supported " + "on a DOMAIN_MEMBER for now.\n")); + return 1; + } + + if (c->opt_stdin) { + set_line_buffering(stdin); + set_line_buffering(stdout); + set_line_buffering(stderr); + } + + status = secrets_fetch_or_upgrade_domain_info(domain, + talloc_tos(), + &info); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Unable to fetch the information for domain[%s] " + "in the secrets database.\n"), + domain); + return 1; + } + + str = secrets_domain_info_string(info, info, domain, include_secrets); + if (str == NULL) { + d_fprintf(stderr, "secrets_domain_info_string() failed.\n"); + return 1; + } + + d_printf("%s", str); + if (!c->opt_force) { + d_printf(_("The password values are only included using " + "-f flag.\n")); + } + + TALLOC_FREE(info); + return 0; +} + +/** + * Entrypoint for 'net primarytrust' code. + * + * @param argc Standard argc. + * @param argv Standard argv without initial components. + * + * @return Integer status (0 means success). + */ + +static int net_primarytrust(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "dumpinfo", + net_primarytrust_dumpinfo, + NET_TRANSPORT_LOCAL, + N_("Dump the details of the workstation trust"), + N_(" net [options] primarytrust dumpinfo'\n" + " Dump the details of the workstation trust " + "in secrets.tdb.\n" + " Requires the -f flag to include the password values.") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net primarytrust", func); +} + static int net_changesecretpw(struct net_context *c, int argc, const char **argv) { @@ -570,6 +647,14 @@ static struct functable net_func[] = { N_(" Use 'net help password' to get more information about " "'net password' commands.") }, + { + "primarytrust", + net_primarytrust, + NET_TRANSPORT_RPC, + N_("Run functions related to the primary workstation trust."), + N_(" Use 'net help primarytrust' to get more extensive information " + "about 'net primarytrust' commands.") + }, { "changetrustpw", net_changetrustpw, NET_TRANSPORT_ADS | NET_TRANSPORT_RPC, -- 1.9.1 From dd94aedabb264556170d9270015cabfd61541301 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 10:29:59 +0200 Subject: [PATCH 53/58] s3:libnet: make use of secrets_store_JoinCtx() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit c3ad8be5d5192070c599350d6ab28c064206b6cf) --- source3/libnet/libnet_join.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 6f497d4..513f83a 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -987,31 +987,15 @@ static ADS_STATUS libnet_join_post_processing_ads_sync(TALLOC_CTX *mem_ctx, static bool libnet_join_joindomain_store_secrets(TALLOC_CTX *mem_ctx, struct libnet_JoinCtx *r) { - if (!secrets_store_domain_sid(r->out.netbios_domain_name, - r->out.domain_sid)) - { - DEBUG(1,("Failed to save domain sid\n")); - return false; - } + NTSTATUS status; - if (!secrets_store_machine_password(r->in.machine_password, - r->out.netbios_domain_name, - r->in.secure_channel_type)) - { - DEBUG(1,("Failed to save machine password\n")); + status = secrets_store_JoinCtx(r); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("secrets_store_JoinCtx() failed %s\n", + nt_errstr(status)); return false; } - if (r->out.krb5_salt != NULL) { - bool ok; - - ok = kerberos_secrets_store_des_salt(r->out.krb5_salt); - if (!ok) { - DEBUG(1,("Failed to save krb5 salt\n")); - return false; - } - } - return true; } -- 1.9.1 From 96cad5d5221cb2ebbc3e39f5ebebe59f8992c1ee Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 20:47:17 +0200 Subject: [PATCH 54/58] s3:trusts_util: make use the workstation password change more robust We use secrets_{prepare,failed,defer,finish}_password_change() to make the process more robust. Even if we just just verified the current password with the DC it can still happen that the remote password change will fail. If a server has the RefusePasswordChange=1 under HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters, it will reject NetrServerPasswordSet2() with NT_STATUS_WRONG_PASSWORD. This results in a successful local change, but a failing remote change, which means the domain membership is broken (as we don't fallback to the previous password for ntlmssp nor kerberos yet). An (at least Samba) RODC will also reject a password change, see https://bugzilla.samba.org/show_bug.cgi?id=12773. Even with this change we still have open problems, e.g. if the password was changed, but we didn't get the servers response. In order to fix that we need to use only netlogon and lsa over unprotected transports, just using schannel authentication (which supports the fallback to the old password). BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 40c42af11fda062fef9df96a9b5ae3e02709f07c) --- source3/libsmb/trusts_util.c | 204 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 187 insertions(+), 17 deletions(-) diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c index ff7f256..57cd542 100644 --- a/source3/libsmb/trusts_util.c +++ b/source3/libsmb/trusts_util.c @@ -24,6 +24,7 @@ #include "rpc_client/cli_netlogon.h" #include "rpc_client/cli_pipe.h" #include "../librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/secrets.h" #include "secrets.h" #include "passdb.h" #include "libsmb/libsmb.h" @@ -114,11 +115,13 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, const char *context_name = NULL; struct trust_pw_change_state *state; struct cli_credentials *creds = NULL; + struct secrets_domain_info1 *info = NULL; + struct secrets_domain_info1_change *prev = NULL; const struct samr_Password *current_nt_hash = NULL; const struct samr_Password *previous_nt_hash = NULL; uint8_t num_nt_hashes = 0; uint8_t idx = 0; - const struct samr_Password *nt_hashes[1+1] = { NULL, }; + const struct samr_Password *nt_hashes[1+3] = { NULL, }; uint8_t idx_nt_hashes = 0; uint8_t idx_current = UINT8_MAX; enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL; @@ -270,10 +273,75 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, return status; } - idx_current = idx; - nt_hashes[idx++] = current_nt_hash; - if (previous_nt_hash != NULL) { - nt_hashes[idx++] = previous_nt_hash; + switch (sec_channel_type) { + + case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: + status = secrets_prepare_password_change(domain, dcname, + new_trust_pw_str, + frame, &info, &prev); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + TALLOC_FREE(new_trust_pw_str); + + if (prev != NULL) { + /* + * We had a failure before we changed the password. + */ + nt_hashes[idx++] = &prev->password->nt_hash; + + DEBUG(0,("%s : %s(%s): A password change was already " + "started against '%s' at %s. Trying to " + "recover...\n", + current_timestring(talloc_tos(), false), + __func__, domain, + prev->password->change_server, + nt_time_string(talloc_tos(), + prev->password->change_time))); + DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] " + "against '%s' at %s.\n", + current_timestring(talloc_tos(), false), + __func__, domain, + nt_errstr(prev->local_status), + nt_errstr(prev->remote_status), + prev->change_server, + nt_time_string(talloc_tos(), + prev->change_time))); + } + + idx_current = idx; + nt_hashes[idx++] = &info->password->nt_hash; + if (info->old_password != NULL) { + nt_hashes[idx++] = &info->old_password->nt_hash; + } + if (info->older_password != NULL) { + nt_hashes[idx++] = &info->older_password->nt_hash; + } + + /* + * We use the password that's already persitent in + * our database in order to handle failures. + */ + data_blob_clear_free(&new_trust_pw_blob); + new_trust_pw_blob = info->next_change->password->cleartext_blob; + break; + + case SEC_CHAN_DNS_DOMAIN: + case SEC_CHAN_DOMAIN: + idx_current = idx; + nt_hashes[idx++] = current_nt_hash; + if (previous_nt_hash != NULL) { + nt_hashes[idx++] = previous_nt_hash; + } + break; + + default: + smb_panic("Unsupported secure channel type"); + break; } num_nt_hashes = idx; @@ -301,11 +369,50 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, return status; } + if (prev != NULL && idx_nt_hashes == 0) { + DEBUG(0,("%s : %s(%s): Verified new password remotely " + "without changing %s\n", + current_timestring(talloc_tos(), false), + __func__, domain, context_name)); + + status = secrets_finish_password_change(prev->password->change_server, + prev->password->change_time, + info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + DEBUG(0,("%s : %s(%s): Recovered previous password change.\n", + current_timestring(talloc_tos(), false), + __func__, domain)); + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + if (idx_nt_hashes != idx_current) { DEBUG(0,("%s : %s(%s): Verified older password remotely " "skip changing %s\n", current_timestring(talloc_tos(), false), __func__, domain, context_name)); + + if (info == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; + } + + status = secrets_defer_password_change(dcname, + NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE, + NT_STATUS_NOT_COMMITTED, + info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } TALLOC_FREE(frame); return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; } @@ -323,16 +430,9 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, case SEC_CHAN_WKSTA: case SEC_CHAN_BDC: - ok = secrets_store_machine_password(new_trust_pw_str, - domain, - sec_channel_type); - if (!ok) { - DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n", - domain)); - TALLOC_FREE(frame); - return NT_STATUS_INTERNAL_DB_CORRUPTION; - } - TALLOC_FREE(new_trust_pw_str); + /* + * we called secrets_prepare_password_change() above. + */ break; case SEC_CHAN_DNS_DOMAIN: @@ -364,10 +464,48 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, &new_trust_pw_blob, new_trust_version); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("%s : %s(%s) remote password change set with %s failed - %s\n", + NTSTATUS status2; + const char *fn = NULL; + + ok = dcerpc_binding_handle_is_connected(b); + + DEBUG(0,("%s : %s(%s) remote password change with %s failed " + "- %s (%s)\n", current_timestring(talloc_tos(), false), __func__, domain, context_name, - nt_errstr(status))); + nt_errstr(status), + ok ? "connected": "disconnected")); + + if (!ok) { + /* + * The connection is broken, we don't + * know if the password was changed, + * we hope to have more luck next time. + */ + status2 = secrets_failed_password_change(dcname, + NT_STATUS_NOT_COMMITTED, + status, + info); + fn = "secrets_failed_password_change"; + } else { + /* + * The server rejected the change, we don't + * retry and defer the change to the next + * "machine password timeout" interval. + */ + status2 = secrets_defer_password_change(dcname, + NT_STATUS_NOT_COMMITTED, + status, + info); + fn = "secrets_defer_password_change"; + } + if (!NT_STATUS_IS_OK(status2)) { + DEBUG(0, ("%s() failed for domain %s!\n", + fn, domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + TALLOC_FREE(frame); return status; } @@ -376,6 +514,38 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, current_timestring(talloc_tos(), false), __func__, domain, context_name)); + switch (sec_channel_type) { + + case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: + status = secrets_finish_password_change( + info->next_change->change_server, + info->next_change->change_time, + info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + DEBUG(0,("%s : %s(%s): Finished password change.\n", + current_timestring(talloc_tos(), false), + __func__, domain)); + break; + + case SEC_CHAN_DNS_DOMAIN: + case SEC_CHAN_DOMAIN: + /* + * we used pdb_set_trusteddom_pw(). + */ + break; + + default: + smb_panic("Unsupported secure channel type"); + break; + } + ok = cli_credentials_set_utf16_password(creds, &new_trust_pw_blob, CRED_SPECIFIED); -- 1.9.1 From a5b4ea9f37932cfcf13d9b9e26e17ef4116443c6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 23 May 2017 17:29:31 +0200 Subject: [PATCH 55/58] net: make use of secrets_*_password_change() for "net changesecretpw" BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 4ae6a3ffb233c9b9576a3b5bb15a51ee56e4dbc3) --- source3/utils/net.c | 51 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/source3/utils/net.c b/source3/utils/net.c index fcf9942..c4923d0 100644 --- a/source3/utils/net.c +++ b/source3/utils/net.c @@ -172,9 +172,23 @@ static int net_changesecretpw(struct net_context *c, int argc, const char **argv) { char *trust_pw; - enum netr_SchannelType sec_channel_type = SEC_CHAN_WKSTA; + int role = lp_server_role(); + + if (role != ROLE_DOMAIN_MEMBER) { + d_printf(_("Machine account password change only supported on a DOMAIN_MEMBER.\n" + "Do NOT use this function unless you know what it does!\n" + "This function will change the ADS Domain member " + "machine account password in the secrets.tdb file!\n")); + return 1; + } if(c->opt_force) { + struct secrets_domain_info1 *info = NULL; + struct secrets_domain_info1_change *prev = NULL; + NTSTATUS status; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); + if (c->opt_stdin) { set_line_buffering(stdin); set_line_buffering(stdout); @@ -188,14 +202,37 @@ static int net_changesecretpw(struct net_context *c, int argc, return 1; } - if (!secrets_store_machine_password(trust_pw, lp_workgroup(), sec_channel_type)) { - d_fprintf(stderr, - _("Unable to write the machine account password in the secrets database")); - return 1; + status = secrets_prepare_password_change(lp_workgroup(), + "localhost", + trust_pw, + talloc_tos(), + &info, &prev); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Unable to write the machine account password in the secrets database")); + return 1; } - else { - d_printf(_("Modified trust account password in secrets database\n")); + if (prev != NULL) { + d_fprintf(stderr, + _("Pending machine account password change found - aborting.")); + status = secrets_failed_password_change("localhost", + NT_STATUS_REQUEST_NOT_ACCEPTED, + NT_STATUS_NOT_COMMITTED, + info); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Failed to abort machine account password change")); + } + return 1; } + status = secrets_finish_password_change("localhost", now, info); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Unable to write the machine account password in the secrets database")); + return 1; + } + + d_printf(_("Modified trust account password in secrets database\n")); } else { d_printf(_("Machine account password change requires the -f flag.\n" -- 1.9.1 From 264e386ab45d77b68e21cb1a552113f809e955f4 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 23 May 2017 17:41:34 +0200 Subject: [PATCH 56/58] s3:libads: make use of secrets_*_password_change() in ads_change_trust_account_password() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit b874dc90c91dd41c35e99bf7c4fe04220465edca) --- source3/libads/util.c | 106 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 16 deletions(-) diff --git a/source3/libads/util.c b/source3/libads/util.c index b0754be..14dbf86 100644 --- a/source3/libads/util.c +++ b/source3/libads/util.c @@ -20,42 +20,116 @@ #include "includes.h" #include "ads.h" #include "secrets.h" +#include "librpc/gen_ndr/ndr_secrets.h" #ifdef HAVE_KRB5 - ADS_STATUS ads_change_trust_account_password(ADS_STRUCT *ads, char *host_principal) { - char *password; - char *new_password; + const char *password = NULL; + const char *new_password = NULL; ADS_STATUS ret; - enum netr_SchannelType sec_channel_type; - - if ((password = secrets_fetch_machine_password(lp_workgroup(), NULL, &sec_channel_type)) == NULL) { - DEBUG(1,("Failed to retrieve password for principal %s\n", host_principal)); - return ADS_ERROR_SYSTEM(ENOENT); + const char *domain = lp_workgroup(); + struct secrets_domain_info1 *info = NULL; + struct secrets_domain_info1_change *prev = NULL; + const DATA_BLOB *cleartext_blob = NULL; + DATA_BLOB pw_blob = data_blob_null; + DATA_BLOB new_pw_blob = data_blob_null; + NTSTATUS status; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); + int role = lp_server_role(); + bool ok; + + if (role != ROLE_DOMAIN_MEMBER) { + DBG_ERR("Machine account password change only supported on a DOMAIN_MEMBER.\n"); + return ADS_ERROR_NT(NT_STATUS_INVALID_SERVER_STATE); } new_password = trust_pw_new_value(talloc_tos(), SEC_CHAN_WKSTA, SEC_ADS); if (new_password == NULL) { ret = ADS_ERROR_SYSTEM(errno); DEBUG(1,("Failed to generate machine password\n")); - goto failed; + return ret; + } + + status = secrets_prepare_password_change(domain, + ads->auth.kdc_server, + new_password, + talloc_tos(), + &info, &prev); + if (!NT_STATUS_IS_OK(status)) { + return ADS_ERROR_NT(status); + } + if (prev != NULL) { + status = NT_STATUS_REQUEST_NOT_ACCEPTED; + secrets_failed_password_change("localhost", + status, + NT_STATUS_NOT_COMMITTED, + info); + return ADS_ERROR_NT(status); + } + + cleartext_blob = &info->password->cleartext_blob; + ok = convert_string_talloc(talloc_tos(), CH_UTF16MUNGED, CH_UNIX, + cleartext_blob->data, + cleartext_blob->length, + (void **)&pw_blob.data, + &pw_blob.length); + if (!ok) { + status = NT_STATUS_UNMAPPABLE_CHARACTER; + if (errno == ENOMEM) { + status = NT_STATUS_NO_MEMORY; + } + DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) " + "failed for password of %s - %s\n", + domain, nt_errstr(status)); + return ADS_ERROR_NT(status); + } + password = (const char *)pw_blob.data; + + cleartext_blob = &info->next_change->password->cleartext_blob; + ok = convert_string_talloc(talloc_tos(), CH_UTF16MUNGED, CH_UNIX, + cleartext_blob->data, + cleartext_blob->length, + (void **)&new_pw_blob.data, + &new_pw_blob.length); + if (!ok) { + status = NT_STATUS_UNMAPPABLE_CHARACTER; + if (errno == ENOMEM) { + status = NT_STATUS_NO_MEMORY; + } + DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) " + "failed for new_password of %s - %s\n", + domain, nt_errstr(status)); + secrets_failed_password_change("localhost", + status, + NT_STATUS_NOT_COMMITTED, + info); + return ADS_ERROR_NT(status); } + new_password = (const char *)new_pw_blob.data; ret = kerberos_set_password(ads->auth.kdc_server, host_principal, password, host_principal, new_password, ads->auth.time_offset); if (!ADS_ERR_OK(ret)) { - goto failed; + status = ads_ntstatus(ret); + DBG_ERR("kerberos_set_password(%s, %s) " + "failed for new_password of %s - %s\n", + ads->auth.kdc_server, host_principal, + domain, nt_errstr(status)); + secrets_failed_password_change(ads->auth.kdc_server, + NT_STATUS_NOT_COMMITTED, + status, + info); + return ret; } - if (!secrets_store_machine_password(new_password, lp_workgroup(), sec_channel_type)) { + status = secrets_finish_password_change(ads->auth.kdc_server, now, info); + if (!NT_STATUS_IS_OK(status)) { DEBUG(1,("Failed to save machine password\n")); - ret = ADS_ERROR_SYSTEM(EACCES); - goto failed; + return ADS_ERROR_NT(status); } -failed: - SAFE_FREE(password); - return ret; + return ADS_SUCCESS; } #endif -- 1.9.1 From 188d9ae106cc3fa24886460ada519bf419d00646 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 23 May 2017 17:42:09 +0200 Subject: [PATCH 57/58] s3:secrets: remove unused secrets_store_[prev_]machine_password() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit f513c20ee04fe896900c99ae804753d445414d7d) --- source3/include/secrets.h | 1 - source3/passdb/machine_account_secrets.c | 49 -------------------------------- 2 files changed, 50 deletions(-) diff --git a/source3/include/secrets.h b/source3/include/secrets.h index 0363b6b..24ae5bd 100644 --- a/source3/include/secrets.h +++ b/source3/include/secrets.h @@ -140,7 +140,6 @@ NTSTATUS secrets_finish_password_change(const char *change_server, const struct secrets_domain_info1 *info); bool secrets_delete_machine_password_ex(const char *domain, const char *realm); bool secrets_delete_domain_sid(const char *domain); -bool secrets_store_machine_password(const char *pass, const char *domain, enum netr_SchannelType sec_channel); char *secrets_fetch_prev_machine_password(const char *domain); time_t secrets_fetch_pass_last_set_time(const char *domain); char *secrets_fetch_machine_password(const char *domain, diff --git a/source3/passdb/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index b88fbe9..3d1cb5b 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -450,55 +450,6 @@ bool secrets_delete_domain_sid(const char *domain) } /************************************************************************ - Routine to store the previous machine password (by storing the current password - as the old) -************************************************************************/ - -static bool secrets_store_prev_machine_password(const char *domain) -{ - char *oldpass; - bool ret; - - oldpass = (char *)secrets_fetch(machine_password_keystr(domain), NULL); - if (oldpass == NULL) { - return true; - } - ret = secrets_store(machine_prev_password_keystr(domain), oldpass, strlen(oldpass)+1); - SAFE_FREE(oldpass); - return ret; -} - -/************************************************************************ - Routine to set the plaintext machine account password for a realm - the password is assumed to be a null terminated ascii string. - Before storing -************************************************************************/ - -bool secrets_store_machine_password(const char *pass, const char *domain, - enum netr_SchannelType sec_channel) -{ - bool ret; - uint32_t last_change_time; - uint32_t sec_channel_type; - - if (!secrets_store_prev_machine_password(domain)) { - return false; - } - - ret = secrets_store(machine_password_keystr(domain), pass, strlen(pass)+1); - if (!ret) - return ret; - - SIVAL(&last_change_time, 0, time(NULL)); - ret = secrets_store(machine_last_change_time_keystr(domain), &last_change_time, sizeof(last_change_time)); - - SIVAL(&sec_channel_type, 0, sec_channel); - ret = secrets_store(machine_sec_channel_type_keystr(domain), &sec_channel_type, sizeof(sec_channel_type)); - - return ret; -} - -/************************************************************************ Set the machine trust account password, the old pw and last change time, domain SID and salting principals based on values passed in (added to supprt the secrets_tdb_sync module on secrets.ldb) -- 1.9.1 From 16e80ac0165a9855f80a762453f7c9b5b3af5303 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 22 Jun 2017 15:30:56 +0200 Subject: [PATCH 58/58] selftest:Samba3: call "net primarytrust dumpinfo" setup_nt4_member() after the join Here we check that we get 'REDACTED SECRET VALUES' printed, in order to avoid regression on the non '-f' behavior. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 9530284383f252efd64bfdf138579964c6500eba) --- selftest/target/Samba3.pm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index 48a8973..d88f8cb 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -352,6 +352,16 @@ sub setup_nt4_member($$$) return undef; } + my $cmd = ""; + $cmd .= "SOCKET_WRAPPER_DEFAULT_IFACE=\"$ret->{SOCKET_WRAPPER_DEFAULT_IFACE}\" "; + $cmd .= "SELFTEST_WINBINDD_SOCKET_DIR=\"$ret->{SELFTEST_WINBINDD_SOCKET_DIR}\" "; + $cmd .= "$net $ret->{CONFIGURATION} primarytrust dumpinfo | grep -q 'REDACTED SECRET VALUES'"; + + if (system($cmd) != 0) { + warn("check failed\n$cmd"); + return undef; + } + if (not $self->check_or_start($ret, "yes", "yes", "yes")) { return undef; } -- 1.9.1