s3-kerberos: remove smb_krb5_get_tkt_from_creds().
[nivanova/samba-autobuild/.git] / source3 / libads / sasl.c
index 0bc741c02a450d44e71bebaa08431edc467f8bb0..9b4d8bd2d444589ab56686c2544125ed78f3300a 100644 (file)
@@ -18,6 +18,7 @@
 */
 
 #include "includes.h"
+#include "../libcli/auth/spnego.h"
 
 #ifdef HAVE_LDAP
 
@@ -126,6 +127,7 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
        struct berval cred, *scred = NULL;
        int rc;
        NTSTATUS nt_status;
+       ADS_STATUS status;
        int turn = 1;
        uint32 features = 0;
 
@@ -246,12 +248,17 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
           then the state will be kept by the signing engine */
 
        if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
-               ads->ldap.out.min = 4;
-               ads->ldap.out.max = 0x0FFFFFFF - NTLMSSP_SIG_SIZE;
+               ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - NTLMSSP_SIG_SIZE;
                ads->ldap.out.sig_size = NTLMSSP_SIG_SIZE;
-               ads->ldap.in.min = 4;
-               ads->ldap.in.max = 0x0FFFFFFF;
-               ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, ntlmssp_state);
+               ads->ldap.in.min_wrapped = ads->ldap.out.sig_size;
+               ads->ldap.in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
+               status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, ntlmssp_state);
+               if (!ADS_ERR_OK(status)) {
+                       DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
+                               ads_errstr(status)));
+                       ntlmssp_end(&ntlmssp_state);
+                       return status;
+               }
        } else {
                ntlmssp_end(&ntlmssp_state);
        }
@@ -262,7 +269,7 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
 #ifdef HAVE_GSSAPI
 static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
 {
-       gss_ctx_id_t context_handle = ads->ldap.wrap_private_data;
+       gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
        ADS_STATUS status;
        int gss_rc;
        uint32 minor_status;
@@ -303,7 +310,7 @@ static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
 
 static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
 {
-       gss_ctx_id_t context_handle = ads->ldap.wrap_private_data;
+       gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
        ADS_STATUS status;
        int gss_rc;
        uint32 minor_status;
@@ -323,7 +330,7 @@ static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
                return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
        }
 
-       if (wrapped.length < wrapped.length) {
+       if (wrapped.length < unwrapped.length) {
                return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
        }
 
@@ -341,7 +348,7 @@ static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
 
 static void ads_sasl_gssapi_disconnect(ADS_STRUCT *ads)
 {
-       gss_ctx_id_t context_handle = ads->ldap.wrap_private_data;
+       gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
        uint32 minor_status;
 
        gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
@@ -363,7 +370,7 @@ static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
 static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
 {
        ADS_STATUS status;
-       BOOL ok;
+       bool ok;
        uint32 minor_status;
        int gss_rc, rc;
        gss_OID_desc krb5_mech_type =
@@ -567,39 +574,43 @@ static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t
        }
 
        if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
-               uint32 max_msg_size = 0x0A000000;
+               uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
 
                gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
                                             (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
                                             GSS_C_QOP_DEFAULT,
-                                            max_msg_size, &ads->ldap.out.max);
+                                            max_msg_size, &ads->ldap.out.max_unwrapped);
                if (gss_rc) {
                        status = ADS_ERROR_GSS(gss_rc, minor_status);
                        goto failed;
                }
 
-               ads->ldap.out.min = 4;
-               ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max;
-               ads->ldap.in.min = 4;
-               ads->ldap.in.max = max_msg_size;
-               ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
+               ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
+               ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
+               ads->ldap.in.max_wrapped = max_msg_size;
+               status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
+               if (!ADS_ERR_OK(status)) {
+                       DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
+                               ads_errstr(status)));
+                       goto failed;
+               }
                /* make sure we don't free context_handle */
                context_handle = GSS_C_NO_CONTEXT;
        }
 
+       status = ADS_SUCCESS;
+
 failed:
        if (context_handle != GSS_C_NO_CONTEXT)
                gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
        return status;
 }
 
-#endif
+#endif /* HAVE_GSSAPI */
 
 #ifdef HAVE_KRB5
 struct ads_service_principal {
-        krb5_context ctx;
         char *string;
-        krb5_principal principal;
 #ifdef HAVE_GSSAPI
         gss_name_t name;
 #endif
@@ -615,14 +626,6 @@ static void ads_free_service_principal(struct ads_service_principal *p)
                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);
 }
 
@@ -631,102 +634,41 @@ static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
                                                 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")};
+       /* 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 
+       /* 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);
-               }
+       /* Also try best guess when we get the w2k8 ignore
+          principal back - gd */
 
-               strlower_m(server);
-               strupper_m(server_realm);
-               asprintf(&p->string, "ldap/%s@%s", server, server_realm);
+       if (!given_principal ||
+           strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
 
-               SAFE_FREE(server);
-               SAFE_FREE(server_realm);
-
-               if (!p->string) {
-                       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);
+               status = ads_guess_service_principal(ads, &p->string);
+               if (!ADS_ERR_OK(status)) {
+                       return status;
                }
-
-               strlower_m(server);
-               strupper_m(server_realm);
-               asprintf(&p->string, "ldap/%s@%s", server, server_realm);
-
-               SAFE_FREE(server);
-               SAFE_FREE(server_realm);
-
+       } else {
+               p->string = SMB_STRDUP(given_principal);
                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);
+       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) {
@@ -735,7 +677,7 @@ static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
        }
 #endif
 
-       return status;
+       return ADS_SUCCESS;
 }
 
 /* 
@@ -793,7 +735,7 @@ static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
 #endif
        return ads_sasl_spnego_rawkrb5_bind(ads, p->string);
 }
-#endif
+#endif /* HAVE_KRB5 */
 
 /* 
    this performs a SASL/SPNEGO bind
@@ -807,7 +749,7 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
        char *given_principal = NULL;
        char *OIDs[ASN1_MAX_OIDS];
 #ifdef HAVE_KRB5
-       BOOL got_kerberos_mechanism = False;
+       bool got_kerberos_mechanism = False;
 #endif
 
        rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
@@ -843,7 +785,7 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
                        got_kerberos_mechanism = True;
                }
 #endif
-               free(OIDs[i]);
+               talloc_free(OIDs[i]);
        }
        DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal));
 
@@ -854,7 +796,7 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
                struct ads_service_principal p;
 
                status = ads_generate_service_principal(ads, given_principal, &p);
-               SAFE_FREE(given_principal);
+               TALLOC_FREE(given_principal);
                if (!ADS_ERR_OK(status)) {
                        return status;
                }
@@ -872,6 +814,11 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
 
                if (ADS_ERR_OK(status)) {
                        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);
@@ -884,7 +831,7 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
        } else
 #endif
        {
-               SAFE_FREE(given_principal);
+               TALLOC_FREE(given_principal);
        }
 
        /* lets do NTLMSSP ... this has the big advantage that we don't need
@@ -918,23 +865,19 @@ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv
        int i=0;
        int gss_rc, rc;
        uint8 *p;
-       uint32 max_msg_size = 0;
+       uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
+       uint8 wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
        ADS_STATUS status;
 
        input_token.value = NULL;
        input_token.length = 0;
 
-       req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
-       switch (ads->ldap.wrap_type) {
-       case ADS_SASLWRAP_TYPE_SEAL:
-               req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
-               break;
-       case ADS_SASLWRAP_TYPE_SIGN:
-               req_flags |= GSS_C_INTEG_FLAG;
-               break;
-       case ADS_SASLWRAP_TYPE_PLAIN:
-               break;
-       }
+       /*
+        * Note: here we always ask the gssapi for sign and seal
+        *       as this is negotiated later after the mutal
+        *       authentication
+        */
+       req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
 
        for (i=0; i < MAX_GSS_PASSES; i++) {
                gss_rc = gss_init_sec_context(&minor_status,
@@ -950,11 +893,10 @@ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv
                                          &output_token,
                                          &ret_flags,
                                          NULL);
-
-               if (input_token.value) {
-                       gss_release_buffer(&minor_status, &input_token);
+               if (scred) {
+                       ber_bvfree(scred);
+                       scred = NULL;
                }
-
                if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
                        status = ADS_ERROR_GSS(gss_rc, minor_status);
                        goto failed;
@@ -987,13 +929,15 @@ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv
 
        gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
                            &conf_state,NULL);
+       if (scred) {
+               ber_bvfree(scred);
+               scred = NULL;
+       }
        if (gss_rc) {
                status = ADS_ERROR_GSS(gss_rc, minor_status);
                goto failed;
        }
 
-       gss_release_buffer(&minor_status, &input_token);
-
        p = (uint8 *)output_token.value;
 
 #if 0
@@ -1001,20 +945,37 @@ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv
 #endif
 
        if (p) {
-               max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
+               wrap_type = CVAL(p,0);
+               SCVAL(p,0,0);
+               max_msg_size = RIVAL(p,0);
        }
 
        gss_release_buffer(&minor_status, &output_token);
 
+       if (!(wrap_type & ads->ldap.wrap_type)) {
+               /*
+                * the server doesn't supports the wrap
+                * type we want :-(
+                */
+               DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n",
+                       ads->ldap.wrap_type, wrap_type));
+               DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n"));
+               status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+               goto failed;
+       }
+
+       /* 0x58 is the minimum windows accepts */
+       if (max_msg_size < 0x58) {
+               max_msg_size = 0x58;
+       }
+
        output_token.length = 4;
        output_token.value = SMB_MALLOC(output_token.length);
        p = (uint8 *)output_token.value;
 
-       *p++ = ads->ldap.wrap_type;
-       /* choose the same size as the server gave us */
-       *p++ = max_msg_size>>16;
-       *p++ = max_msg_size>>8;
-       *p++ = max_msg_size;
+       RSIVAL(p,0,max_msg_size);
+       SCVAL(p,0,ads->ldap.wrap_type);
+
        /*
         * we used to add sprintf("dn:%s", ads->config.bind_path) here.
         * but using ads->config.bind_path is the wrong! It should be
@@ -1049,20 +1010,25 @@ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv
                gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
                                             (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
                                             GSS_C_QOP_DEFAULT,
-                                            max_msg_size, &ads->ldap.out.max);
+                                            max_msg_size, &ads->ldap.out.max_unwrapped);
                if (gss_rc) {
                        status = ADS_ERROR_GSS(gss_rc, minor_status);
                        goto failed;
                }
 
-               ads->ldap.out.min = 4;
-               ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max;
-               ads->ldap.in.min = 4;
-               ads->ldap.in.max = max_msg_size;
-               ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
+               ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
+               ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
+               ads->ldap.in.max_wrapped = max_msg_size;
+               status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
+               if (!ADS_ERR_OK(status)) {
+                       DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
+                               ads_errstr(status)));
+                       goto failed;
+               }
                /* make sure we don't free context_handle */
                context_handle = GSS_C_NO_CONTEXT;
        }
+
 failed:
 
        if (context_handle != GSS_C_NO_CONTEXT)
@@ -1103,7 +1069,7 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
        return status;
 }
 
-#endif /* HAVE_GGSAPI */
+#endif /* HAVE_GSSAPI */
 
 /* mapping between SASL mechanisms and functions */
 static struct {