/* perform a LDAP/SASL/SPNEGO/{NTLMSSP,KRB5} bind (just how many layers can we fit on one socket??) */ static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads, const char *sasl, enum credentials_use_kerberos krb5_state, const char *target_service, const char *target_hostname, const DATA_BLOB server_blob) { DATA_BLOB blob_in = data_blob_null; DATA_BLOB blob_out = data_blob_null; int rc; NTSTATUS nt_status; ADS_STATUS status; struct auth_generic_state *auth_generic_state; bool use_spnego_principal = lp_client_use_spnego_principal(); const char *sasl_list[] = { sasl, NULL }; 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))) { return ADS_ERROR_NT(nt_status); } if (server_blob.length == 0) { use_spnego_principal = false; } 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, target_service); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } } if (target_hostname != NULL) { nt_status = gensec_set_target_hostname( auth_generic_state->gensec_security, target_hostname); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } } if (target_service != NULL && target_hostname != NULL) { use_spnego_principal = false; } 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); break; case ADS_SASLWRAP_TYPE_SIGN: if (ads->auth.flags & ADS_AUTH_SASL_FORCE) { gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); } else { /* * windows servers are broken with sign only, * so we let the NTLMSSP backend to seal here, * via GENSEC_FEATURE_LDAP_STYLE. */ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_LDAP_STYLE); } break; case ADS_SASLWRAP_TYPE_PLAIN: break; } nt_status = auth_generic_client_start_by_sasl(auth_generic_state, sasl_list); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } rc = LDAP_SASL_BIND_IN_PROGRESS; nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; if (use_spnego_principal) { blob_in = data_blob_dup_talloc(talloc_tos(), server_blob); if (blob_in.length == 0) { TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } } else { blob_in = data_blob_null; } blob_out = data_blob_null; while (true) { struct berval cred, *scred = NULL; nt_status = gensec_update(auth_generic_state->gensec_security, talloc_tos(), 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)) { TALLOC_FREE(auth_generic_state); data_blob_free(&blob_out); return ADS_ERROR_NT(nt_status); } if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_out.length == 0) { break; } cred.bv_val = (char *)blob_out.data; cred.bv_len = blob_out.length; scred = NULL; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, sasl, &cred, NULL, NULL, &scred); data_blob_free(&blob_out); if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) { if (scred) { ber_bvfree(scred); } TALLOC_FREE(auth_generic_state); return ADS_ERROR(rc); } if (scred) { blob_in = data_blob_talloc(talloc_tos(), scred->bv_val, scred->bv_len); if (blob_in.length != scred->bv_len) { ber_bvfree(scred); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } ber_bvfree(scred); } else { blob_in = data_blob_null; } if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_in.length == 0) { break; } } data_blob_free(&blob_in); data_blob_free(&blob_out); if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SEAL) { bool ok; 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 ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE); } 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); } } else if (wrap->wrap_type >= ADS_SASLWRAP_TYPE_SIGN) { bool ok; 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); } } 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; } 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); 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); return ADS_ERROR(rc); }
/* perform a LDAP/SASL/SPNEGO/{NTLMSSP,KRB5} bind (just how many layers can we fit on one socket??) */ static ADS_STATUS ads_sasl_spnego_gensec_bind(ADS_STRUCT *ads, const char *sasl, enum credentials_use_kerberos krb5_state, const char *target_service, const char *target_hostname, const DATA_BLOB server_blob) { DATA_BLOB blob_in = data_blob_null; DATA_BLOB blob_out = data_blob_null; int rc; NTSTATUS nt_status; ADS_STATUS status; struct auth_generic_state *auth_generic_state; bool use_spnego_principal = lp_client_use_spnego_principal(); const char *sasl_list[] = { sasl, NULL }; 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))) { return ADS_ERROR_NT(nt_status); } if (server_blob.length == 0) { use_spnego_principal = false; } 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, target_service); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } } if (target_hostname != NULL) { nt_status = gensec_set_target_hostname( auth_generic_state->gensec_security, target_hostname); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } } if (target_service != NULL && target_hostname != NULL) { use_spnego_principal = false; } switch (ads->ldap.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); break; case ADS_SASLWRAP_TYPE_SIGN: if (ads->auth.flags & ADS_AUTH_SASL_FORCE) { gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); } else { /* * windows servers are broken with sign only, * so we let the NTLMSSP backend to seal here, * via GENSEC_FEATURE_LDAP_STYLE. */ gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SIGN); gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_LDAP_STYLE); } break; case ADS_SASLWRAP_TYPE_PLAIN: break; } nt_status = auth_generic_client_start_by_sasl(auth_generic_state, sasl_list); if (!NT_STATUS_IS_OK(nt_status)) { return ADS_ERROR_NT(nt_status); } rc = LDAP_SASL_BIND_IN_PROGRESS; nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; if (use_spnego_principal) { blob_in = data_blob_dup_talloc(talloc_tos(), server_blob); if (blob_in.length == 0) { TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } } else { blob_in = data_blob_null; } blob_out = data_blob_null; while (true) { struct berval cred, *scred = NULL; nt_status = gensec_update(auth_generic_state->gensec_security, talloc_tos(), 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)) { TALLOC_FREE(auth_generic_state); data_blob_free(&blob_out); return ADS_ERROR_NT(nt_status); } if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_out.length == 0) { break; } cred.bv_val = (char *)blob_out.data; cred.bv_len = blob_out.length; scred = NULL; rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, sasl, &cred, NULL, NULL, &scred); data_blob_free(&blob_out); if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) { if (scred) { ber_bvfree(scred); } TALLOC_FREE(auth_generic_state); return ADS_ERROR(rc); } if (scred) { blob_in = data_blob_talloc(talloc_tos(), scred->bv_val, scred->bv_len); if (blob_in.length != scred->bv_len) { ber_bvfree(scred); TALLOC_FREE(auth_generic_state); return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); } ber_bvfree(scred); } else { blob_in = data_blob_null; } if (NT_STATUS_IS_OK(nt_status) && rc == 0 && blob_in.length == 0) { break; } } 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); 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))); 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); }