+ cred.bv_val = (char *)msg1.data;
+ cred.bv_len = msg1.length;
+ scred = NULL;
+ rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
+ data_blob_free(&msg1);
+ 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 = data_blob(scred->bv_val, scred->bv_len);
+ ber_bvfree(scred);
+ } else {
+ blob = data_blob_null;
+ }
+
+ } else {
+
+ TALLOC_FREE(auth_generic_state);
+ data_blob_free(&blob_out);
+ return ADS_ERROR_NT(nt_status);
+ }
+
+ if ((turn == 1) &&
+ (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(talloc_tos(), blob, &blob_in,
+ &tmp_blob)) {
+
+ 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(talloc_tos(), blob, nt_status, OID_NTLMSSP,
+ &blob_in)) {
+
+ 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);
+ }
+ }
+ data_blob_free(&blob);
+ data_blob_free(&blob_out);
+ turn++;
+ } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
+
+ if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
+ 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, 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);
+}
+
+#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;
+ ADS_STATUS status;
+ int gss_rc;
+ uint32 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;
+
+ if (conf_req_flag && conf_state == 0) {
+ return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+
+ if ((ads->ldap.out.size - 4) < wrapped.length) {
+ return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+ }
+
+ /* 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);
+
+ 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 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);
+ }
+
+ /* 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 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;
+}
+
+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
+};
+
+/*
+ perform a LDAP/SASL/SPNEGO/GSSKRB5 bind
+*/
+static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
+{
+ ADS_STATUS status;
+ bool ok;
+ uint32 minor_status;
+ int gss_rc, rc;
+ gss_OID_desc krb5_mech_type =
+ {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};
+ gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
+ gss_buffer_desc input_token, output_token;
+ uint32 req_flags, ret_flags;
+ uint32 req_tmp, ret_tmp;
+ DATA_BLOB unwrapped;
+ DATA_BLOB wrapped;
+ struct berval cred, *scred = NULL;
+
+ 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 explicit ask for the krb5 mech_type */
+ gss_rc = gss_init_sec_context(&minor_status,
+ GSS_C_NO_CREDENTIAL,
+ &context_handle,
+ serv_name,
+ mech_type,
+ req_flags,
+ 0,
+ NULL,
+ &input_token,
+ &actual_mech_type,
+ &output_token,
+ &ret_flags,
+ NULL);
+ if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
+ status = ADS_ERROR_GSS(gss_rc, minor_status);
+ goto failed;
+ }
+
+ /*
+ * As some gssapi krb5 mech implementations
+ * automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG
+ * to req_flags internaly, it's not possible to
+ * use plain or signing only connection via
+ * the gssapi interface.
+ *
+ * Because of this we need to check it the ret_flags
+ * has more flags as req_flags and correct the value
+ * of ads->ldap.wrap_type.
+ *
+ * I ads->auth.flags has ADS_AUTH_SASL_FORCE
+ * we need to give an error.
+ */
+ req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
+ ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
+
+ if (req_tmp == ret_tmp) {
+ /* everythings fine... */
+
+ } else if (req_flags & GSS_C_CONF_FLAG) {
+ /*
+ * here we wanted sealing but didn't got it
+ * from the gssapi library
+ */
+ status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ goto failed;
+
+ } else if ((req_flags & GSS_C_INTEG_FLAG) &&
+ !(ret_flags & GSS_C_INTEG_FLAG)) {
+ /*
+ * here we wanted siging but didn't got it
+ * from the gssapi library
+ */
+ status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ goto failed;
+
+ } else if (ret_flags & GSS_C_CONF_FLAG) {
+ /*
+ * here we didn't want sealing
+ * but the gssapi library forces it
+ * so correct the needed wrap_type if
+ * the caller didn't forced siging only
+ */
+ if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
+ status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ goto failed;
+ }
+
+ ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
+ req_flags = ret_flags;
+
+ } else if (ret_flags & GSS_C_INTEG_FLAG) {
+ /*
+ * here we didn't want signing
+ * but the gssapi library forces it
+ * so correct the needed wrap_type if
+ * the caller didn't forced plain
+ */
+ if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
+ status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+ goto failed;
+ }
+
+ ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
+ req_flags = ret_flags;
+ } else {
+ /*
+ * This could (should?) not happen
+ */
+ status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+ goto failed;
+
+ }
+
+ /* and wrap that in a shiny SPNEGO wrapper */
+ unwrapped = data_blob_const(output_token.value, output_token.length);
+ 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);
+ goto failed;
+ }
+
+ cred.bv_val = (char *)wrapped.data;
+ cred.bv_len = wrapped.length;
+
+ rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL,
+ &scred);
+ data_blob_free(&wrapped);
+ if (rc != LDAP_SUCCESS) {