From 83b04c60fac76ccd2d5aecb14f8896a07d488b1f Mon Sep 17 00:00:00 2001 From: =?utf8?q?G=C3=BCnther=20Deschner?= Date: Thu, 31 Jan 2008 13:05:36 +0100 Subject: [PATCH] Enable v3-0-test to successfully join a windows 2008 domain controller. This is hand-merged from a couple of commits from 3-2-test, cherry-picking was hardly possible without importing all the ldap sign/seal work from metze. Guenther --- source/include/ads.h | 3 + source/libads/sasl.c | 140 +++++++++++++++++++++++++++---------- source/libads/util.c | 57 +++++++++++++++ source/libsmb/cliconnect.c | 3 +- 4 files changed, 164 insertions(+), 39 deletions(-) diff --git a/source/include/ads.h b/source/include/ads.h index fcaeb2069d2..24884f5e185 100644 --- a/source/include/ads.h +++ b/source/include/ads.h @@ -321,4 +321,7 @@ typedef struct { int val; int critical; } ads_control; + +#define ADS_IGNORE_PRINCIPAL "not_defined_in_RFC4178@please_ignore" + #endif /* _INCLUDE_ADS_H_ */ diff --git a/source/libads/sasl.c b/source/libads/sasl.c index 0067a19d3b6..40749be745c 100644 --- a/source/libads/sasl.c +++ b/source/libads/sasl.c @@ -137,10 +137,81 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads) } #ifdef HAVE_KRB5 +struct ads_service_principal { + char *string; +#ifdef HAVE_GSSAPI + gss_name_t name; +#endif +}; + +static void ads_free_service_principal(struct ads_service_principal *p) +{ + SAFE_FREE(p->string); + +#ifdef HAVE_GSSAPI + if (p->name) { + uint32 minor_status; + gss_release_name(&minor_status, &p->name); + } +#endif + ZERO_STRUCTP(p); +} + +static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads, + const char *given_principal, + struct ads_service_principal *p) +{ + ADS_STATUS status; +#ifdef HAVE_GSSAPI + gss_buffer_desc input_name; + /* GSS_KRB5_NT_PRINCIPAL_NAME */ + gss_OID_desc nt_principal = + {10, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")}; + uint32 minor_status; + int gss_rc; +#endif + + ZERO_STRUCTP(p); + + /* I've seen a child Windows 2000 domain not send + the principal name back in the first round of + the SASL bind reply. So we guess based on server + name and realm. --jerry */ + /* Also try best guess when we get the w2k8 ignore + principal back - gd */ + + if (!given_principal || + strequal(given_principal, ADS_IGNORE_PRINCIPAL)) { + + status = ads_guess_service_principal(ads, &p->string); + if (!ADS_ERR_OK(status)) { + return status; + } + } else { + p->string = SMB_STRDUP(given_principal); + if (!p->string) { + return ADS_ERROR(LDAP_NO_MEMORY); + } + } + +#ifdef HAVE_GSSAPI + input_name.value = p->string; + input_name.length = strlen(p->string); + + gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name); + if (gss_rc) { + ads_free_service_principal(p); + return ADS_ERROR_GSS(gss_rc, minor_status); + } +#endif + + return ADS_SUCCESS; +} + /* perform a LDAP/SASL/SPNEGO/KRB5 bind */ -static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal) +static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal) { DATA_BLOB blob = data_blob(NULL, 0); struct berval cred, *scred = NULL; @@ -167,6 +238,13 @@ static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *princip return ADS_ERROR(rc); } + +static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, + struct ads_service_principal *p) +{ + return ads_sasl_spnego_rawkrb5_bind(ads, p->string); +} + #endif /* @@ -178,7 +256,7 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads) int rc, i; ADS_STATUS status; DATA_BLOB blob; - char *principal = NULL; + char *given_principal = NULL; char *OIDs[ASN1_MAX_OIDS]; #ifdef HAVE_KRB5 BOOL got_kerberos_mechanism = False; @@ -201,7 +279,7 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads) /* the server sent us the first part of the SPNEGO exchange in the negprot reply */ - if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) { + if (!spnego_parse_negTokenInit(blob, OIDs, &given_principal)) { data_blob_free(&blob); status = ADS_ERROR(LDAP_OPERATIONS_ERROR); goto failed; @@ -219,42 +297,23 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads) #endif free(OIDs[i]); } - DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", principal)); + DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal)); #ifdef HAVE_KRB5 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) && got_kerberos_mechanism) { - /* I've seen a child Windows 2000 domain not send - the principal name back in the first round of - the SASL bind reply. So we guess based on server - name and realm. --jerry */ - if ( !principal ) { - if ( ads->server.realm && ads->server.ldap_server ) { - char *server, *server_realm; - - server = SMB_STRDUP( ads->server.ldap_server ); - server_realm = SMB_STRDUP( ads->server.realm ); - - if ( !server || !server_realm ) - return ADS_ERROR(LDAP_NO_MEMORY); - - strlower_m( server ); - strupper_m( server_realm ); - asprintf( &principal, "ldap/%s@%s", server, server_realm ); - - SAFE_FREE( server ); - SAFE_FREE( server_realm ); - - if ( !principal ) - return ADS_ERROR(LDAP_NO_MEMORY); - } - + struct ads_service_principal p; + + status = ads_generate_service_principal(ads, given_principal, &p); + SAFE_FREE(given_principal); + if (!ADS_ERR_OK(status)) { + return status; } - - status = ads_sasl_spnego_krb5_bind(ads, principal); + + status = ads_sasl_spnego_krb5_bind(ads, &p); if (ADS_ERR_OK(status)) { - SAFE_FREE(principal); + ads_free_service_principal(&p); return status; } @@ -264,19 +323,26 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads) status = ADS_ERROR_KRB5(ads_kinit_password(ads)); if (ADS_ERR_OK(status)) { - status = ads_sasl_spnego_krb5_bind(ads, principal); + status = ads_sasl_spnego_krb5_bind(ads, &p); + if (!ADS_ERR_OK(status)) { + DEBUG(0,("kinit succeeded but " + "ads_sasl_spnego_krb5_bind failed: %s\n", + ads_errstr(status))); + } } + ads_free_service_principal(&p); + /* only fallback to NTLMSSP if allowed */ if (ADS_ERR_OK(status) || !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) { - SAFE_FREE(principal); return status; } - } + } else #endif - - SAFE_FREE(principal); + { + SAFE_FREE(given_principal); + } /* lets do NTLMSSP ... this has the big advantage that we don't need to sync clocks, and we don't rely on special versions of the krb5 diff --git a/source/libads/util.c b/source/libads/util.c index eb6dccb3afd..365f72e7da7 100644 --- a/source/libads/util.c +++ b/source/libads/util.c @@ -52,4 +52,61 @@ failed: SAFE_FREE(password); return ret; } + +ADS_STATUS ads_guess_service_principal(ADS_STRUCT *ads, + char **returned_principal) +{ + char *princ = NULL; + + if (ads->server.realm && ads->server.ldap_server) { + char *server, *server_realm; + + server = SMB_STRDUP(ads->server.ldap_server); + server_realm = SMB_STRDUP(ads->server.realm); + + if (!server || !server_realm) { + return ADS_ERROR(LDAP_NO_MEMORY); + } + + strlower_m(server); + strupper_m(server_realm); + asprintf(&princ, "ldap/%s@%s", server, server_realm); + + SAFE_FREE(server); + SAFE_FREE(server_realm); + + if (!princ) { + return ADS_ERROR(LDAP_NO_MEMORY); + } + } else if (ads->config.realm && ads->config.ldap_server_name) { + char *server, *server_realm; + + server = SMB_STRDUP(ads->config.ldap_server_name); + server_realm = SMB_STRDUP(ads->config.realm); + + if (!server || !server_realm) { + return ADS_ERROR(LDAP_NO_MEMORY); + } + + strlower_m(server); + strupper_m(server_realm); + asprintf(&princ, "ldap/%s@%s", server, server_realm); + + SAFE_FREE(server); + SAFE_FREE(server_realm); + + if (!princ) { + return ADS_ERROR(LDAP_NO_MEMORY); + } + } + + if (!princ) { + return ADS_ERROR(LDAP_PARAM_ERROR); + } + + *returned_principal = princ; + + return ADS_SUCCESS; +} + #endif diff --git a/source/libsmb/cliconnect.c b/source/libsmb/cliconnect.c index 3168dd19e24..2f6606fafa5 100644 --- a/source/libsmb/cliconnect.c +++ b/source/libsmb/cliconnect.c @@ -866,8 +866,7 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user, /* If we get a bad principal, try to guess it if we have a valid host NetBIOS name. */ - if (strequal(principal, - "not_defined_in_RFC4178@please_ignore")) { + if (strequal(principal, ADS_IGNORE_PRINCIPAL)) { SAFE_FREE(principal); } if (principal == NULL && -- 2.34.1