TODO: s3-libads: Pass cli_credentials to ads_sasl_spnego_gensec_bind()
[metze/samba/wip.git] / source3 / libads / sasl.c
index e205e9f22955789d192efe0cebcc2bba7f118670..841f24c6487106e4de33620309c8ec3d86a89a6f 100644 (file)
 #include "ads.h"
 #include "smb_krb5.h"
 #include "system/gssapi.h"
-#include "lib/param/loadparm.h"
+#include "lib/param/param.h"
+#include "krb5_env.h"
+
+static struct cli_credentials *ads_sasl_creds_init(TALLOC_CTX *mem_ctx,
+                                                  const char *username,
+                                                  const char *password,
+                                                  const char *domain,
+                                                  const char *realm,
+                                                  enum credentials_use_kerberos krb5_state)
+{
+       struct loadparm_context *lp_ctx;
+       struct cli_credentials *creds;
+       bool ok;
+
+       creds = cli_credentials_init(mem_ctx);
+       if (creds == NULL) {
+               return NULL;
+       }
+
+       lp_ctx = loadparm_init_s3(creds, loadparm_s3_helpers());
+       if (lp_ctx == NULL) {
+               goto fail;
+       }
+       cli_credentials_set_conf(creds, lp_ctx);
+
+       if (username != NULL) {
+               ok = cli_credentials_set_username(creds,
+                                                 username,
+                                                 CRED_SPECIFIED);
+               if (!ok) {
+                       goto fail;
+               }
+       }
+
+       if (password != NULL) {
+               ok = cli_credentials_set_password(creds,
+                                                 password,
+                                                 CRED_SPECIFIED);
+               if (!ok) {
+                       goto fail;
+               }
+       }
+
+       if (domain != NULL) {
+               ok = cli_credentials_set_domain(creds,
+                                               domain,
+                                               CRED_SPECIFIED);
+               if (!ok) {
+                       goto fail;
+               }
+       }
+
+       if (realm != NULL) {
+               ok = cli_credentials_set_realm(creds,
+                                              realm,
+                                              CRED_SPECIFIED);
+               if (!ok) {
+                       goto fail;
+               }
+       }
+
+       cli_credentials_set_kerberos_state(creds, krb5_state);
+
+       return creds;
+fail:
+       TALLOC_FREE(creds);
+       return NULL;
+}
 
 #ifdef HAVE_LDAP
 
-static ADS_STATUS ads_sasl_gensec_wrap(ADS_STRUCT *ads, uint8_t *buf, uint32_t len)
+static ADS_STATUS ads_sasl_gensec_wrap(struct ads_saslwrap *wrap,
+                                      uint8_t *buf, uint32_t len)
 {
        struct gensec_security *gensec_security =
-               talloc_get_type_abort(ads->ldap.wrap_private_data,
+               talloc_get_type_abort(wrap->wrap_private_data,
                struct gensec_security);
        NTSTATUS nt_status;
        DATA_BLOB unwrapped, wrapped;
@@ -46,32 +114,32 @@ static ADS_STATUS ads_sasl_gensec_wrap(ADS_STRUCT *ads, uint8_t *buf, uint32_t l
                return ADS_ERROR_NT(nt_status);
        }
 
-       if ((ads->ldap.out.size - 4) < wrapped.length) {
+       if ((wrap->out.size - 4) < wrapped.length) {
                TALLOC_FREE(frame);
                return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
        }
 
        /* copy the wrapped blob to the right location */
-       memcpy(ads->ldap.out.buf + 4, wrapped.data, wrapped.length);
+       memcpy(wrap->out.buf + 4, wrapped.data, wrapped.length);
 
        /* set how many bytes must be written to the underlying socket */
-       ads->ldap.out.left = 4 + wrapped.length;
+       wrap->out.left = 4 + wrapped.length;
 
        TALLOC_FREE(frame);
 
        return ADS_SUCCESS;
 }
 
-static ADS_STATUS ads_sasl_gensec_unwrap(ADS_STRUCT *ads)
+static ADS_STATUS ads_sasl_gensec_unwrap(struct ads_saslwrap *wrap)
 {
        struct gensec_security *gensec_security =
-               talloc_get_type_abort(ads->ldap.wrap_private_data,
+               talloc_get_type_abort(wrap->wrap_private_data,
                struct gensec_security);
        NTSTATUS nt_status;
        DATA_BLOB unwrapped, wrapped;
        TALLOC_CTX *frame = talloc_stackframe();
 
-       wrapped = data_blob_const(ads->ldap.in.buf + 4, ads->ldap.in.ofs - 4);
+       wrapped = data_blob_const(wrap->in.buf + 4, wrap->in.ofs - 4);
 
        nt_status = gensec_unwrap(gensec_security, frame, &wrapped, &unwrapped);
        if (!NT_STATUS_IS_OK(nt_status)) {
@@ -85,27 +153,27 @@ static ADS_STATUS ads_sasl_gensec_unwrap(ADS_STRUCT *ads)
        }
 
        /* copy the wrapped blob to the right location */
-       memcpy(ads->ldap.in.buf + 4, unwrapped.data, unwrapped.length);
+       memcpy(wrap->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;
+       wrap->in.left   = unwrapped.length;
+       wrap->in.ofs    = 4;
 
        TALLOC_FREE(frame);
 
        return ADS_SUCCESS;
 }
 
-static void ads_sasl_gensec_disconnect(ADS_STRUCT *ads)
+static void ads_sasl_gensec_disconnect(struct ads_saslwrap *wrap)
 {
        struct gensec_security *gensec_security =
-               talloc_get_type_abort(ads->ldap.wrap_private_data,
+               talloc_get_type_abort(wrap->wrap_private_data,
                struct gensec_security);
 
        TALLOC_FREE(gensec_security);
 
-       ads->ldap.wrap_ops = NULL;
-       ads->ldap.wrap_private_data = NULL;
+       wrap->wrap_ops = NULL;
+       wrap->wrap_private_data = NULL;
 }
 
 static const struct ads_saslwrap_ops ads_sasl_gensec_ops = {
@@ -121,7 +189,7 @@ static const struct ads_saslwrap_ops ads_sasl_gensec_ops = {
 */
 static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads,
                                const char *sasl,
-                               enum credentials_use_kerberos krb5_state,
+                               struct cli_credentials *creds,
                                const char *target_service,
                                const char *target_hostname,
                                const DATA_BLOB server_blob)
@@ -134,19 +202,17 @@ static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads,
        struct auth_generic_state *auth_generic_state;
        bool use_spnego_principal = lp_client_use_spnego_principal();
        const char *sasl_list[] = { sasl, NULL };
+       enum credentials_use_kerberos krb5_state;
+       NTTIME end_nt_time;
+       struct ads_saslwrap *wrap = &ads->ldap_wrap_data;
 
        nt_status = auth_generic_client_prepare(NULL, &auth_generic_state);
        if (!NT_STATUS_IS_OK(nt_status)) {
                return ADS_ERROR_NT(nt_status);
        }
 
-       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 = auth_generic_set_domain(auth_generic_state, ads->auth.realm))) {
-               return ADS_ERROR_NT(nt_status);
-       }
-       if (!NT_STATUS_IS_OK(nt_status = auth_generic_set_password(auth_generic_state, ads->auth.password))) {
+       nt_status = auth_generic_set_creds(auth_generic_state, creds);
+       if (!NT_STATUS_IS_OK(nt_status)) {
                return ADS_ERROR_NT(nt_status);
        }
 
@@ -154,13 +220,11 @@ static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads,
                use_spnego_principal = false;
        }
 
+       krb5_state = cli_credentials_get_kerberos_state(creds);
        if (krb5_state == CRED_DONT_USE_KERBEROS) {
                use_spnego_principal = false;
        }
 
-       cli_credentials_set_kerberos_state(auth_generic_state->credentials,
-                                          krb5_state);
-
        if (target_service != NULL) {
                nt_status = gensec_set_target_service(
                                        auth_generic_state->gensec_security,
@@ -183,7 +247,7 @@ static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads,
                use_spnego_principal = false;
        }
 
-       switch (ads->ldap.wrap_type) {
+       switch (wrap->wrap_type) {
        case ADS_SASLWRAP_TYPE_SEAL:
                gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN);
                gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
@@ -276,196 +340,77 @@ static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads,
        data_blob_free(&blob_in);
        data_blob_free(&blob_out);
 
-       if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
-               size_t max_wrapped = gensec_max_wrapped_size(auth_generic_state->gensec_security);
-               ads->ldap.out.max_unwrapped = gensec_max_input_size(auth_generic_state->gensec_security);
+       if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SEAL) {
+               bool ok;
 
-               ads->ldap.out.sig_size = max_wrapped - ads->ldap.out.max_unwrapped;
-               ads->ldap.in.min_wrapped = ads->ldap.out.sig_size;
-               ads->ldap.in.max_wrapped = max_wrapped;
-               status = ads_setup_sasl_wrapping(ads, &ads_sasl_gensec_ops, auth_generic_state->gensec_security);
-               if (!ADS_ERR_OK(status)) {
-                       DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
-                               ads_errstr(status)));
+               ok = gensec_have_feature(auth_generic_state->gensec_security,
+                                        GENSEC_FEATURE_SEAL);
+               if (!ok) {
+                       DEBUG(0,("The gensec feature sealing request, but unavailable\n"));
                        TALLOC_FREE(auth_generic_state);
-                       return status;
-               }
-               /* 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_KRB5
-static ADS_STATUS ads_init_gssapi_cred(ADS_STRUCT *ads, gss_cred_id_t *cred)
-{
-       ADS_STATUS status;
-       krb5_context kctx;
-       krb5_error_code kerr;
-       krb5_ccache kccache = NULL;
-       uint32_t maj, min;
-
-       *cred = GSS_C_NO_CREDENTIAL;
-
-       if (!ads->auth.ccache_name) {
-               return ADS_SUCCESS;
-       }
-
-       kerr = krb5_init_context(&kctx);
-       if (kerr) {
-               return ADS_ERROR_KRB5(kerr);
-       }
-
-#ifdef HAVE_GSS_KRB5_IMPORT_CRED
-       kerr = krb5_cc_resolve(kctx, ads->auth.ccache_name, &kccache);
-       if (kerr) {
-               status = ADS_ERROR_KRB5(kerr);
-               goto done;
-       }
-
-       maj = gss_krb5_import_cred(&min, kccache, NULL, NULL, cred);
-       if (maj != GSS_S_COMPLETE) {
-               status = ADS_ERROR_GSS(maj, min);
-               goto done;
-       }
-#else
-       /* We need to fallback to overriding the default creds.
-        * This operation is not thread safe as it changes the process
-        * environment variable, but we do not have any better option
-        * with older kerberos libraries */
-       {
-               const char *oldccname = NULL;
-
-               oldccname = getenv("KRB5CCNAME");
-               setenv("KRB5CCNAME", ads->auth.ccache_name, 1);
-
-               maj = gss_acquire_cred(&min, GSS_C_NO_NAME, GSS_C_INDEFINITE,
-                                      NULL, GSS_C_INITIATE, cred, NULL, NULL);
-
-               if (oldccname) {
-                       setenv("KRB5CCNAME", oldccname, 1);
-               } else {
-                       unsetenv("KRB5CCNAME");
+                       return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
                }
 
-               if (maj != GSS_S_COMPLETE) {
-                       status = ADS_ERROR_GSS(maj, min);
-                       goto done;
+               ok = gensec_have_feature(auth_generic_state->gensec_security,
+                                        GENSEC_FEATURE_SIGN);
+               if (!ok) {
+                       DEBUG(0,("The gensec feature signing request, but unavailable\n"));
+                       TALLOC_FREE(auth_generic_state);
+                       return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
                }
-       }
-#endif
 
-       status = ADS_SUCCESS;
-
-done:
-       if (!ADS_ERR_OK(status) && kccache != NULL) {
-               krb5_cc_close(kctx, kccache);
-       }
-       krb5_free_context(kctx);
-       return status;
-}
-
-static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8_t *buf, uint32_t len)
-{
-       gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
-       ADS_STATUS status;
-       int gss_rc;
-       uint32_t minor_status;
-       gss_buffer_desc unwrapped, wrapped;
-       int conf_req_flag, conf_state;
-
-       unwrapped.value         = buf;
-       unwrapped.length        = len;
-
-       /* for now request sign and seal */
-       conf_req_flag   = (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL);
-
-       gss_rc = gss_wrap(&minor_status, context_handle,
-                         conf_req_flag, GSS_C_QOP_DEFAULT,
-                         &unwrapped, &conf_state,
-                         &wrapped);
-       status = ADS_ERROR_GSS(gss_rc, minor_status);
-       if (!ADS_ERR_OK(status)) return status;
+       } else if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SIGN) {
+               bool ok;
 
-       if (conf_req_flag && conf_state == 0) {
-               return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
+               ok = gensec_have_feature(auth_generic_state->gensec_security,
+                                        GENSEC_FEATURE_SIGN);
+               if (!ok) {
+                       DEBUG(0,("The gensec feature signing request, but unavailable\n"));
+                       TALLOC_FREE(auth_generic_state);
+                       return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
+               }
        }
 
-       if ((ads->ldap.out.size - 4) < wrapped.length) {
-               return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+       ads->auth.tgs_expire = LONG_MAX;
+       end_nt_time = gensec_expire_time(auth_generic_state->gensec_security);
+       if (end_nt_time != GENSEC_EXPIRE_TIME_INFINITY) {
+               struct timeval tv;
+               nttime_to_timeval(&tv, end_nt_time);
+               ads->auth.tgs_expire = tv.tv_sec;
        }
 
-       /* copy the wrapped blob to the right location */
-       memcpy(ads->ldap.out.buf + 4, wrapped.value, wrapped.length);
-
-       /* set how many bytes must be written to the underlying socket */
-       ads->ldap.out.left = 4 + wrapped.length;
-
-       gss_release_buffer(&minor_status, &wrapped);
+       if (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
+               size_t max_wrapped =
+                       gensec_max_wrapped_size(auth_generic_state->gensec_security);
+               wrap->out.max_unwrapped =
+                       gensec_max_input_size(auth_generic_state->gensec_security);
 
-       return ADS_SUCCESS;
-}
-
-static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
-{
-       gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
-       ADS_STATUS status;
-       int gss_rc;
-       uint32_t minor_status;
-       gss_buffer_desc unwrapped, wrapped;
-       int conf_state;
-
-       wrapped.value   = ads->ldap.in.buf + 4;
-       wrapped.length  = ads->ldap.in.ofs - 4;
-
-       gss_rc = gss_unwrap(&minor_status, context_handle,
-                           &wrapped, &unwrapped,
-                           &conf_state, GSS_C_QOP_DEFAULT);
-       status = ADS_ERROR_GSS(gss_rc, minor_status);
-       if (!ADS_ERR_OK(status)) return status;
-
-       if (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL && conf_state == 0) {
-               return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
-       }
-
-       if (wrapped.length < unwrapped.length) {
-               return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+               wrap->out.sig_size = max_wrapped - wrap->out.max_unwrapped;
+               /*
+                * Note that we have to truncate this to 0x2C
+                * (taken from a capture with LDAP unbind), as the
+                * signature size is not constant for Kerberos with
+                * arcfour-hmac-md5.
+                */
+               wrap->in.min_wrapped = MIN(wrap->out.sig_size, 0x2C);
+               wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
+               status = ads_setup_sasl_wrapping(wrap, ads->ldap.ld,
+                                                &ads_sasl_gensec_ops,
+                                                auth_generic_state->gensec_security);
+               if (!ADS_ERR_OK(status)) {
+                       DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
+                               ads_errstr(status)));
+                       TALLOC_FREE(auth_generic_state);
+                       return status;
+               }
+               /* Only keep the gensec_security element around long-term */
+               talloc_steal(NULL, auth_generic_state->gensec_security);
        }
+       TALLOC_FREE(auth_generic_state);
 
-       /* copy the wrapped blob to the right location */
-       memcpy(ads->ldap.in.buf + 4, unwrapped.value, unwrapped.length);
-
-       /* set how many bytes must be written to the underlying socket */
-       ads->ldap.in.left       = unwrapped.length;
-       ads->ldap.in.ofs        = 4;
-
-       gss_release_buffer(&minor_status, &unwrapped);
-
-       return ADS_SUCCESS;
-}
-
-static void ads_sasl_gssapi_disconnect(ADS_STRUCT *ads)
-{
-       gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
-       uint32_t minor_status;
-
-       gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
-
-       ads->ldap.wrap_ops = NULL;
-       ads->ldap.wrap_private_data = NULL;
+       return ADS_ERROR(rc);
 }
 
-static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
-       .name           = "gssapi",
-       .wrap           = ads_sasl_gssapi_wrap,
-       .unwrap         = ads_sasl_gssapi_unwrap,
-       .disconnect     = ads_sasl_gssapi_disconnect
-};
-
-#endif /* HAVE_KRB5 */
-
 #ifdef HAVE_KRB5
 struct ads_service_principal {
        char *service;
@@ -647,50 +592,16 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
 {
        TALLOC_CTX *frame = talloc_stackframe();
        struct ads_service_principal p = {0};
-       struct berval *scred=NULL;
-       int rc, i;
        ADS_STATUS status;
-       DATA_BLOB blob;
-       char *given_principal = NULL;
-       char *OIDs[ASN1_MAX_OIDS];
-#ifdef HAVE_KRB5
-       bool got_kerberos_mechanism = False;
-#endif
-
-       rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
-
-       if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
-               status = ADS_ERROR(rc);
-               goto done;
-       }
-
-       blob = data_blob(scred->bv_val, scred->bv_len);
+       enum credentials_use_kerberos krb5_state;
+       struct cli_credentials *creds;
 
-       ber_bvfree(scred);
-
-#if 0
-       file_save("sasl_spnego.dat", blob.data, blob.length);
-#endif
-
-       /* the server sent us the first part of the SPNEGO exchange in the negprot 
-          reply */
-       if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &given_principal, NULL) ||
-                       OIDs[0] == NULL) {
-               status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
-               goto done;
-       }
-       TALLOC_FREE(given_principal);
-
-       /* make sure the server understands kerberos */
-       for (i=0;OIDs[i];i++) {
-               DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
-#ifdef HAVE_KRB5
-               if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
-                   strcmp(OIDs[i], OID_KERBEROS5) == 0) {
-                       got_kerberos_mechanism = True;
-               }
-#endif
-               talloc_free(OIDs[i]);
+       if (ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) {
+               krb5_state = CRED_DONT_USE_KERBEROS;
+       } else if (ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP) {
+               krb5_state = CRED_AUTO_USE_KERBEROS;
+       } else {
+               krb5_state = CRED_MUST_USE_KERBEROS;
        }
 
        status = ads_generate_service_principal(ads, &p);
@@ -698,297 +609,73 @@ static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
                goto done;
        }
 
-#ifdef HAVE_KRB5
-       if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
-           got_kerberos_mechanism) 
-       {
-               status = ads_sasl_spnego_gensec_bind(ads, "GSS-SPNEGO",
-                                                    CRED_MUST_USE_KERBEROS,
-                                                    p.service, p.hostname,
-                                                    blob);
-               if (ADS_ERR_OK(status)) {
-                       ads_free_service_principal(&p);
-                       goto done;
-               }
-
-               DEBUG(10,("ads_sasl_spnego_gensec_bind(KRB5) failed with: %s, "
-                         "calling kinit\n", ads_errstr(status)));
-
-               status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
-
-               if (ADS_ERR_OK(status)) {
-                       status = ads_sasl_spnego_gensec_bind(ads, "GSS-SPNEGO",
-                                                       CRED_MUST_USE_KERBEROS,
-                                                       p.service, p.hostname,
-                                                       blob);
-                       if (!ADS_ERR_OK(status)) {
-                               DEBUG(0,("kinit succeeded but "
-                                       "ads_sasl_spnego_gensec_bind(KRB5) failed: %s\n",
-                                       ads_errstr(status)));
-                       }
-               }
-
-               /* only fallback to NTLMSSP if allowed */
-               if (ADS_ERR_OK(status) || 
-                   !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
-                       goto done;
-               }
+       creds = ads_sasl_creds_init(frame,
+                                   ads->auth.user_name,
+                                   ads->auth.password,
+                                   ads->auth.realm,
+                                   ads->auth.realm,
+                                   krb5_state);
+       if (creds == NULL) {
+               status = ADS_ERROR_SYSTEM(ENOMEM);
+               goto done;
        }
-#endif
 
-       /* 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 
-          library for HMAC_MD4 encryption */
-       status = ads_sasl_spnego_gensec_bind(ads, "GSS-SPNEGO",
-                                            CRED_DONT_USE_KERBEROS,
-                                            p.service, p.hostname,
+       status = ads_sasl_spnego_gensec_bind(ads,
+                                            "GSS-SPNEGO",
+                                            creds,
+                                            p.service,
+                                            p.hostname,
                                             data_blob_null);
+
 done:
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(1,("ads_sasl_spnego_gensec_bind(GSS-SPNEGO) failed "
+                        "for %s/%s with user[%s] realm=[%s]: %s\n",
+                         p.service, p.hostname,
+                         ads->auth.user_name,
+                         ads->auth.realm,
+                         ads_errstr(status)));
+       }
        ads_free_service_principal(&p);
        TALLOC_FREE(frame);
        return status;
 }
 
 #ifdef HAVE_KRB5
-#define MAX_GSS_PASSES 3
-
-/* this performs a SASL/gssapi bind
-   we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
-   is very dependent on correctly configured DNS whereas
-   this routine is much less fragile
-   see RFC2078 and RFC2222 for details
-*/
-static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
-{
-       uint32_t minor_status;
-       gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
-       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_t req_flags, ret_flags;
-       int conf_state;
-       struct berval cred;
-       struct berval *scred = NULL;
-       int i=0;
-       int gss_rc, rc;
-       uint8_t *p;
-       uint32_t max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
-       uint8_t wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
-       ADS_STATUS status;
-
-       input_token.value = NULL;
-       input_token.length = 0;
-
-       status = ads_init_gssapi_cred(ads, &gss_cred);
-       if (!ADS_ERR_OK(status)) {
-               goto failed;
-       }
-
-       /*
-        * 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,
-                                         gss_cred,
-                                         &context_handle,
-                                         serv_name,
-                                         mech_type,
-                                         req_flags,
-                                         0,
-                                         NULL,
-                                         &input_token,
-                                         NULL,
-                                         &output_token,
-                                         &ret_flags,
-                                         NULL);
-               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;
-               }
-
-               cred.bv_val = (char *)output_token.value;
-               cred.bv_len = output_token.length;
-
-               rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
-                                     &scred);
-               if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
-                       status = ADS_ERROR(rc);
-                       goto failed;
-               }
-
-               if (output_token.value) {
-                       gss_release_buffer(&minor_status, &output_token);
-               }
-
-               if (scred) {
-                       input_token.value = scred->bv_val;
-                       input_token.length = scred->bv_len;
-               } else {
-                       input_token.value = NULL;
-                       input_token.length = 0;
-               }
-
-               if (gss_rc == 0) break;
-       }
-
-       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;
-       }
-
-       p = (uint8_t *)output_token.value;
-
-#if 0
-       file_save("sasl_gssapi.dat", output_token.value, output_token.length);
-#endif
-
-       if (p) {
-               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);
-       if (!output_token.value) {
-               output_token.length = 0;
-               status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
-               goto failed;
-       }
-       p = (uint8_t *)output_token.value;
-
-       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
-        * 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, /* 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;
-       }
-
-       /* 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;
-
-       rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
-                             &scred);
-       gss_release_buffer(&minor_status, &input_token);
-       status = ADS_ERROR(rc);
-       if (!ADS_ERR_OK(status)) {
-               goto failed;
-       }
-
-       if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
-               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_unwrapped);
-               if (gss_rc) {
-                       status = ADS_ERROR_GSS(gss_rc, minor_status);
-                       goto failed;
-               }
-
-               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 (gss_cred != GSS_C_NO_CREDENTIAL)
-               gss_release_cred(&minor_status, &gss_cred);
-       if (context_handle != GSS_C_NO_CONTEXT)
-               gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
-
-       if(scred)
-               ber_bvfree(scred);
-       return status;
-}
 
 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
 {
+       TALLOC_CTX *frame = talloc_stackframe();
        ADS_STATUS status;
        struct ads_service_principal p;
+       struct cli_credentials *creds;
 
        status = ads_generate_service_principal(ads, &p);
        if (!ADS_ERR_OK(status)) {
-               return status;
+               goto done;
        }
 
-       status = ads_sasl_gssapi_do_bind(ads, p.name);
-       if (ADS_ERR_OK(status)) {
-               ads_free_service_principal(&p);
-               return status;
+       creds = ads_sasl_creds_init(frame,
+                                   ads->auth.user_name,
+                                   ads->auth.password,
+                                   ads->auth.realm,
+                                   ads->auth.realm,
+                                   CRED_MUST_USE_KERBEROS);
+       if (creds == NULL) {
+               status = ADS_ERROR_SYSTEM(ENOMEM);
+               goto done;
        }
 
-       DEBUG(10,("ads_sasl_gssapi_do_bind failed with: %s, "
-                 "calling kinit\n", ads_errstr(status)));
-
-       status = ADS_ERROR_KRB5(ads_kinit_password(ads));
-
-       if (ADS_ERR_OK(status)) {
-               status = ads_sasl_gssapi_do_bind(ads, p.name);
-       }
+       status = ads_sasl_spnego_gensec_bind(ads,
+                                            "GSSAPI",
+                                            creds,
+                                            p.service,
+                                            p.hostname,
+                                            data_blob_null);
 
+done:
        ads_free_service_principal(&p);
-
+       TALLOC_FREE(frame);
        return status;
 }
 
@@ -1013,6 +700,7 @@ ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
        ADS_STATUS status;
        int i, j;
        LDAPMessage *res;
+       struct ads_saslwrap *wrap = &ads->ldap_wrap_data;
 
        /* get a list of supported SASL mechanisms */
        status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
@@ -1021,11 +709,11 @@ ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
        values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
 
        if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
-               ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
+               wrap->wrap_type = ADS_SASLWRAP_TYPE_SEAL;
        } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
-               ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
+               wrap->wrap_type = ADS_SASLWRAP_TYPE_SIGN;
        } else {
-               ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
+               wrap->wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
        }
 
        /* try our supported mechanisms in order */
@@ -1038,11 +726,11 @@ 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)
+                                   wrap->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;
+                                       wrap->wrap_type = ADS_SASLWRAP_TYPE_SIGN;
                                        goto retry;
                                }
                                ldap_value_free(values);