r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[abartlet/samba.git/.git] / source / libads / sasl.c
index fe31ef94bb6e9bd0aebdd5d9776674c2b77ff192..9536ba31beef981112a58ede51164382753e3035 100644 (file)
@@ -5,7 +5,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 */
 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
 {
-       DATA_BLOB msg1 = data_blob(NULL, 0);
-       DATA_BLOB blob = data_blob(NULL, 0);
-       DATA_BLOB blob_in = data_blob(NULL, 0);
-       DATA_BLOB blob_out = data_blob(NULL, 0);
+       DATA_BLOB msg1 = data_blob_null;
+       DATA_BLOB blob = data_blob_null;
+       DATA_BLOB blob_in = data_blob_null;
+       DATA_BLOB blob_out = data_blob_null;
        struct berval cred, *scred = NULL;
        int rc;
        NTSTATUS nt_status;
@@ -54,7 +53,7 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
                return ADS_ERROR_NT(nt_status);
        }
 
-       blob_in = data_blob(NULL, 0);
+       blob_in = data_blob_null;
 
        do {
                nt_status = ntlmssp_update(ntlmssp_state, 
@@ -90,7 +89,7 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
                                blob = data_blob(scred->bv_val, scred->bv_len);
                                ber_bvfree(scred);
                        } else {
-                               blob = data_blob(NULL, 0);
+                               blob = data_blob_null;
                        }
 
                } else {
@@ -102,7 +101,7 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
                
                if ((turn == 1) && 
                    (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
-                       DATA_BLOB tmp_blob = data_blob(NULL, 0);
+                       DATA_BLOB tmp_blob = data_blob_null;
                        /* the server might give us back two challenges */
                        if (!spnego_parse_challenge(blob, &blob_in, 
                                                    &tmp_blob)) {
@@ -114,7 +113,7 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
                        }
                        data_blob_free(&tmp_blob);
                } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
-                       if (!spnego_parse_auth_response(blob, nt_status, 
+                       if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP, 
                                                        &blob_in)) {
 
                                ntlmssp_end(&ntlmssp_state);
@@ -136,17 +135,19 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
        return ADS_ERROR(rc);
 }
 
+#ifdef HAVE_KRB5
 /* 
    perform a LDAP/SASL/SPNEGO/KRB5 bind
 */
 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
 {
-       DATA_BLOB blob = data_blob(NULL, 0);
+       DATA_BLOB blob = data_blob_null;
        struct berval cred, *scred = NULL;
-       DATA_BLOB session_key = data_blob(NULL, 0);
+       DATA_BLOB session_key = data_blob_null;
        int rc;
 
-       rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0);
+       rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
+                                    &ads->auth.tgs_expire);
 
        if (rc) {
                return ADS_ERROR_KRB5(rc);
@@ -165,6 +166,7 @@ static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *princip
 
        return ADS_ERROR(rc);
 }
+#endif
 
 /* 
    this performs a SASL/SPNEGO bind
@@ -216,17 +218,48 @@ 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", principal));
 
 #ifdef HAVE_KRB5
        if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
-           got_kerberos_mechanism) {
+           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);                               
+                       }
+                       
+               }
+               
                status = ads_sasl_spnego_krb5_bind(ads, principal);
                if (ADS_ERR_OK(status)) {
                        SAFE_FREE(principal);
                        return status;
                }
 
+               DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
+                         "calling kinit\n", ads_errstr(status)));
+
                status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
 
                if (ADS_ERR_OK(status)) {
@@ -267,7 +300,7 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
        uint32 minor_status;
        gss_name_t serv_name;
        gss_buffer_desc input_name;
-       gss_ctx_id_t context_handle;
+       gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
        gss_OID mech_type = GSS_C_NULL_OID;
        gss_buffer_desc output_token, input_token;
        uint32 ret_flags, conf_state;
@@ -277,9 +310,9 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
        int gss_rc, rc;
        uint8 *p;
        uint32 max_msg_size = 0;
-       char *sname;
+       char *sname = NULL;
        ADS_STATUS status;
-       krb5_principal principal;
+       krb5_principal principal = NULL;
        krb5_context ctx = NULL;
        krb5_enctype enc_types[] = {
 #ifdef ENCTYPE_ARCFOUR_HMAC
@@ -297,30 +330,43 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
        initialize_krb5_error_table();
        status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
        if (!ADS_ERR_OK(status)) {
+               SAFE_FREE(sname);
                return status;
        }
        status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
        if (!ADS_ERR_OK(status)) {
+               SAFE_FREE(sname);
+               krb5_free_context(ctx); 
                return status;
        }
        status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
        if (!ADS_ERR_OK(status)) {
+               SAFE_FREE(sname);
+               krb5_free_context(ctx); 
                return status;
        }
 
-       free(sname);
-       krb5_free_context(ctx); 
-
        input_name.value = &principal;
        input_name.length = sizeof(principal);
 
        gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
+
+       /*
+        * 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.
+        */
+
+       SAFE_FREE(sname);
+
        if (gss_rc) {
+               krb5_free_principal(ctx, principal);
+               krb5_free_context(ctx); 
                return ADS_ERROR_GSS(gss_rc, minor_status);
        }
 
-       context_handle = GSS_C_NO_CONTEXT;
-
        input_token.value = NULL;
        input_token.length = 0;
 
@@ -348,7 +394,7 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
                        goto failed;
                }
 
-               cred.bv_val = output_token.value;
+               cred.bv_val = (char *)output_token.value;
                cred.bv_len = output_token.length;
 
                rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
@@ -373,8 +419,6 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
                if (gss_rc == 0) break;
        }
 
-       gss_release_name(&minor_status, &serv_name);
-
        gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
                            (int *)&conf_state,NULL);
        if (gss_rc) {
@@ -396,18 +440,23 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
 
        gss_release_buffer(&minor_status, &output_token);
 
-       output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
-       p = output_token.value;
+       output_token.length = 4;
+       output_token.value = SMB_MALLOC(output_token.length);
+       p = (uint8 *)output_token.value;
 
        *p++ = 1; /* no sign & seal selection */
        /* choose the same size as the server gave us */
        *p++ = max_msg_size>>16;
        *p++ = max_msg_size>>8;
        *p++ = max_msg_size;
-       snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
-       p += strlen((const char *)p);
-
-       output_token.length = PTR_DIFF(p, output_token.value);
+       /*
+        * we used to add sprintf("dn:%s", ads->config.bind_path) here.
+        * but using ads->config.bind_path is the wrong! It should be
+        * the DN of the user object!
+        *
+        * w2k3 gives an error when we send an incorrect DN, but sending nothing
+        * is ok and matches the information flow used in GSS-SPNEGO.
+        */
 
        gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
                          &output_token, (int *)&conf_state,
@@ -419,7 +468,7 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
 
        free(output_token.value);
 
-       cred.bv_val = input_token.value;
+       cred.bv_val = (char *)input_token.value;
        cred.bv_len = input_token.length;
 
        rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
@@ -429,6 +478,13 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
        gss_release_buffer(&minor_status, &input_token);
 
 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); 
+
        if(scred)
                ber_bvfree(scred);
        return status;