From: Stefan Metzmacher Date: Tue, 1 Mar 2016 20:25:13 +0000 (+0100) Subject: s3-libads: Use gensec GSSAPI for ads_sasl_gssapi_bind() X-Git-Url: http://git.samba.org/?p=metze%2Fsamba%2Fwip.git;a=commitdiff_plain;h=700e66209fd6da807af0e8160db98bdfd71184ba s3-libads: Use gensec GSSAPI for ads_sasl_gssapi_bind() Signed-off-by: Stefan Metzmacher --- diff --git a/source3/libads/sasl.c b/source3/libads/sasl.c index 010a2538206d..98d683788be5 100644 --- a/source3/libads/sasl.c +++ b/source3/libads/sasl.c @@ -351,149 +351,6 @@ static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads, 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 = smb_krb5_init_context_common(&kctx); - if (kerr) { - DBG_ERR("kerberos init context failed (%s)\n", - error_message(kerr)); - return ADS_ERROR_KRB5(kerr); - } - - kerr = krb5_cc_resolve(kctx, ads->auth.ccache_name, &kccache); - if (kerr) { - status = ADS_ERROR_KRB5(kerr); - goto done; - } - - maj = smb_gss_krb5_import_cred(&min, kctx, kccache, NULL, NULL, cred); - if (maj != GSS_S_COMPLETE) { - status = ADS_ERROR_GSS(maj, min); - goto done; - } - - 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(struct ads_saslwrap *wrap, uint8_t *buf, uint32_t len) -{ - gss_ctx_id_t context_handle = (gss_ctx_id_t)wrap->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 = (wrap->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; - - if (conf_req_flag && conf_state == 0) { - return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED); - } - - if ((wrap->out.size - 4) < wrapped.length) { - return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR); - } - - /* copy the wrapped blob to the right location */ - memcpy(wrap->out.buf + 4, wrapped.value, wrapped.length); - - /* set how many bytes must be written to the underlying socket */ - wrap->out.left = 4 + wrapped.length; - - gss_release_buffer(&minor_status, &wrapped); - - return ADS_SUCCESS; -} - -static ADS_STATUS ads_sasl_gssapi_unwrap(struct ads_saslwrap *wrap) -{ - gss_ctx_id_t context_handle = (gss_ctx_id_t)wrap->wrap_private_data; - ADS_STATUS status; - int gss_rc; - uint32_t minor_status; - gss_buffer_desc unwrapped, wrapped; - int conf_state; - - wrapped.value = wrap->in.buf + 4; - wrapped.length = wrap->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 (wrap->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); - } - - /* copy the wrapped blob to the right location */ - memcpy(wrap->in.buf + 4, unwrapped.value, unwrapped.length); - - /* set how many bytes must be written to the underlying socket */ - wrap->in.left = unwrapped.length; - wrap->in.ofs = 4; - - gss_release_buffer(&minor_status, &unwrapped); - - return ADS_SUCCESS; -} - -static void ads_sasl_gssapi_disconnect(struct ads_saslwrap *wrap) -{ - gss_ctx_id_t context_handle = (gss_ctx_id_t)wrap->wrap_private_data; - uint32_t minor_status; - - gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER); - - wrap->wrap_ops = NULL; - wrap->wrap_private_data = NULL; -} - -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; @@ -810,220 +667,6 @@ done: } #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; - struct ads_saslwrap *wrap = &ads->ldap_wrap_data; - - 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 & wrap->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", - wrap->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,wrap->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 (wrap->wrap_type > ADS_SASLWRAP_TYPE_PLAIN) { - gss_rc = gss_wrap_size_limit(&minor_status, context_handle, - (wrap->wrap_type == ADS_SASLWRAP_TYPE_SEAL), - GSS_C_QOP_DEFAULT, - max_msg_size, &wrap->out.max_unwrapped); - if (gss_rc) { - status = ADS_ERROR_GSS(gss_rc, minor_status); - goto failed; - } - - wrap->out.sig_size = max_msg_size - wrap->out.max_unwrapped; - wrap->in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */ - wrap->in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED; - status = ads_setup_sasl_wrapping(wrap->wrap_private_data, ads->ldap.ld, - &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) { @@ -1037,20 +680,30 @@ static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads) if (ads->auth.password == NULL || ads->auth.password[0] == '\0') { - status = ads_sasl_gssapi_do_bind(ads, p.name); + status = ads_sasl_spnego_gensec_bind(ads, + "GSSAPI", + CRED_MUST_USE_KERBEROS, + p.service, + p.hostname, + data_blob_null); if (ADS_ERR_OK(status)) { ads_free_service_principal(&p); return status; } - DEBUG(10,("ads_sasl_gssapi_do_bind failed with: %s, " + 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_gssapi_do_bind(ads, p.name); + status = ads_sasl_spnego_gensec_bind(ads, + "GSSAPI", + CRED_MUST_USE_KERBEROS, + p.service, + p.hostname, + data_blob_null); } ads_free_service_principal(&p);