From 3bb31eeaaf2c23de887473a87b5d3c472ad6e4b9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 12 Jun 2017 18:58:49 +0200 Subject: [PATCH 01/56] 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 6aa850b05a7c6af20c0694aadff268cea9ecf15a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 12 Jun 2017 15:22:42 +0200 Subject: [PATCH 02/56] 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 04e12ede26abc6b9a738ddedc72878e11b06024f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 12 Jun 2017 17:58:20 +0200 Subject: [PATCH 03/56] 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 394b0e55f934fab673005c77a95266d8698d5f7f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 12 Jun 2017 17:58:46 +0200 Subject: [PATCH 04/56] 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 ecaa4c41acf7fba0436cf2dacee7672d1330ee6d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 15:05:51 +0200 Subject: [PATCH 05/56] 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 30f247badd7f6eaef0f3f092d58932a5561686f4 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 16:13:37 +0200 Subject: [PATCH 06/56] 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 13c48ca..b4bd768 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -434,78 +434,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 1d0a01070c0eb0570477b64c3a5f9b1343560c2b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 17:13:02 +0200 Subject: [PATCH 07/56] 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 4fbc2e0..248195e 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -422,6 +422,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) /** * @brief Get a list of encryption types allowed for session keys diff --git a/lib/krb5_wrap/krb5_samba.h b/lib/krb5_wrap/krb5_samba.h index c921538..cdd3dc4 100644 --- a/lib/krb5_wrap/krb5_samba.h +++ b/lib/krb5_wrap/krb5_samba.h @@ -352,6 +352,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 e3ad4badd54e1229c4bb7352d52ff0b5446b50e8 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 11:32:46 +0200 Subject: [PATCH 08/56] 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 248195e..6a863bd 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -435,7 +435,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. * @@ -452,6 +453,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, @@ -542,6 +545,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) /** * @brief Get a list of encryption types allowed for session keys diff --git a/lib/krb5_wrap/krb5_samba.h b/lib/krb5_wrap/krb5_samba.h index cdd3dc4..5834629 100644 --- a/lib/krb5_wrap/krb5_samba.h +++ b/lib/krb5_wrap/krb5_samba.h @@ -358,6 +358,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 3fc47c39b6bc511172590d88ab62b94a86d21315 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 12:42:04 +0200 Subject: [PATCH 09/56] 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 4d00ef6..e33c383 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 fbf17af91e78bf5948772d6060dc1ad11ced4f34 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 13:53:19 +0200 Subject: [PATCH 10/56] 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 e33c383..1e290d7 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, @@ -2111,6 +2103,14 @@ static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx, return WERR_INVALID_PARAMETER; } + 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 01a2e4f8bfb2a7fdc7be6937a204b3e9ef2994b6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 15:45:22 +0200 Subject: [PATCH 11/56] 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 aac1fee1a290a4cafde46d51be15c2e62ee6f4d2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 15:45:22 +0200 Subject: [PATCH 12/56] 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 1e290d7..37645a7 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 b38f07eafe19b3f6f24be921bb29eac9952918f9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:38:26 +0200 Subject: [PATCH 13/56] 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 18c678f58287f5d798cae47da090ca40efc34541 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:40:25 +0200 Subject: [PATCH 14/56] 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 37645a7..a5e863a 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 08a33963ae7f643b541f14ff8df08f91a343cc0e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:48:49 +0200 Subject: [PATCH 15/56] 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 a5e863a..780c26c 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 47aa094b64593043e1030ccc58a1028fdbb66f2d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:50:49 +0200 Subject: [PATCH 16/56] 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 780c26c..7493ac8 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; @@ -2214,6 +2220,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); @@ -2224,7 +2242,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_GEN_FAILURE; } -- 1.9.1 From a733fdfb58cd4837e61eb534b3c0500696441d6c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:52:59 +0200 Subject: [PATCH 17/56] 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 7493ac8..5e4a0d3 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -2211,12 +2211,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; } @@ -2237,6 +2237,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 37c13c960b2a0d1c15b28ecc7824dfe72670c11c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 15:59:00 +0200 Subject: [PATCH 18/56] 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 5e4a0d3..56bddf5 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -2237,6 +2237,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; @@ -2628,11 +2632,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 7c523616bf951fe4f6eb2050619825e0acd42bab Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 18 May 2017 16:02:44 +0200 Subject: [PATCH 19/56] 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 56bddf5..7669c2e 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 a9e2b143921844fb53636a896070857858688b47 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 16:01:55 +0200 Subject: [PATCH 20/56] 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 b4bd768..ba2311b 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -273,27 +273,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 ************************************************************************/ @@ -372,10 +351,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, @@ -387,14 +364,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 a33ca7f2433e8b9d1f8ccdf69e7b54f842c4b9ba Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 16:15:34 +0200 Subject: [PATCH 21/56] 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 ba2311b..a307286 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -355,9 +355,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 */ @@ -371,6 +369,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 32dfedd9713860801d94b75b9415b46f5fcc54df Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 16:28:42 +0200 Subject: [PATCH 22/56] 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 4dd39ea..2c9fc03 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 = smb_krb5_get_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 c8add71e81dd1ce06c3c9c88751ec6ad9b2bb896 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 17:04:36 +0200 Subject: [PATCH 23/56] 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 9aefac1..1b5ac67 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 e63338206189cea1930258eaa4db0c263fbad138 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 17:08:24 +0200 Subject: [PATCH 24/56] 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 96df10f..ff12ec0 100644 --- a/source3/libads/kerberos_keytab.c +++ b/source3/libads/kerberos_keytab.c @@ -237,10 +237,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, @@ -254,7 +258,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; } @@ -272,14 +275,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 e481c5897e8e14fe5ff4d8fa3dd4a139db41de8f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 17:09:20 +0200 Subject: [PATCH 25/56] 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 a307286..6cfbca6 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -369,13 +369,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 ab49425ace50dea520b3a66c7014378ce36bc7c9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 17:17:00 +0200 Subject: [PATCH 26/56] 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 6cfbca6..cfb09a7 100644 --- a/source3/libads/kerberos.c +++ b/source3/libads/kerberos.c @@ -272,103 +272,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 1b5ac67..c76e7b2 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 65922e4be76c79866e42dd474c0325cdcd7a96e6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 11:38:12 +0200 Subject: [PATCH 27/56] 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 44b82a98a67141610c51cd07da693be57dc27f36 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:10:45 +0200 Subject: [PATCH 28/56] 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 cb1bf4eeb59d2ceb04e31542b23e81e2c139cffa Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 21 Jun 2017 19:38:15 +0200 Subject: [PATCH 29/56] 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 e82bfd09b229199f1e1f5b22160a660e60ef5e24 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:18:33 +0200 Subject: [PATCH 30/56] 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 b1f40aa37b007f67cf024082217f855af644a783 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 20 Jun 2017 13:07:15 +0200 Subject: [PATCH 31/56] 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 beb8760..6f7a7a4 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 9fb825847dbd5726d884c13a33b0e61ff1c186ea Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:21:37 +0200 Subject: [PATCH 32/56] 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 6878c63107514a195be92311568708413a99045e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:21:37 +0200 Subject: [PATCH 33/56] 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 ee9309a529e3344426ee34d20c1c70eb2520fa24 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:27:45 +0200 Subject: [PATCH 34/56] 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 d128724e95d4eb56c84656f8b73e13cfcff70f8c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 24 May 2017 06:44:32 +0200 Subject: [PATCH 35/56] 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 2cb97de6fb989d1c68f224c41ad469f9889b0c45 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:31:01 +0200 Subject: [PATCH 36/56] 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 2fa642cad54dbea8b7c62327b47d48110785a544 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:40:05 +0200 Subject: [PATCH 37/56] 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 7669c2e..9a2f227 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -1705,10 +1705,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 7ba39037e81d64498450b4a6f4dbb6d668ff2f27 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 12:44:31 +0200 Subject: [PATCH 38/56] 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 9a2f227..ecae4c5 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -1701,11 +1701,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 92c7374a72d80c1f413baf9223c1d67fc797f453 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 24 May 2017 05:56:32 +0200 Subject: [PATCH 39/56] 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 fd4011fb0198f25159b7bb193af41c207a83a7e4 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 15:36:29 +0200 Subject: [PATCH 40/56] 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 c18e88f..baa5799 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -869,6 +869,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 f657172..7d12f17 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 17b3e4d..c179cc2 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 c389e00..8636ccd 100644 --- a/source3/winbindd/winbindd_dual.c +++ b/source3/winbindd/winbindd_dual.c @@ -1095,6 +1095,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 52e8629..0a2a5e2 100644 --- a/source3/winbindd/winbindd_dual_srv.c +++ b/source3/winbindd/winbindd_dual_srv.c @@ -730,6 +730,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 @@ -1406,6 +1407,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 697b967427991d4d31f6562dbc7d286cf5ac0eb3 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 20:44:40 +0200 Subject: [PATCH 41/56] 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 9539a78e33a19c950f244ca210da823c697f5d2e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Jun 2017 11:17:03 +0200 Subject: [PATCH 42/56] 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 e8a233ba3479bad43dce82629c0dca7868d5a591 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Jun 2017 11:18:37 +0200 Subject: [PATCH 43/56] 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 ecae4c5..99bba2a 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 ee0b7f15d085ab086443479019005bf55d07a982 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 21 Jun 2017 21:30:39 +0200 Subject: [PATCH 44/56] 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 988cd50434b2ca7b65752424441b066fdb590e68 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 11:35:20 +0200 Subject: [PATCH 45/56] 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 66a07e5..6dda7dc 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 9d80eaf76e4003bb957c8859727a106f74ade983 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 11:35:37 +0200 Subject: [PATCH 46/56] 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 9ee7f623aac1e2970b872f98e07a28e494a63f76 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 10:09:01 +0200 Subject: [PATCH 47/56] 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 4791ee44f5d5f7149da5f340cf5f33e442ee359b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 10:11:18 +0200 Subject: [PATCH 48/56] 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 fba8f124c95f800c207f944f421f4484fb76f1d5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 19 May 2017 16:28:17 +0200 Subject: [PATCH 49/56] 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 aa7d492b93004c89d752ab3ffd39038279eca0b0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 24 May 2017 18:05:40 +0200 Subject: [PATCH 50/56] 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 6f7a7a4..f7f0bf6 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 1a3babe13e4f4e1890765942429c52709cae92c4 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 17 May 2017 10:29:59 +0200 Subject: [PATCH 51/56] 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 99bba2a..591c177 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 11e2cc93abca0f6dc14069c46ee96b6c787f2a64 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 20:47:17 +0200 Subject: [PATCH 52/56] 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 35882f139a7c36c55a60785241fe189d3c97350e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 23 May 2017 17:29:31 +0200 Subject: [PATCH 53/56] 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 f7f0bf6..7aa7405 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 75c61e5e46cc7465ad01def1b0a03fc27fb4552a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 23 May 2017 17:41:34 +0200 Subject: [PATCH 54/56] 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 88b7c3fbe86db3b3a93cf141c92e3860e0e37479 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 23 May 2017 17:42:09 +0200 Subject: [PATCH 55/56] 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 6bca3be71ac04dba2de06e0b14c92cb55cb8ebae Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 22 Jun 2017 15:30:56 +0200 Subject: [PATCH 56/56] 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 a7699f8..f4871aa 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