s3-libads: Move to using only the HAVE_KRB5 define
[sfrench/samba-autobuild/.git] / source3 / libads / sasl.c
index 3856f5b49ae78ca4609508a77783d0359dc8d303..02fd7545e5ee08be8291d8a0d0c1ee0004fc4aa9 100644 (file)
 
 #include "includes.h"
 #include "../libcli/auth/spnego.h"
-#include "ntlmssp.h"
+#include "auth/gensec/gensec.h"
+#include "auth_generic.h"
+#include "ads.h"
+#include "smb_krb5.h"
 
 #ifdef HAVE_LDAP
 
 static ADS_STATUS ads_sasl_ntlmssp_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
 {
-       struct ntlmssp_state *ntlmssp_state =
-               (struct ntlmssp_state *)ads->ldap.wrap_private_data;
-       ADS_STATUS status;
+       struct gensec_security *gensec_security =
+               talloc_get_type_abort(ads->ldap.wrap_private_data,
+               struct gensec_security);
        NTSTATUS nt_status;
-       DATA_BLOB sig;
-       uint8 *dptr = ads->ldap.out.buf + (4 + NTLMSSP_SIG_SIZE);
-
-       /* copy the data to the right location */
-       memcpy(dptr, buf, len);
-
-       /* create the signature and may encrypt the data */
-       if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
-               nt_status = ntlmssp_seal_packet(ntlmssp_state,
-                                               dptr, len,
-                                               dptr, len,
-                                               &sig);
-       } else {
-               nt_status = ntlmssp_sign_packet(ntlmssp_state,
-                                               dptr, len,
-                                               dptr, len,
-                                               &sig);
+       DATA_BLOB unwrapped, wrapped;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       unwrapped = data_blob_const(buf, len);
+
+       nt_status = gensec_wrap(gensec_security, frame, &unwrapped, &wrapped);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               TALLOC_FREE(frame);
+               return ADS_ERROR_NT(nt_status);
        }
-       status = ADS_ERROR_NT(nt_status);
-       if (!ADS_ERR_OK(status)) return status;
 
-       /* copy the signature to the right location */
-       memcpy(ads->ldap.out.buf + 4,
-              sig.data, NTLMSSP_SIG_SIZE);
+       if ((ads->ldap.out.size - 4) < wrapped.length) {
+               return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+       }
 
-       data_blob_free(&sig);
+       /* copy the wrapped blob to the right location */
+       memcpy(ads->ldap.out.buf + 4, wrapped.data, wrapped.length);
 
        /* set how many bytes must be written to the underlying socket */
-       ads->ldap.out.left = 4 + NTLMSSP_SIG_SIZE + len;
+       ads->ldap.out.left = 4 + wrapped.length;
+
+       TALLOC_FREE(frame);
 
        return ADS_SUCCESS;
 }
 
 static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads)
 {
-       struct ntlmssp_state *ntlmssp_state =
-               (struct ntlmssp_state *)ads->ldap.wrap_private_data;
-       ADS_STATUS status;
+       struct gensec_security *gensec_security =
+               talloc_get_type_abort(ads->ldap.wrap_private_data,
+               struct gensec_security);
        NTSTATUS nt_status;
-       DATA_BLOB sig;
-       uint8 *dptr = ads->ldap.in.buf + (4 + NTLMSSP_SIG_SIZE);
-       uint32 dlen = ads->ldap.in.ofs - (4 + NTLMSSP_SIG_SIZE);
-
-       /* wrap the signature into a DATA_BLOB */
-       sig = data_blob_const(ads->ldap.in.buf + 4, NTLMSSP_SIG_SIZE);
-
-       /* verify the signature and maybe decrypt the data */
-       if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
-               nt_status = ntlmssp_unseal_packet(ntlmssp_state,
-                                                 dptr, dlen,
-                                                 dptr, dlen,
-                                                 &sig);
-       } else {
-               nt_status = ntlmssp_check_packet(ntlmssp_state,
-                                                dptr, dlen,
-                                                dptr, dlen,
-                                                &sig);
+       DATA_BLOB unwrapped, wrapped;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       wrapped = data_blob_const(ads->ldap.in.buf + 4, ads->ldap.in.ofs - 4);
+
+       nt_status = gensec_unwrap(gensec_security, frame, &wrapped, &unwrapped);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               TALLOC_FREE(frame);
+               return ADS_ERROR_NT(nt_status);
        }
-       status = ADS_ERROR_NT(nt_status);
-       if (!ADS_ERR_OK(status)) return status;
 
-       /* set the amount of bytes for the upper layer and set the ofs to the data */
-       ads->ldap.in.left       = dlen;
-       ads->ldap.in.ofs        = 4 + NTLMSSP_SIG_SIZE;
+       if (wrapped.length < unwrapped.length) {
+               TALLOC_FREE(frame);
+               return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+       }
+
+       /* copy the wrapped blob to the right location */
+       memcpy(ads->ldap.in.buf + 4, unwrapped.data, unwrapped.length);
+
+       /* set how many bytes must be written to the underlying socket */
+       ads->ldap.in.left       = unwrapped.length;
+       ads->ldap.in.ofs        = 4;
+
+       TALLOC_FREE(frame);
 
        return ADS_SUCCESS;
 }
 
 static void ads_sasl_ntlmssp_disconnect(ADS_STRUCT *ads)
 {
-       struct ntlmssp_state *ntlmssp_state =
-               (struct ntlmssp_state *)ads->ldap.wrap_private_data;
+       struct gensec_security *gensec_security =
+               talloc_get_type_abort(ads->ldap.wrap_private_data,
+               struct gensec_security);
 
-       ntlmssp_end(&ntlmssp_state);
+       TALLOC_FREE(gensec_security);
 
        ads->ldap.wrap_ops = NULL;
        ads->ldap.wrap_private_data = NULL;
@@ -130,43 +126,39 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
        NTSTATUS nt_status;
        ADS_STATUS status;
        int turn = 1;
-       uint32 features = 0;
 
-       struct ntlmssp_state *ntlmssp_state;
+       struct auth_generic_state *auth_generic_state;
 
-       nt_status = ntlmssp_client_start(NULL,
-                                        global_myname(),
-                                        lp_workgroup(),
-                                        lp_client_ntlmv2_auth(),
-                                        &ntlmssp_state);
+       nt_status = auth_generic_client_prepare(NULL, &auth_generic_state);
        if (!NT_STATUS_IS_OK(nt_status)) {
                return ADS_ERROR_NT(nt_status);
        }
-       ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
 
-       if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
+       if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_username(auth_generic_state, ads->auth.user_name))) {
                return ADS_ERROR_NT(nt_status);
        }
-       if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
+       if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_domain(auth_generic_state, ads->auth.realm))) {
                return ADS_ERROR_NT(nt_status);
        }
-       if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
+       if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_password(auth_generic_state, ads->auth.password))) {
                return ADS_ERROR_NT(nt_status);
        }
 
        switch (ads->ldap.wrap_type) {
        case ADS_SASLWRAP_TYPE_SEAL:
-               features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
+               gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
+               gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
                break;
        case ADS_SASLWRAP_TYPE_SIGN:
                if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
-                       features = NTLMSSP_FEATURE_SIGN;
+                       gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
                } else {
                        /*
                         * windows servers are broken with sign only,
                         * so we need to use seal here too
                         */
-                       features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
+                       gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
+                       gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
                        ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
                }
                break;
@@ -174,23 +166,28 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
                break;
        }
 
-       ntlmssp_want_feature(ntlmssp_state, features);
+       nt_status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return ADS_ERROR_NT(nt_status);
+       }
 
        blob_in = data_blob_null;
 
        do {
-               nt_status = ntlmssp_update(ntlmssp_state, 
-                                          blob_in, &blob_out);
+               nt_status = gensec_update(auth_generic_state->gensec_security,
+                                         talloc_tos(), NULL, blob_in, &blob_out);
                data_blob_free(&blob_in);
                if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
                     || NT_STATUS_IS_OK(nt_status))
                    && blob_out.length) {
                        if (turn == 1) {
+                               const char *OIDs_ntlm[] = {OID_NTLMSSP, NULL};
                                /* and wrap it in a SPNEGO wrapper */
-                               msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
+                               msg1 = spnego_gen_negTokenInit(talloc_tos(),
+                                               OIDs_ntlm, &blob_out, NULL);
                        } else {
                                /* wrap it in SPNEGO */
-                               msg1 = spnego_gen_auth(blob_out);
+                               msg1 = spnego_gen_auth(talloc_tos(), blob_out);
                        }
 
                        data_blob_free(&blob_out);
@@ -205,7 +202,7 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
                                        ber_bvfree(scred);
                                }
 
-                               ntlmssp_end(&ntlmssp_state);
+                               TALLOC_FREE(auth_generic_state);
                                return ADS_ERROR(rc);
                        }
                        if (scred) {
@@ -217,7 +214,7 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
 
                } else {
 
-                       ntlmssp_end(&ntlmssp_state);
+                       TALLOC_FREE(auth_generic_state);
                        data_blob_free(&blob_out);
                        return ADS_ERROR_NT(nt_status);
                }
@@ -226,20 +223,20 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
                    (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
                        DATA_BLOB tmp_blob = data_blob_null;
                        /* the server might give us back two challenges */
-                       if (!spnego_parse_challenge(blob, &blob_in, 
+                       if (!spnego_parse_challenge(talloc_tos(), blob, &blob_in, 
                                                    &tmp_blob)) {
 
-                               ntlmssp_end(&ntlmssp_state);
+                               TALLOC_FREE(auth_generic_state);
                                data_blob_free(&blob);
                                DEBUG(3,("Failed to parse challenges\n"));
                                return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
                        }
                        data_blob_free(&tmp_blob);
                } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
-                       if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP, 
+                       if (!spnego_parse_auth_response(talloc_tos(), blob, nt_status, OID_NTLMSSP, 
                                                        &blob_in)) {
 
-                               ntlmssp_end(&ntlmssp_state);
+                               TALLOC_FREE(auth_generic_state);
                                data_blob_free(&blob);
                                DEBUG(3,("Failed to parse auth response\n"));
                                return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
@@ -250,29 +247,28 @@ static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
                turn++;
        } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
        
-       /* we have a reference conter on ntlmssp_state, if we are signing
-          then the state will be kept by the signing engine */
-
        if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
-               ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - NTLMSSP_SIG_SIZE;
-               ads->ldap.out.sig_size = NTLMSSP_SIG_SIZE;
+               uint32_t sig_size = gensec_sig_size(auth_generic_state->gensec_security, 0);
+               ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - sig_size;
+               ads->ldap.out.sig_size = sig_size;
                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);
+               status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, auth_generic_state->gensec_security);
                if (!ADS_ERR_OK(status)) {
                        DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
                                ads_errstr(status)));
-                       ntlmssp_end(&ntlmssp_state);
+                       TALLOC_FREE(auth_generic_state);
                        return status;
                }
-       } else {
-               ntlmssp_end(&ntlmssp_state);
+               /* Only keep the gensec_security element around long-term */
+               talloc_steal(NULL, auth_generic_state->gensec_security);
        }
+       TALLOC_FREE(auth_generic_state);
 
        return ADS_ERROR(rc);
 }
 
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
 static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
 {
        gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
@@ -380,7 +376,7 @@ static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t
        uint32 minor_status;
        int gss_rc, rc;
        gss_OID_desc krb5_mech_type =
-       {9, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
+       {9, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
        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};
@@ -503,7 +499,8 @@ static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t
 
        /* and wrap that in a shiny SPNEGO wrapper */
        unwrapped = data_blob_const(output_token.value, output_token.length);
-       wrapped = gen_negTokenTarg(spnego_mechs, unwrapped);
+       wrapped = spnego_gen_negTokenInit(talloc_tos(),
+                       spnego_mechs, &unwrapped, NULL);
        gss_release_buffer(&minor_status, &output_token);
        if (unwrapped.length > wrapped.length) {
                status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
@@ -527,7 +524,7 @@ static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t
                wrapped = data_blob_null;
        }
 
-       ok = spnego_parse_auth_response(wrapped, NT_STATUS_OK,
+       ok = spnego_parse_auth_response(talloc_tos(), wrapped, NT_STATUS_OK,
                                        OID_KERBEROS5_OLD,
                                        &unwrapped);
        if (scred) ber_bvfree(scred);
@@ -612,12 +609,12 @@ failed:
        return status;
 }
 
-#endif /* HAVE_GSSAPI */
+#endif /* HAVE_KRB5 */
 
 #ifdef HAVE_KRB5
 struct ads_service_principal {
         char *string;
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
         gss_name_t name;
 #endif
 };
@@ -626,7 +623,7 @@ static void ads_free_service_principal(struct ads_service_principal *p)
 {
        SAFE_FREE(p->string);
 
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
        if (p->name) {
                uint32 minor_status;
                gss_release_name(&minor_status, &p->name);
@@ -635,16 +632,85 @@ static void ads_free_service_principal(struct ads_service_principal *p)
        ZERO_STRUCTP(p);
 }
 
+
+static 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) {
+                       SAFE_FREE(server);
+                       SAFE_FREE(server_realm);
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
+
+               strlower_m(server);
+               strupper_m(server_realm);
+               if (asprintf(&princ, "ldap/%s@%s", server, server_realm) == -1) {
+                       SAFE_FREE(server);
+                       SAFE_FREE(server_realm);
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
+
+               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) {
+                       SAFE_FREE(server);
+                       SAFE_FREE(server_realm);
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
+
+               strlower_m(server);
+               strupper_m(server_realm);
+               if (asprintf(&princ, "ldap/%s@%s", server, server_realm) == -1) {
+                       SAFE_FREE(server);
+                       SAFE_FREE(server_realm);
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
+
+               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;
+}
+
 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
+#ifdef HAVE_KRB5
        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")};
+       {10, discard_const_p(char, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
        uint32 minor_status;
        int gss_rc;
 #endif
@@ -655,10 +721,12 @@ static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
           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 */
+       /* Also try best guess when we get the w2k8 ignore principal
+          back, or when we are configured to ignore it - gd,
+          abartlet */
 
-       if (!given_principal ||
+       if (!lp_client_use_spnego_principal() ||
+           !given_principal ||
            strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
 
                status = ads_guess_service_principal(ads, &p->string);
@@ -672,7 +740,7 @@ static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
                }
        }
 
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
        input_name.value = p->string;
        input_name.length = strlen(p->string);
 
@@ -700,7 +768,8 @@ static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *prin
                return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
        }
 
-       rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0,
+       rc = spnego_gen_krb5_negTokenInit(talloc_tos(), principal,
+                                    ads->auth.time_offset, &blob, &session_key, 0,
                                     &ads->auth.tgs_expire);
 
        if (rc) {
@@ -724,7 +793,7 @@ static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *prin
 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
                                            struct ads_service_principal *p)
 {
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
        /*
         * we only use the gsskrb5 based implementation
         * when sasl sign or seal is requested.
@@ -775,7 +844,8 @@ 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, &given_principal)) {
+       if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &given_principal, NULL) ||
+                       OIDs[0] == NULL) {
                data_blob_free(&blob);
                status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
                goto failed;
@@ -849,7 +919,7 @@ failed:
        return status;
 }
 
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
 #define MAX_GSS_PASSES 3
 
 /* this performs a SASL/gssapi bind
@@ -977,6 +1047,11 @@ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv
 
        output_token.length = 4;
        output_token.value = SMB_MALLOC(output_token.length);
+       if (!output_token.value) {
+               output_token.length = 0;
+               status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+               goto failed;
+       }
        p = (uint8 *)output_token.value;
 
        RSIVAL(p,0,max_msg_size);
@@ -992,14 +1067,19 @@ static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv
         */
 
        gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
-                         &output_token, &conf_state,
-                         &input_token);
+                       &output_token, /* used as *input* here. */
+                       &conf_state,
+                       &input_token); /* Used as *output* here. */
        if (gss_rc) {
                status = ADS_ERROR_GSS(gss_rc, minor_status);
+               output_token.length = 0;
+               SAFE_FREE(output_token.value);
                goto failed;
        }
 
-       free(output_token.value);
+       /* We've finished with output_token. */
+       SAFE_FREE(output_token.value);
+       output_token.length = 0;
 
        cred.bv_val = (char *)input_token.value;
        cred.bv_len = input_token.length;
@@ -1075,7 +1155,7 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
        return status;
 }
 
-#endif /* HAVE_GSSAPI */
+#endif /* HAVE_KRB5 */
 
 /* mapping between SASL mechanisms and functions */
 static struct {
@@ -1083,7 +1163,7 @@ static struct {
        ADS_STATUS (*fn)(ADS_STRUCT *);
 } sasl_mechanisms[] = {
        {"GSS-SPNEGO", ads_sasl_spnego_bind},
-#ifdef HAVE_GSSAPI
+#ifdef HAVE_KRB5
        {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
 #endif
        {NULL, NULL}
@@ -1117,7 +1197,17 @@ ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
                for (j=0;values && values[j];j++) {
                        if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
                                DEBUG(4,("Found SASL mechanism %s\n", values[j]));
+retry:
                                status = sasl_mechanisms[i].fn(ads);
+                               if (status.error_type == ENUM_ADS_ERROR_LDAP &&
+                                   status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
+                                   ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
+                               {
+                                       DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
+                                                "retrying with signing enabled\n"));
+                                       ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
+                                       goto retry;
+                               }
                                ldap_value_free(values);
                                ldap_msgfree(res);
                                return status;