s3:gse: Move setup of service_principal to update function
authorAndreas Schneider <asn@samba.org>
Thu, 9 Mar 2017 07:05:26 +0000 (08:05 +0100)
committerAndreas Schneider <asn@cryptomilk.org>
Fri, 10 Mar 2017 10:37:22 +0000 (11:37 +0100)
BUG: https://bugzilla.samba.org/show_bug.cgi?id=12554

Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>

Signed-off-by: Andreas Schneider <asn@samba.org>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
source3/librpc/crypto/gse.c

index 8fee44424c7bf36afa15fa36eeb1e04bd5bd3539..9d4334393951b393c1aacaee8cc861665318e546 100644 (file)
@@ -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 */
 
@@ -343,11 +319,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;
 }
@@ -367,10 +341,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,