From 690b5dc6512815ee72c616d2d54091fb59516d42 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 5 Jan 2017 09:34:36 +0100 Subject: [PATCH 01/21] replace: Include sysmacros.h In the GNU C Library, "makedev" is defined by . For historical compatibility, it is currently defined by as well, but it is planned to remove this soon. Signed-off-by: Andreas Schneider Reviewed-by: Volker Lendecke Autobuild-User(master): Volker Lendecke Autobuild-Date(master): Sun Jan 8 22:30:03 CET 2017 on sn-devel-144 (cherry picked from commit 0127bdd33b251a52c6ffc44b6cb3b82b16a80741) --- lib/replace/replace.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/replace/replace.h b/lib/replace/replace.h index c69a069e4b3..1dbeacfff66 100644 --- a/lib/replace/replace.h +++ b/lib/replace/replace.h @@ -171,6 +171,10 @@ #include #endif +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + #ifdef HAVE_SETPROCTITLE_H #include #endif -- 2.12.0 From c2edb05c463ce35c19fac9af4744844cf09ed6b6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 29 Dec 2016 14:00:36 +0100 Subject: [PATCH 02/21] s4:gensec_gssapi: the value gensec_get_target_principal() should overwrite gensec_get_target_hostname() If gensec_get_target_principal() has a value, we no longer have to verify the gensec_get_target_hostname() value, it can be just an ipadress. Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 48bcca566ebb3a5385b15b0525d7fbdd06361e04) --- source4/auth/gensec/gensec_gssapi.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index a6c4019aa6f..3974c3d42a0 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -307,7 +307,15 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi gss_buffer_desc name_token; gss_OID name_type; OM_uint32 maj_stat, min_stat; + const char *target_principal = NULL; const char *hostname = gensec_get_target_hostname(gensec_security); + const char *service = gensec_get_target_service(gensec_security); + const char *realm = cli_credentials_get_realm(creds); + + target_principal = gensec_get_target_principal(gensec_security); + if (target_principal != NULL) { + goto do_start; + } if (!hostname) { DEBUG(3, ("No hostname for target computer passed in, cannot use kerberos for this connection\n")); @@ -322,6 +330,8 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi return NT_STATUS_INVALID_PARAMETER; } +do_start: + nt_status = gensec_gssapi_start(gensec_security); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; @@ -333,16 +343,18 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi gensec_gssapi_state->gss_want_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG); } - gensec_gssapi_state->target_principal = gensec_get_target_principal(gensec_security); - if (gensec_gssapi_state->target_principal) { + if (target_principal != NULL) { name_type = GSS_C_NULL_OID; } else { - gensec_gssapi_state->target_principal = talloc_asprintf(gensec_gssapi_state, "%s/%s@%s", - gensec_get_target_service(gensec_security), - hostname, cli_credentials_get_realm(creds)); - + target_principal = talloc_asprintf(gensec_gssapi_state, + "%s/%s@%s", service, hostname, realm); + if (target_principal == NULL) { + return NT_STATUS_NO_MEMORY; + } name_type = GSS_C_NT_USER_NAME; } + gensec_gssapi_state->target_principal = target_principal; + name_token.value = discard_const_p(uint8_t, gensec_gssapi_state->target_principal); name_token.length = strlen(gensec_gssapi_state->target_principal); -- 2.12.0 From 7aad3342693a3357774237bbdcd717b9ec52c5a6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 29 Dec 2016 15:20:00 +0100 Subject: [PATCH 03/21] s4:gensec_gssapi: require a realm in gensec_gssapi_client_start() Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 3a870baee8d9dbe5359f04a108814afc27e57d46) --- source4/auth/gensec/gensec_gssapi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 3974c3d42a0..957cfa4229d 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -330,6 +330,16 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi return NT_STATUS_INVALID_PARAMETER; } + if (realm == NULL) { + const char *cred_name = cli_credentials_get_unparsed_name(creds, + gensec_security); + DEBUG(3, ("cli_credentials(%s) without realm, " + "cannot use kerberos for this connection %s/%s\n", + cred_name, service, hostname)); + talloc_free(discard_const_p(char, cred_name)); + return NT_STATUS_INVALID_PARAMETER; + } + do_start: nt_status = gensec_gssapi_start(gensec_security); -- 2.12.0 From 87c18806a0288d8ebfa36fedb0df1e0b847e23c1 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 6 Mar 2017 09:13:09 +0100 Subject: [PATCH 04/21] testprogs: Use smbclient by default in test_kinit_trusts This is the tool we use by default and we should test with it. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Signed-off-by: Andreas Schneider Reviewed-by: Stefan Metzmacher (cherry picked from commit 9b3ff90dbc5cc1017dfc89831a1081272e6c2356) --- testprogs/blackbox/test_kinit_trusts_heimdal.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testprogs/blackbox/test_kinit_trusts_heimdal.sh b/testprogs/blackbox/test_kinit_trusts_heimdal.sh index 073e0e7517e..040bf919203 100755 --- a/testprogs/blackbox/test_kinit_trusts_heimdal.sh +++ b/testprogs/blackbox/test_kinit_trusts_heimdal.sh @@ -32,7 +32,7 @@ if test -x $samba4bindir/samba4kinit; then samba4kinit=$samba4bindir/samba4kinit fi -smbclient="$samba4bindir/smbclient4" +smbclient="$samba4bindir/smbclient" wbinfo="$samba4bindir/wbinfo" rpcclient="$samba4bindir/rpcclient" samba_tool="$samba4bindir/samba-tool" -- 2.12.0 From 06df00fba72bc0599e830732dd3185f2212c59eb Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 6 Mar 2017 09:15:45 +0100 Subject: [PATCH 05/21] testprogs: Add kinit_trusts tests with smbclient4 BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Signed-off-by: Andreas Schneider Reviewed-by: Stefan Metzmacher (cherry picked from commit 42bd003f468ab95b6ac97c774e2cd217d06c05ed) --- testprogs/blackbox/test_kinit_trusts_heimdal.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/testprogs/blackbox/test_kinit_trusts_heimdal.sh b/testprogs/blackbox/test_kinit_trusts_heimdal.sh index 040bf919203..e67f77361a4 100755 --- a/testprogs/blackbox/test_kinit_trusts_heimdal.sh +++ b/testprogs/blackbox/test_kinit_trusts_heimdal.sh @@ -52,8 +52,16 @@ rm -rf $KRB5CCNAME_PATH echo $TRUST_PASSWORD > $PREFIX/tmppassfile testit "kinit with password" $samba4kinit $enctype --password-file=$PREFIX/tmppassfile --request-pac $TRUST_USERNAME@$TRUST_REALM || failed=`expr $failed + 1` test_smbclient "Test login with user kerberos ccache" 'ls' "$unc" -k yes || failed=`expr $failed + 1` +rm -rf $KRB5CCNAME_PATH + +# Test with smbclient4 +smbclient="$samba4bindir/smbclient4" +testit "kinit with password" $samba4kinit $enctype --password-file=$PREFIX/tmppassfile --request-pac $TRUST_USERNAME@$TRUST_REALM || failed=`expr $failed + 1` +test_smbclient "Test login with user kerberos ccache (smbclient4)" 'ls' "$unc" -k yes || failed=`expr $failed + 1` +rm -rf $KRB5CCNAME_PATH testit "kinit with password (enterprise style)" $samba4kinit $enctype --enterprise --password-file=$PREFIX/tmppassfile --request-pac $TRUST_USERNAME@$TRUST_REALM || failed=`expr $failed + 1` +smbclient="$samba4bindir/smbclient" test_smbclient "Test login with user kerberos ccache" 'ls' "$unc" -k yes || failed=`expr $failed + 1` if test x"${TYPE}" = x"forest" ;then -- 2.12.0 From b1ae6618ccf6c5be8efbeee15d5bc81e37da2031 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 8 Mar 2017 10:40:08 +0100 Subject: [PATCH 06/21] krb5_wrap: Do not return an empty realm from smb_krb5_get_realm_from_hostname() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit 946f9dd1170be63b91e31ce825ea123f3c07329b) --- lib/krb5_wrap/krb5_samba.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c index 10b42dec53f..9dc7304d566 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -2691,7 +2691,9 @@ static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, goto out; } - if (realm_list && realm_list[0]) { + if (realm_list != NULL && + realm_list[0] != NULL && + realm_list[0][0] != '\0') { realm = talloc_strdup(mem_ctx, realm_list[0]); } -- 2.12.0 From ee7c761a0b31dfe72ddf3a34f74180287cd7539c Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 8 Mar 2017 10:48:52 +0100 Subject: [PATCH 07/21] krb5_wrap: Try to guess the correct realm from the service hostname If we do not get a realm mapping from the krb5.conf or from the Kerberos library try to guess it from the service hostname. The guessing of the realm from the service hostname is already implemented in Heimdal. This makes the behavior of smb_krb5_get_realm_from_hostname() consistent with both MIT and Heimdal. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit 65228925ab3c4da4ae299f77cae219fc7d37cc68) --- lib/krb5_wrap/krb5_samba.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c index 9dc7304d566..f8ef9f1df0f 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -2695,6 +2695,19 @@ static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, realm_list[0] != NULL && realm_list[0][0] != '\0') { realm = talloc_strdup(mem_ctx, realm_list[0]); + } else { + const char *p = NULL; + + /* + * "dc6.samba2003.example.com" + * returns a realm of "SAMBA2003.EXAMPLE.COM" + * + * "dc6." returns realm as NULL + */ + p = strchr_m(hostname, '.'); + if (p != NULL && p[1] != '\0') { + realm = talloc_strdup_upper(mem_ctx, p + 1); + } } out: -- 2.12.0 From 1ca2210cbf30884d3059732cc2bc7369a12e8344 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 8 Mar 2017 11:56:30 +0100 Subject: [PATCH 08/21] krb5_wrap: pass client_realm to smb_krb5_get_realm_from_hostname() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit f0c4fcace586197d5c170f6a9dcc175df23e3802) --- lib/krb5_wrap/krb5_samba.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c index f8ef9f1df0f..36bcc65e22a 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -2664,7 +2664,8 @@ static char *smb_krb5_get_default_realm_from_ccache(TALLOC_CTX *mem_ctx) ************************************************************************/ static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, - const char *hostname) + const char *hostname, + const char *client_realm) { #if defined(HAVE_KRB5_REALM_TYPE) /* Heimdal. */ @@ -2695,6 +2696,9 @@ static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, realm_list[0] != NULL && realm_list[0][0] != '\0') { realm = talloc_strdup(mem_ctx, realm_list[0]); + if (realm == NULL) { + goto out; + } } else { const char *p = NULL; @@ -2707,9 +2711,16 @@ static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, p = strchr_m(hostname, '.'); if (p != NULL && p[1] != '\0') { realm = talloc_strdup_upper(mem_ctx, p + 1); + if (realm == NULL) { + goto out; + } } } + if (realm == NULL) { + realm = talloc_strdup(mem_ctx, client_realm); + } + out: if (ctx) { @@ -2752,7 +2763,8 @@ char *smb_krb5_get_principal_from_service_hostname(TALLOC_CTX *mem_ctx, if (host) { /* DNS name. */ realm = smb_krb5_get_realm_from_hostname(talloc_tos(), - remote_name); + remote_name, + default_realm); } else { /* NetBIOS name - use our realm. */ realm = smb_krb5_get_default_realm_from_ccache(talloc_tos()); -- 2.12.0 From 6c0d1d39ea53d1d54f14d0e19011429c163be692 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 8 Mar 2017 11:56:30 +0100 Subject: [PATCH 09/21] krb5_wrap: Make smb_krb5_get_realm_from_hostname() public BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit 339a2ecb3f05d0c9e860a5dd59b8bdbc51d4ffa7) --- lib/krb5_wrap/krb5_samba.c | 28 +++++++++++++++++++++------- lib/krb5_wrap/krb5_samba.h | 4 ++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c index 36bcc65e22a..2b0ec6bfa0e 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -2659,13 +2659,27 @@ static char *smb_krb5_get_default_realm_from_ccache(TALLOC_CTX *mem_ctx) return realm; } -/************************************************************************ - Routine to get the realm from a given DNS name. -************************************************************************/ - -static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, - const char *hostname, - const char *client_realm) +/** + * @brief Get the realm from the service hostname. + * + * This function will look for a domain realm mapping in the [domain_realm] + * section of the krb5.conf first and fallback to extract the realm from + * the provided service hostname. As a last resort it will return the + * provided client_realm. + * + * @param[in] mem_ctx The talloc context + * + * @param[in] hostname The service hostname + * + * @param[in] client_realm If we can not find a mapping, fall back to + * this realm. + * + * @return The realm to use for the service hostname, NULL if a fatal error + * occured. + */ +char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, + const char *hostname, + const char *client_realm) { #if defined(HAVE_KRB5_REALM_TYPE) /* Heimdal. */ diff --git a/lib/krb5_wrap/krb5_samba.h b/lib/krb5_wrap/krb5_samba.h index 71e81ea26e1..accae449a0e 100644 --- a/lib/krb5_wrap/krb5_samba.h +++ b/lib/krb5_wrap/krb5_samba.h @@ -314,6 +314,10 @@ krb5_error_code smb_krb5_principal_set_realm(krb5_context context, krb5_principal principal, const char *realm); +char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, + const char *hostname, + const char *client_realm); + char *smb_krb5_get_principal_from_service_hostname(TALLOC_CTX *mem_ctx, const char *service, const char *remote_name, -- 2.12.0 From cf46e2b6f68874a53bb58aa0f37341180062d23d Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 6 Mar 2017 09:19:13 +0100 Subject: [PATCH 10/21] s4:gensec-gssapi: Create a helper function to setup server_principal BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit 8f7c4529420316b553c80cd3d19b6996525b029a) --- source4/auth/gensec/gensec_gssapi.c | 88 +++++++++++++++++++++++++------------ source4/auth/gensec/gensec_gssapi.h | 2 +- 2 files changed, 61 insertions(+), 29 deletions(-) diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 957cfa4229d..ec57d193714 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -83,6 +83,56 @@ static int gensec_gssapi_destructor(struct gensec_gssapi_state *gensec_gssapi_st return 0; } +static NTSTATUS gensec_gssapi_setup_server_principal(TALLOC_CTX *mem_ctx, + const char *target_principal, + const char *service, + const char *hostname, + const char *realm, + const gss_OID mech, + char **pserver_principal, + gss_name_t *pserver_name) +{ + char *server_principal = NULL; + gss_buffer_desc name_token; + gss_OID name_type; + OM_uint32 maj_stat, min_stat = 0; + + if (target_principal != NULL) { + server_principal = talloc_strdup(mem_ctx, target_principal); + name_type = GSS_C_NULL_OID; + } else { + server_principal = talloc_asprintf(mem_ctx, + "%s/%s@%s", + service, hostname, realm); + name_type = GSS_C_NT_USER_NAME; + } + if (server_principal == NULL) { + return NT_STATUS_NO_MEMORY; + } + + name_token.value = (uint8_t *)server_principal; + name_token.length = strlen(server_principal); + + maj_stat = gss_import_name(&min_stat, + &name_token, + name_type, + pserver_name); + if (maj_stat) { + DBG_WARNING("GSS Import name of %s failed: %s\n", + server_principal, + gssapi_error_string(mem_ctx, + maj_stat, + min_stat, + mech)); + TALLOC_FREE(server_principal); + return NT_STATUS_INVALID_PARAMETER; + } + + *pserver_principal = server_principal; + + return NT_STATUS_OK; +} + static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security) { struct gensec_gssapi_state *gensec_gssapi_state; @@ -304,9 +354,6 @@ static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_securi struct gensec_gssapi_state *gensec_gssapi_state; struct cli_credentials *creds = gensec_get_credentials(gensec_security); NTSTATUS nt_status; - gss_buffer_desc name_token; - gss_OID name_type; - OM_uint32 maj_stat, min_stat; const char *target_principal = NULL; const char *hostname = gensec_get_target_hostname(gensec_security); const char *service = gensec_get_target_service(gensec_security); @@ -353,31 +400,16 @@ do_start: gensec_gssapi_state->gss_want_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG); } - if (target_principal != NULL) { - name_type = GSS_C_NULL_OID; - } else { - target_principal = talloc_asprintf(gensec_gssapi_state, - "%s/%s@%s", service, hostname, realm); - if (target_principal == NULL) { - return NT_STATUS_NO_MEMORY; - } - name_type = GSS_C_NT_USER_NAME; - } - gensec_gssapi_state->target_principal = target_principal; - - name_token.value = discard_const_p(uint8_t, gensec_gssapi_state->target_principal); - name_token.length = strlen(gensec_gssapi_state->target_principal); - - - maj_stat = gss_import_name (&min_stat, - &name_token, - name_type, - &gensec_gssapi_state->server_name); - if (maj_stat) { - DEBUG(2, ("GSS Import name of %s failed: %s\n", - (char *)name_token.value, - gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); - return NT_STATUS_INVALID_PARAMETER; + nt_status = gensec_gssapi_setup_server_principal(gensec_gssapi_state, + target_principal, + service, + hostname, + realm, + gensec_gssapi_state->gss_oid, + &gensec_gssapi_state->target_principal, + &gensec_gssapi_state->server_name); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; } return NT_STATUS_OK; diff --git a/source4/auth/gensec/gensec_gssapi.h b/source4/auth/gensec/gensec_gssapi.h index cf0e3a8d914..d788b5ebc38 100644 --- a/source4/auth/gensec/gensec_gssapi.h +++ b/source4/auth/gensec/gensec_gssapi.h @@ -65,5 +65,5 @@ struct gensec_gssapi_state { int gss_exchange_count; size_t sig_size; - const char *target_principal; + char *target_principal; }; -- 2.12.0 From ca72deb34bf9cb0f7550f87236bdbd9b26790160 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 8 Mar 2017 12:34:59 +0100 Subject: [PATCH 11/21] s4:gensec_gssapi: Move setup of service_principal to update function BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit bf6358bf035e7ad48bd15cc2164afab2a19e7ad6) --- source4/auth/gensec/gensec_gssapi.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index ec57d193714..6cb4431e0d9 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -400,18 +400,6 @@ do_start: gensec_gssapi_state->gss_want_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG); } - nt_status = gensec_gssapi_setup_server_principal(gensec_gssapi_state, - target_principal, - service, - hostname, - realm, - gensec_gssapi_state->gss_oid, - &gensec_gssapi_state->target_principal, - &gensec_gssapi_state->server_name); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - return NT_STATUS_OK; } @@ -452,7 +440,11 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, OM_uint32 min_stat2; gss_buffer_desc input_token = { 0, NULL }; gss_buffer_desc output_token = { 0, NULL }; - + struct cli_credentials *cli_creds = gensec_get_credentials(gensec_security); + const char *target_principal = gensec_get_target_principal(gensec_security); + const char *hostname = gensec_get_target_hostname(gensec_security); + const char *service = gensec_get_target_service(gensec_security); + const char *client_realm = cli_credentials_get_realm(cli_creds); gss_OID gss_oid_p = NULL; OM_uint32 time_req = 0; OM_uint32 time_rec = 0; @@ -491,6 +483,21 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, return NT_STATUS_INTERNAL_ERROR; } #endif + + if (gensec_gssapi_state->server_name == NULL) { + nt_status = gensec_gssapi_setup_server_principal(gensec_gssapi_state, + target_principal, + service, + hostname, + client_realm, + gensec_gssapi_state->gss_oid, + &gensec_gssapi_state->target_principal, + &gensec_gssapi_state->server_name); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + } + maj_stat = gss_init_sec_context(&min_stat, gensec_gssapi_state->client_cred->creds, &gensec_gssapi_state->gssapi_context, -- 2.12.0 From 96b5b8b215f9af8548fa165ea0fda072b3f3d8a8 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 8 Mar 2017 11:03:17 +0100 Subject: [PATCH 12/21] s4:gensec_gssapi: Use smb_krb5_get_realm_from_hostname() With credentials for administrator@FOREST1.EXAMPLE.COM this patch changes the target_principal for the ldap service of host dc2.forest2.example.com from ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM to ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM Typically ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM should be used in order to allow the KDC of FOREST1.EXAMPLE.COM to generate a referral ticket for krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM. The problem is that KDCs only return such referral tickets if there's a forest trust between FOREST1.EXAMPLE.COM and FOREST2.EXAMPLE.COM. If there's only an external domain trust between FOREST1.EXAMPLE.COM and FOREST2.EXAMPLE.COM the KDC of FOREST1.EXAMPLE.COM will respond with S_PRINCIPAL_UNKNOWN when being asked for ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM. In the case of an external trust the client can still ask explicitly for krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM and the KDC of FOREST1.EXAMPLE.COM will generate it. From there the client can use the krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM ticket and ask a KDC of FOREST2.EXAMPLE.COM for a service ticket for ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM. With Heimdal we'll get the fallback on S_PRINCIPAL_UNKNOWN behavior when we pass ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM as target principal. As _krb5_get_cred_kdc_any() first calls get_cred_kdc_referral() (which always starts with the client realm) and falls back to get_cred_kdc_capath() (which starts with the given realm). MIT krb5 only tries the given realm of the target principal, if we want to autodetect support for transitive forest trusts, we'll have to do the fallback ourself. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit 3781eb250173981a8890b82d1ff9358f144034cd) --- source4/auth/gensec/gensec_gssapi.c | 62 ++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 6cb4431e0d9..57392a04e60 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -445,6 +445,7 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, const char *hostname = gensec_get_target_hostname(gensec_security); const char *service = gensec_get_target_service(gensec_security); const char *client_realm = cli_credentials_get_realm(cli_creds); + const char *server_realm = NULL; gss_OID gss_oid_p = NULL; OM_uint32 time_req = 0; OM_uint32 time_rec = 0; @@ -484,12 +485,71 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, } #endif + /* + * With credentials for + * administrator@FOREST1.EXAMPLE.COM this patch changes + * the target_principal for the ldap service of host + * dc2.forest2.example.com from + * + * ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM + * + * to + * + * ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM + * + * Typically + * ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM + * should be used in order to allow the KDC of + * FOREST1.EXAMPLE.COM to generate a referral ticket + * for krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM. + * + * The problem is that KDCs only return such referral + * tickets if there's a forest trust between + * FOREST1.EXAMPLE.COM and FOREST2.EXAMPLE.COM. If + * there's only an external domain trust between + * FOREST1.EXAMPLE.COM and FOREST2.EXAMPLE.COM the KDC + * of FOREST1.EXAMPLE.COM will respond with + * S_PRINCIPAL_UNKNOWN when being asked for + * ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM. + * + * In the case of an external trust the client can + * still ask explicitly for + * krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM and + * the KDC of FOREST1.EXAMPLE.COM will generate it. + * + * From there the client can use the + * krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM + * ticket and ask a KDC of FOREST2.EXAMPLE.COM for a + * service ticket for + * ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM. + * + * With Heimdal we'll get the fallback on + * S_PRINCIPAL_UNKNOWN behavior when we pass + * ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM as + * target principal. As _krb5_get_cred_kdc_any() first + * calls get_cred_kdc_referral() (which always starts + * with the client realm) and falls back to + * get_cred_kdc_capath() (which starts with the given + * realm). + * + * MIT krb5 only tries the given realm of the target + * principal, if we want to autodetect support for + * transitive forest trusts, would have to do the + * fallback ourself. + */ if (gensec_gssapi_state->server_name == NULL) { + server_realm = smb_krb5_get_realm_from_hostname(gensec_gssapi_state, + hostname, + client_realm); + if (server_realm == NULL) { + return NT_STATUS_NO_MEMORY; + } + nt_status = gensec_gssapi_setup_server_principal(gensec_gssapi_state, target_principal, service, hostname, - client_realm, + server_realm, gensec_gssapi_state->gss_oid, &gensec_gssapi_state->target_principal, &gensec_gssapi_state->server_name); -- 2.12.0 From 6cb66e6b79236e0f6e24596d4b74eff70bde11c7 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 8 Mar 2017 13:10:05 +0100 Subject: [PATCH 13/21] s4:gensec_gssapi: Correctly handle external trusts with MIT BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit 2dd4887648bf006a577e03fc027e881738ca04ab) --- source4/auth/gensec/gensec_gssapi.c | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c index 57392a04e60..61911aae9d9 100644 --- a/source4/auth/gensec/gensec_gssapi.c +++ b/source4/auth/gensec/gensec_gssapi.c @@ -464,6 +464,7 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, switch (gensec_security->gensec_role) { case GENSEC_CLIENT: { + bool fallback = false; #ifdef SAMBA4_USES_HEIMDAL struct gsskrb5_send_to_kdc send_to_kdc; krb5_error_code ret; @@ -537,6 +538,48 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, * transitive forest trusts, would have to do the * fallback ourself. */ +#ifndef SAMBA4_USES_HEIMDAL + if (gensec_gssapi_state->server_name == NULL) { + nt_status = gensec_gssapi_setup_server_principal(gensec_gssapi_state, + target_principal, + service, + hostname, + client_realm, + gensec_gssapi_state->gss_oid, + &gensec_gssapi_state->target_principal, + &gensec_gssapi_state->server_name); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + maj_stat = gss_init_sec_context(&min_stat, + gensec_gssapi_state->client_cred->creds, + &gensec_gssapi_state->gssapi_context, + gensec_gssapi_state->server_name, + gensec_gssapi_state->gss_oid, + gensec_gssapi_state->gss_want_flags, + time_req, + gensec_gssapi_state->input_chan_bindings, + &input_token, + &gss_oid_p, + &output_token, + &gensec_gssapi_state->gss_got_flags, /* ret flags */ + &time_rec); + if (maj_stat != GSS_S_FAILURE) { + goto init_sec_context_done; + } + if (min_stat != (OM_uint32)KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) { + goto init_sec_context_done; + } + if (target_principal != NULL) { + goto init_sec_context_done; + } + + fallback = true; + TALLOC_FREE(gensec_gssapi_state->target_principal); + gss_release_name(&min_stat2, &gensec_gssapi_state->server_name); + } +#endif /* !SAMBA4_USES_HEIMDAL */ if (gensec_gssapi_state->server_name == NULL) { server_realm = smb_krb5_get_realm_from_hostname(gensec_gssapi_state, hostname, @@ -545,6 +588,11 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, return NT_STATUS_NO_MEMORY; } + if (fallback && + strequal(client_realm, server_realm)) { + goto init_sec_context_done; + } + nt_status = gensec_gssapi_setup_server_principal(gensec_gssapi_state, target_principal, service, @@ -571,6 +619,9 @@ static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, &output_token, &gensec_gssapi_state->gss_got_flags, /* ret flags */ &time_rec); + goto init_sec_context_done; + /* JUMP! */ +init_sec_context_done: if (gss_oid_p) { gensec_gssapi_state->gss_oid = gss_oid_p; } -- 2.12.0 From 95b985a7c2be54b3e94d46cc9a47a41f0a5b84f6 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 9 Mar 2017 07:54:29 +0100 Subject: [PATCH 14/21] s3:gse: Use smb_krb5_get_realm_from_hostname() With credentials for administrator@FOREST1.EXAMPLE.COM this patch changes the target_principal for the ldap service of host dc2.forest2.example.com from ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM to ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM Typically ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM should be used in order to allow the KDC of FOREST1.EXAMPLE.COM to generate a referral ticket for krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM. The problem is that KDCs only return such referral tickets if there's a forest trust between FOREST1.EXAMPLE.COM and FOREST2.EXAMPLE.COM. If there's only an external domain trust between FOREST1.EXAMPLE.COM and FOREST2.EXAMPLE.COM the KDC of FOREST1.EXAMPLE.COM will respond with S_PRINCIPAL_UNKNOWN when being asked for ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM. In the case of an external trust the client can still ask explicitly for krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM and the KDC of FOREST1.EXAMPLE.COM will generate it. From there the client can use the krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM ticket and ask a KDC of FOREST2.EXAMPLE.COM for a service ticket for ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM. With Heimdal we'll get the fallback on S_PRINCIPAL_UNKNOWN behavior when we pass ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM as target principal. As _krb5_get_cred_kdc_any() first calls get_cred_kdc_referral() (which always starts with the client realm) and falls back to get_cred_kdc_capath() (which starts with the given realm). MIT krb5 only tries the given realm of the target principal, if we want to autodetect support for transitive forest trusts, we'll have to do the fallback ourself. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit a3d95ed9037fb8b14a451da02dcadf011485ae34) --- source3/librpc/crypto/gse.c | 93 +++++++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 25 deletions(-) diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c index abf20bc7dfd..57632f6cc8f 100644 --- a/source3/librpc/crypto/gse.c +++ b/source3/librpc/crypto/gse.c @@ -120,6 +120,54 @@ static int gse_context_destructor(void *ptr) return 0; } +static NTSTATUS gse_setup_server_principal(TALLOC_CTX *mem_ctx, + const char *target_principal, + const char *service, + const char *hostname, + const char *realm, + char **pserver_principal, + gss_name_t *pserver_name) +{ + char *server_principal = NULL; + gss_buffer_desc name_token; + gss_OID name_type; + OM_uint32 maj_stat, min_stat = 0; + + if (target_principal != NULL) { + server_principal = talloc_strdup(mem_ctx, target_principal); + name_type = GSS_C_NULL_OID; + } else { + server_principal = talloc_asprintf(mem_ctx, + "%s/%s@%s", + service, + hostname, + realm); + name_type = GSS_C_NT_USER_NAME; + } + if (server_principal == NULL) { + return NT_STATUS_NO_MEMORY; + } + + name_token.value = (uint8_t *)server_principal; + name_token.length = strlen(server_principal); + + maj_stat = gss_import_name(&min_stat, + &name_token, + name_type, + pserver_name); + if (maj_stat) { + DBG_WARNING("GSS Import name of %s failed: %s\n", + server_principal, + gse_errstr(mem_ctx, maj_stat, min_stat)); + TALLOC_FREE(server_principal); + return NT_STATUS_INVALID_PARAMETER; + } + + *pserver_principal = server_principal; + + return NT_STATUS_OK; +} + static NTSTATUS gse_context_init(TALLOC_CTX *mem_ctx, bool do_sign, bool do_seal, const char *ccache_name, @@ -203,11 +251,12 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx, { struct gse_context *gse_ctx; OM_uint32 gss_maj, gss_min; - gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER; #ifdef HAVE_GSS_KRB5_CRED_NO_CI_FLAGS_X gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER; gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X); #endif + char *server_principal = NULL; + char *server_realm = NULL; NTSTATUS status; if (!server || !service) { @@ -223,30 +272,24 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx, /* Guess the realm based on the supplied service, and avoid the GSS libs doing DNS lookups which may fail. - - TODO: Loop with the KDC on some more combinations (local - realm in particular), possibly falling back to - GSS_C_NT_HOSTBASED_SERVICE */ - name_buffer.value = - smb_krb5_get_principal_from_service_hostname(gse_ctx, - service, - server, - realm); - if (!name_buffer.value) { - status = NT_STATUS_NO_MEMORY; - goto err_out; + server_realm = smb_krb5_get_realm_from_hostname(mem_ctx, + server, + realm); + if (server_realm == NULL) { + return NT_STATUS_NO_MEMORY; } - name_buffer.length = strlen((char *)name_buffer.value); - gss_maj = gss_import_name(&gss_min, &name_buffer, - GSS_C_NT_USER_NAME, - &gse_ctx->server_name); - if (gss_maj) { - DEBUG(5, ("gss_import_name failed for %s, with [%s]\n", - (char *)name_buffer.value, - gse_errstr(gse_ctx, gss_maj, gss_min))); - status = NT_STATUS_INTERNAL_ERROR; - goto err_out; + + status = gse_setup_server_principal(mem_ctx, + NULL, + service, + server, + server_realm, + &server_principal, + &gse_ctx->server_name); + TALLOC_FREE(server_realm); + if (!NT_STATUS_IS_OK(status)) { + return status; } /* TODO: get krb5 ticket using username/password, if no valid @@ -299,11 +342,11 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx, #endif *_gse_ctx = gse_ctx; - TALLOC_FREE(name_buffer.value); + TALLOC_FREE(server_principal); return NT_STATUS_OK; err_out: - TALLOC_FREE(name_buffer.value); + TALLOC_FREE(server_principal); TALLOC_FREE(gse_ctx); return status; } -- 2.12.0 From 81be2b738ee1f31540cb5bc712a4887a95ab67f5 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 9 Mar 2017 09:10:12 +0100 Subject: [PATCH 15/21] krb5_wrap: Remove obsolete smb_krb5_get_principal_from_service_hostname() BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Signed-off-by: Andreas Schneider Reviewed-by: Stefan Metzmacher (cherry picked from commit 804e828d52ec922f3970e847652ab1ee5538b9b0) --- lib/krb5_wrap/krb5_samba.c | 111 --------------------------------------------- lib/krb5_wrap/krb5_samba.h | 5 -- 2 files changed, 116 deletions(-) diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c index 2b0ec6bfa0e..0b67ea52a19 100644 --- a/lib/krb5_wrap/krb5_samba.c +++ b/lib/krb5_wrap/krb5_samba.c @@ -2604,61 +2604,6 @@ krb5_error_code smb_krb5_principal_set_realm(krb5_context context, } -/************************************************************************ - Routine to get the default realm from the kerberos credentials cache. - Caller must free if the return value is not NULL. -************************************************************************/ - -static char *smb_krb5_get_default_realm_from_ccache(TALLOC_CTX *mem_ctx) -{ - char *realm = NULL; - krb5_context ctx = NULL; - krb5_ccache cc = NULL; - krb5_principal princ = NULL; - - initialize_krb5_error_table(); - if (krb5_init_context(&ctx)) { - return NULL; - } - - DEBUG(5,("kerberos_get_default_realm_from_ccache: " - "Trying to read krb5 cache: %s\n", - krb5_cc_default_name(ctx))); - if (krb5_cc_default(ctx, &cc)) { - DEBUG(5,("kerberos_get_default_realm_from_ccache: " - "failed to read default cache\n")); - goto out; - } - if (krb5_cc_get_principal(ctx, cc, &princ)) { - DEBUG(5,("kerberos_get_default_realm_from_ccache: " - "failed to get default principal\n")); - goto out; - } - -#if defined(HAVE_KRB5_PRINCIPAL_GET_REALM) - realm = talloc_strdup(mem_ctx, krb5_principal_get_realm(ctx, princ)); -#elif defined(HAVE_KRB5_PRINC_REALM) - { - krb5_data *realm_data = krb5_princ_realm(ctx, princ); - realm = talloc_strndup(mem_ctx, realm_data->data, realm_data->length); - } -#endif - - out: - - if (ctx) { - if (princ) { - krb5_free_principal(ctx, princ); - } - if (cc) { - krb5_cc_close(ctx, cc); - } - krb5_free_context(ctx); - } - - return realm; -} - /** * @brief Get the realm from the service hostname. * @@ -2749,62 +2694,6 @@ char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, } /** - * @brief Get the principal as a string from the service hostname. - * - * @param[in] mem_ctx The talloc context - * - * @param[in] service The service name - * - * @param[in] remote_name The remote name - * - * @param[in] default_realm The default_realm if we cannot get it from the - * hostname or netbios name. - * - * @return A talloc'ed principal string or NULL if an error occured. - * - * The caller needs to free the principal with talloc_free() if it isn't needed - * anymore. - */ -char *smb_krb5_get_principal_from_service_hostname(TALLOC_CTX *mem_ctx, - const char *service, - const char *remote_name, - const char *default_realm) -{ - char *realm = NULL; - char *host = NULL; - char *principal; - host = strchr_m(remote_name, '.'); - if (host) { - /* DNS name. */ - realm = smb_krb5_get_realm_from_hostname(talloc_tos(), - remote_name, - default_realm); - } else { - /* NetBIOS name - use our realm. */ - realm = smb_krb5_get_default_realm_from_ccache(talloc_tos()); - } - - if (realm == NULL || *realm == '\0') { - realm = talloc_strdup(talloc_tos(), default_realm); - if (!realm) { - return NULL; - } - DEBUG(3,("Cannot get realm from, " - "desthost %s or default ccache. Using default " - "smb.conf realm %s\n", - remote_name, - realm)); - } - - principal = talloc_asprintf(mem_ctx, - "%s/%s@%s", - service, remote_name, - realm); - TALLOC_FREE(realm); - return principal; -} - -/** * @brief Get an error string from a Kerberos error code. * * @param[in] context The library context. diff --git a/lib/krb5_wrap/krb5_samba.h b/lib/krb5_wrap/krb5_samba.h index accae449a0e..c921538efcb 100644 --- a/lib/krb5_wrap/krb5_samba.h +++ b/lib/krb5_wrap/krb5_samba.h @@ -318,11 +318,6 @@ char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx, const char *hostname, const char *client_realm); -char *smb_krb5_get_principal_from_service_hostname(TALLOC_CTX *mem_ctx, - const char *service, - const char *remote_name, - const char *default_realm); - char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx); -- 2.12.0 From 7ee72ef4dbbbf795e188dd604b21e60d2f7a6caa Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 6 Mar 2017 08:16:11 +0100 Subject: [PATCH 16/21] s3:gse: Pass down the gensec_security pointer BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit e6b1e58874de30d094f9bce474479cfddb39d3fc) --- source3/librpc/crypto/gse.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c index 57632f6cc8f..5a39522a828 100644 --- a/source3/librpc/crypto/gse.c +++ b/source3/librpc/crypto/gse.c @@ -352,10 +352,13 @@ err_out: } static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx, - struct gse_context *gse_ctx, + struct gensec_security *gensec_security, const DATA_BLOB *token_in, DATA_BLOB *token_out) { + struct gse_context *gse_ctx = + talloc_get_type_abort(gensec_security->private_data, + struct gse_context); OM_uint32 gss_maj, gss_min; gss_buffer_desc in_data; gss_buffer_desc out_data; @@ -542,10 +545,13 @@ done: } static NTSTATUS gse_get_server_auth_token(TALLOC_CTX *mem_ctx, - struct gse_context *gse_ctx, + struct gensec_security *gensec_security, const DATA_BLOB *token_in, DATA_BLOB *token_out) { + struct gse_context *gse_ctx = + talloc_get_type_abort(gensec_security->private_data, + struct gse_context); OM_uint32 gss_maj, gss_min; gss_buffer_desc in_data; gss_buffer_desc out_data; @@ -762,17 +768,16 @@ static NTSTATUS gensec_gse_update(struct gensec_security *gensec_security, const DATA_BLOB in, DATA_BLOB *out) { NTSTATUS status; - struct gse_context *gse_ctx = - talloc_get_type_abort(gensec_security->private_data, - struct gse_context); switch (gensec_security->gensec_role) { case GENSEC_CLIENT: - status = gse_get_client_auth_token(mem_ctx, gse_ctx, + status = gse_get_client_auth_token(mem_ctx, + gensec_security, &in, out); break; case GENSEC_SERVER: - status = gse_get_server_auth_token(mem_ctx, gse_ctx, + status = gse_get_server_auth_token(mem_ctx, + gensec_security, &in, out); break; } -- 2.12.0 From f00569f2db94de7e2ab8a51b06723731cb7d1207 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 9 Mar 2017 08:05:26 +0100 Subject: [PATCH 17/21] s3:gse: Move setup of service_principal to update function BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit 3ba1ad1f8c7871070d0ecbe5d49c5c44afe98bbf) --- source3/librpc/crypto/gse.c | 97 +++++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 26 deletions(-) diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c index 5a39522a828..3580181061e 100644 --- a/source3/librpc/crypto/gse.c +++ b/source3/librpc/crypto/gse.c @@ -255,8 +255,6 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx, gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER; gss_OID oid = discard_const(GSS_KRB5_CRED_NO_CI_FLAGS_X); #endif - char *server_principal = NULL; - char *server_realm = NULL; NTSTATUS status; if (!server || !service) { @@ -270,28 +268,6 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx, return NT_STATUS_NO_MEMORY; } - /* Guess the realm based on the supplied service, and avoid the GSS libs - doing DNS lookups which may fail. - */ - server_realm = smb_krb5_get_realm_from_hostname(mem_ctx, - server, - realm); - if (server_realm == NULL) { - return NT_STATUS_NO_MEMORY; - } - - status = gse_setup_server_principal(mem_ctx, - NULL, - service, - server, - server_realm, - &server_principal, - &gse_ctx->server_name); - TALLOC_FREE(server_realm); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - /* TODO: get krb5 ticket using username/password, if no valid * one already available in ccache */ @@ -342,11 +318,9 @@ static NTSTATUS gse_init_client(TALLOC_CTX *mem_ctx, #endif *_gse_ctx = gse_ctx; - TALLOC_FREE(server_principal); return NT_STATUS_OK; err_out: - TALLOC_FREE(server_principal); TALLOC_FREE(gse_ctx); return status; } @@ -366,10 +340,81 @@ static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx, NTSTATUS status; OM_uint32 time_rec = 0; struct timeval tv; + struct cli_credentials *cli_creds = gensec_get_credentials(gensec_security); + const char *hostname = gensec_get_target_hostname(gensec_security); + const char *service = gensec_get_target_service(gensec_security); + const char *client_realm = cli_credentials_get_realm(cli_creds); + char *server_principal = NULL; + char *server_realm = NULL; in_data.value = token_in->data; in_data.length = token_in->length; + /* + * With credentials for administrator@FOREST1.EXAMPLE.COM this patch + * changes the target_principal for the ldap service of host + * dc2.forest2.example.com from + * + * ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM + * + * to + * + * ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM + * + * Typically ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM should be + * used in order to allow the KDC of FOREST1.EXAMPLE.COM to generate a + * referral ticket for krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM. + * + * The problem is that KDCs only return such referral tickets if + * there's a forest trust between FOREST1.EXAMPLE.COM and + * FOREST2.EXAMPLE.COM. If there's only an external domain trust + * between FOREST1.EXAMPLE.COM and FOREST2.EXAMPLE.COM the KDC of + * FOREST1.EXAMPLE.COM will respond with S_PRINCIPAL_UNKNOWN when being + * asked for ldap/dc2.forest2.example.com@FOREST1.EXAMPLE.COM. + * + * In the case of an external trust the client can still ask explicitly + * for krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM and the KDC of + * FOREST1.EXAMPLE.COM will generate it. + * + * From there the client can use the + * krbtgt/FOREST2.EXAMPLE.COM@FOREST1.EXAMPLE.COM ticket and ask a KDC + * of FOREST2.EXAMPLE.COM for a service ticket for + * ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM. + * + * With Heimdal we'll get the fallback on S_PRINCIPAL_UNKNOWN behavior + * when we pass ldap/dc2.forest2.example.com@FOREST2.EXAMPLE.COM as + * target principal. As _krb5_get_cred_kdc_any() first calls + * get_cred_kdc_referral() (which always starts with the client realm) + * and falls back to get_cred_kdc_capath() (which starts with the given + * realm). + * + * MIT krb5 only tries the given realm of the target principal, if we + * want to autodetect support for transitive forest trusts, would have + * to do the fallback ourself. + */ + if (gse_ctx->server_name == NULL) { + server_realm = smb_krb5_get_realm_from_hostname(mem_ctx, + hostname, + client_realm); + if (server_realm == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = gse_setup_server_principal(mem_ctx, + NULL, + service, + hostname, + server_realm, + &server_principal, + &gse_ctx->server_name); + TALLOC_FREE(server_realm); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + TALLOC_FREE(server_principal); + } + gss_maj = gss_init_sec_context(&gss_min, gse_ctx->creds, &gse_ctx->gssapi_context, -- 2.12.0 From 0d4be702c09bc9d2594e2007117d3809539c47d4 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 9 Mar 2017 08:11:07 +0100 Subject: [PATCH 18/21] s3:gse: Check if we have a target_princpal set we should use BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit ada31d65d6c5929d2fbddfea5611a5f5fe5a0d74) --- source3/librpc/crypto/gse.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c index 3580181061e..721fd8c1625 100644 --- a/source3/librpc/crypto/gse.c +++ b/source3/librpc/crypto/gse.c @@ -341,6 +341,7 @@ static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx, OM_uint32 time_rec = 0; struct timeval tv; struct cli_credentials *cli_creds = gensec_get_credentials(gensec_security); + const char *target_principal = gensec_get_target_principal(gensec_security); const char *hostname = gensec_get_target_hostname(gensec_security); const char *service = gensec_get_target_service(gensec_security); const char *client_realm = cli_credentials_get_realm(cli_creds); @@ -401,7 +402,7 @@ static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx, } status = gse_setup_server_principal(mem_ctx, - NULL, + target_principal, service, hostname, server_realm, -- 2.12.0 From 3d4b0509d43cf535b5ba465b6669cbff8bb0278f Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 9 Mar 2017 08:18:27 +0100 Subject: [PATCH 19/21] s3:gse: Correctly handle external trusts with MIT BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Andreas Schneider Signed-off-by: Stefan Metzmacher (cherry picked from commit b8bca7d08fe05758e536767b1146cdcdd8b9fee3) --- source3/librpc/crypto/gse.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c index 721fd8c1625..3abf774633b 100644 --- a/source3/librpc/crypto/gse.c +++ b/source3/librpc/crypto/gse.c @@ -347,6 +347,7 @@ static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx, const char *client_realm = cli_credentials_get_realm(cli_creds); char *server_principal = NULL; char *server_realm = NULL; + bool fallback = false; in_data.value = token_in->data; in_data.length = token_in->length; @@ -393,6 +394,50 @@ static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx, * want to autodetect support for transitive forest trusts, would have * to do the fallback ourself. */ +#ifndef SAMBA4_USES_HEIMDAL + if (gse_ctx->server_name == NULL) { + OM_uint32 gss_min2 = 0; + + status = gse_setup_server_principal(mem_ctx, + target_principal, + service, + hostname, + client_realm, + &server_principal, + &gse_ctx->server_name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + gss_maj = gss_init_sec_context(&gss_min, + gse_ctx->creds, + &gse_ctx->gssapi_context, + gse_ctx->server_name, + &gse_ctx->gss_mech, + gse_ctx->gss_want_flags, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + &in_data, + NULL, + &out_data, + &gse_ctx->gss_got_flags, + &time_rec); + if (gss_maj != GSS_S_FAILURE) { + goto init_sec_context_done; + } + if (gss_min != (OM_uint32)KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) { + goto init_sec_context_done; + } + if (target_principal != NULL) { + goto init_sec_context_done; + } + + fallback = true; + TALLOC_FREE(server_principal); + gss_release_name(&gss_min2, &gse_ctx->server_name); + } +#endif /* !SAMBA4_USES_HEIMDAL */ + if (gse_ctx->server_name == NULL) { server_realm = smb_krb5_get_realm_from_hostname(mem_ctx, hostname, @@ -401,6 +446,11 @@ static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx, return NT_STATUS_NO_MEMORY; } + if (fallback && + strequal(client_realm, server_realm)) { + goto init_sec_context_done; + } + status = gse_setup_server_principal(mem_ctx, target_principal, service, @@ -425,6 +475,10 @@ static NTSTATUS gse_get_client_auth_token(TALLOC_CTX *mem_ctx, 0, GSS_C_NO_CHANNEL_BINDINGS, &in_data, NULL, &out_data, &gse_ctx->gss_got_flags, &time_rec); + goto init_sec_context_done; + /* JUMP! */ +init_sec_context_done: + switch (gss_maj) { case GSS_S_COMPLETE: /* we are done with it */ -- 2.12.0 From 58802adb808df88123528898252025b94035f73f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sun, 29 Jan 2017 17:19:14 +0100 Subject: [PATCH 20/21] HEIMDAL:kdc: make it possible to disable the principal based referral detection BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 209886e95c3afe1e4e50bacc30b40a543856a7a0) --- source4/heimdal/kdc/default_config.c | 1 + source4/heimdal/kdc/kdc.h | 2 ++ source4/heimdal/kdc/krb5tgs.c | 4 +++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/source4/heimdal/kdc/default_config.c b/source4/heimdal/kdc/default_config.c index 6fbf5fdae15..0129c5d3c54 100644 --- a/source4/heimdal/kdc/default_config.c +++ b/source4/heimdal/kdc/default_config.c @@ -55,6 +55,7 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config) c->preauth_use_strongest_session_key = FALSE; c->tgs_use_strongest_session_key = FALSE; c->use_strongest_server_key = TRUE; + c->autodetect_referrals = TRUE; c->check_ticket_addresses = TRUE; c->allow_null_ticket_addresses = TRUE; c->allow_anonymous = FALSE; diff --git a/source4/heimdal/kdc/kdc.h b/source4/heimdal/kdc/kdc.h index 9d52fd4c2ec..16263d6919b 100644 --- a/source4/heimdal/kdc/kdc.h +++ b/source4/heimdal/kdc/kdc.h @@ -69,6 +69,8 @@ typedef struct krb5_kdc_configuration { krb5_boolean allow_anonymous; enum krb5_kdc_trpolicy trpolicy; + krb5_boolean autodetect_referrals; + krb5_boolean enable_pkinit; krb5_boolean pkinit_princ_in_cert; const char *pkinit_kdc_identity; diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c index 334a6eb1dc8..a888788bb6f 100644 --- a/source4/heimdal/kdc/krb5tgs.c +++ b/source4/heimdal/kdc/krb5tgs.c @@ -1660,7 +1660,9 @@ server_lookup: Realm req_rlm; krb5_realm *realms; - if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) { + if (!config->autodetect_referrals) { + /* noop */ + } else if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) { if(nloop++ < 2) { new_rlm = find_rpath(context, tgt->crealm, req_rlm); if(new_rlm) { -- 2.12.0 From f6ef1ba0d7aba52e2a8c0667a41605da18b2995a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sun, 29 Jan 2017 17:20:09 +0100 Subject: [PATCH 21/21] s4:kdc: disable principal based autodetected referral detection BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554 Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider (cherry picked from commit 3314bf52aaef60ef5cc1110587b53064df7c475d) --- source4/kdc/kdc-heimdal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source4/kdc/kdc-heimdal.c b/source4/kdc/kdc-heimdal.c index f2927e5cb9f..061296a4f40 100644 --- a/source4/kdc/kdc-heimdal.c +++ b/source4/kdc/kdc-heimdal.c @@ -379,6 +379,8 @@ static void kdc_task_init(struct task_server *task) kdc_config->tgs_use_strongest_session_key = false; kdc_config->use_strongest_server_key = true; + kdc_config->autodetect_referrals = false; + /* Register hdb-samba4 hooks for use as a keytab */ kdc->base_ctx = talloc_zero(kdc, struct samba_kdc_base_context); -- 2.12.0