diff options
-rw-r--r-- | source3/libads/sasl.c | 234 |
1 files changed, 146 insertions, 88 deletions
diff --git a/source3/libads/sasl.c b/source3/libads/sasl.c index f5e540d304..732691942f 100644 --- a/source3/libads/sasl.c +++ b/source3/libads/sasl.c @@ -360,7 +360,7 @@ static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = { /* perform a LDAP/SASL/SPNEGO/GSSKRB5 bind */ -static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const char *sname) +static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t serv_name) { ADS_STATUS status; BOOL ok; @@ -371,7 +371,6 @@ static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const char *snam gss_OID mech_type = &krb5_mech_type; gss_OID actual_mech_type = GSS_C_NULL_OID; const char *spnego_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL}; - gss_name_t serv_name; gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT; gss_buffer_desc input_token, output_token; uint32 req_flags, ret_flags; @@ -379,50 +378,6 @@ static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const char *snam DATA_BLOB unwrapped; DATA_BLOB wrapped; struct berval cred, *scred = NULL; - krb5_principal principal = NULL; - gss_buffer_desc input_name; - krb5_context ctx = NULL; - krb5_enctype enc_types[] = { -#ifdef ENCTYPE_ARCFOUR_HMAC - ENCTYPE_ARCFOUR_HMAC, -#endif - ENCTYPE_DES_CBC_MD5, - ENCTYPE_NULL}; - gss_OID_desc nt_principal = - {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")}; - - initialize_krb5_error_table(); - status = ADS_ERROR_KRB5(krb5_init_context(&ctx)); - if (!ADS_ERR_OK(status)) { - return status; - } - status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types)); - if (!ADS_ERR_OK(status)) { - krb5_free_context(ctx); - return status; - } - status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal)); - if (!ADS_ERR_OK(status)) { - krb5_free_context(ctx); - return status; - } - - /* - * The MIT libraries have a *HORRIBLE* bug - input_value.value needs - * to point to the *address* of the krb5_principal, and the gss libraries - * to a shallow copy of the krb5_principal pointer - so we need to keep - * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* ! - * Just one more way in which MIT engineers screwed me over.... JRA. - */ - input_name.value = &principal; - input_name.length = sizeof(principal); - - gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name); - if (gss_rc) { - krb5_free_principal(ctx, principal); - krb5_free_context(ctx); - return ADS_ERROR_GSS(gss_rc, minor_status); - } input_token.value = NULL; input_token.length = 0; @@ -633,17 +588,136 @@ static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const char *snam } failed: - gss_release_name(&minor_status, &serv_name); if (context_handle != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER); - krb5_free_principal(ctx, principal); - krb5_free_context(ctx); return status; } #endif #ifdef HAVE_KRB5 +struct ads_service_principal { + krb5_context ctx; + char *string; + krb5_principal principal; +#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 + if (p->principal) { + krb5_free_principal(p->ctx, p->principal); + } + + if (p->ctx) { + krb5_free_context(p->ctx); + } + + 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; + krb5_enctype enc_types[] = { +#ifdef ENCTYPE_ARCFOUR_HMAC + ENCTYPE_ARCFOUR_HMAC, +#endif + ENCTYPE_DES_CBC_MD5, + ENCTYPE_NULL}; +#ifdef HAVE_GSSAPI + gss_buffer_desc input_name; + gss_OID_desc nt_principal = + {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")}; + 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 */ + if (given_principal) { + p->string = SMB_STRDUP(given_principal); + if (!p->string) { + return ADS_ERROR(LDAP_NO_MEMORY); + } + } else 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(&p->string, "ldap/%s@%s", server, server_realm); + + SAFE_FREE(server); + SAFE_FREE(server_realm); + + if (!p->string) { + return ADS_ERROR(LDAP_NO_MEMORY); + } + } + + initialize_krb5_error_table(); + status = ADS_ERROR_KRB5(krb5_init_context(&p->ctx)); + if (!ADS_ERR_OK(status)) { + ads_free_service_principal(p); + return status; + } + status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(p->ctx, enc_types)); + if (!ADS_ERR_OK(status)) { + ads_free_service_principal(p); + return status; + } + status = ADS_ERROR_KRB5(smb_krb5_parse_name(p->ctx, p->string, &p->principal)); + if (!ADS_ERR_OK(status)) { + ads_free_service_principal(p); + return status; + } + +#ifdef HAVE_GSSAPI + /* + * The MIT libraries have a *HORRIBLE* bug - input_value.value needs + * to point to the *address* of the krb5_principal, and the gss libraries + * to a shallow copy of the krb5_principal pointer - so we need to keep + * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* ! + * Just one more way in which MIT engineers screwed me over.... JRA. + * + * That's the reason for principal not beeing a local var in this function + */ + input_name.value = &p->principal; + input_name.length = sizeof(p->principal); + + 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 status; +} + /* perform a LDAP/SASL/SPNEGO/KRB5 bind */ @@ -679,7 +753,8 @@ static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *prin return ADS_ERROR(rc); } -static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal) +static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, + struct ads_service_principal *p) { #ifdef HAVE_GSSAPI /* @@ -693,10 +768,10 @@ static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *princip * against clock skew errors */ if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { - return ads_sasl_spnego_gsskrb5_bind(ads, principal); + return ads_sasl_spnego_gsskrb5_bind(ads, p->name); } #endif - return ads_sasl_spnego_rawkrb5_bind(ads, principal); + return ads_sasl_spnego_rawkrb5_bind(ads, p->string); } #endif @@ -709,7 +784,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; @@ -732,7 +807,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; @@ -750,42 +825,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; } @@ -795,19 +851,21 @@ 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); } + 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 |