Exemple #1
0
/* 
   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, CONST_DISCARD(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 = gen_negTokenTarg(spnego_mechs, unwrapped);
	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) {
		status = ADS_ERROR(rc);
		goto failed;
	}

	if (scred) {
		wrapped = data_blob_const(scred->bv_val, scred->bv_len);
	} else {
		wrapped = data_blob_null;
	}

	ok = spnego_parse_auth_response(wrapped, NT_STATUS_OK,
					OID_KERBEROS5_OLD,
					&unwrapped);
	if (scred) ber_bvfree(scred);
	if (!ok) {
		status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
		goto failed;
	}

	input_token.value	= unwrapped.data;
	input_token.length	= unwrapped.length;

	/* 
	 * As we asked for mutal authentication
	 * we need to pass the servers response
	 * to gssapi
	 */
	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);
	data_blob_free(&unwrapped);
	if (gss_rc) {
		status = ADS_ERROR_GSS(gss_rc, minor_status);
		goto failed;
	}

	gss_release_buffer(&minor_status, &output_token);

	/*
	 * If we the sign and seal options
	 * doesn't match after getting the response
	 * from the server, we don't want to use the connection
	 */
	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... */
		status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
		goto failed;
	}

	if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
		uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;

		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;
	}

	status = ADS_SUCCESS;

failed:
	if (context_handle != GSS_C_NO_CONTEXT)
		gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
	return status;
}
Exemple #2
0
/* 
   perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
   we fit on one socket??)
*/
static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
{
	DATA_BLOB msg1 = data_blob(NULL, 0);
	DATA_BLOB blob = data_blob(NULL, 0);
	DATA_BLOB blob_in = data_blob(NULL, 0);
	DATA_BLOB blob_out = data_blob(NULL, 0);
	struct berval cred, *scred = NULL;
	int rc;
	NTSTATUS nt_status;
	int turn = 1;

	struct ntlmssp_state *ntlmssp_state;

	if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
		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))) {
		return ADS_ERROR_NT(nt_status);
	}
	if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_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))) {
		return ADS_ERROR_NT(nt_status);
	}

	blob_in = data_blob(NULL, 0);

	do {
		nt_status = ntlmssp_update(ntlmssp_state, 
					   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) {
				/* and wrap it in a SPNEGO wrapper */
				msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
			} else {
				/* wrap it in SPNEGO */
				msg1 = spnego_gen_auth(blob_out);
			}

			data_blob_free(&blob_out);

			cred.bv_val = (char *)msg1.data;
			cred.bv_len = msg1.length;
			scred = NULL;
			rc = ldap_sasl_bind_s(ads->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);
				}

				ntlmssp_end(&ntlmssp_state);
				return ADS_ERROR(rc);
			}
			if (scred) {
				blob = data_blob(scred->bv_val, scred->bv_len);
				ber_bvfree(scred);
			} else {
				blob = data_blob(NULL, 0);
			}

		} else {

			ntlmssp_end(&ntlmssp_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, 0);
			/* the server might give us back two challenges */
			if (!spnego_parse_challenge(blob, &blob_in, 
						    &tmp_blob)) {

				ntlmssp_end(&ntlmssp_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, 
							&blob_in)) {

				ntlmssp_end(&ntlmssp_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));
	
	/* we have a reference conter on ntlmssp_state, if we are signing
	   then the state will be kept by the signing engine */

	ntlmssp_end(&ntlmssp_state);

	return ADS_ERROR(rc);
}
Exemple #3
0
/* 
   perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
   we fit on one socket??)
*/
static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
{
	DATA_BLOB msg1 = data_blob_null;
	DATA_BLOB blob = data_blob_null;
	DATA_BLOB blob_in = data_blob_null;
	DATA_BLOB blob_out = data_blob_null;
	struct berval cred, *scred = NULL;
	int rc;
	NTSTATUS nt_status;
	ADS_STATUS status;
	int turn = 1;
	uint32 features = 0;

	struct ntlmssp_state *ntlmssp_state;

	if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
		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))) {
		return ADS_ERROR_NT(nt_status);
	}
	if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_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))) {
		return ADS_ERROR_NT(nt_status);
	}

	switch (ads->ldap.wrap_type) {
	case ADS_SASLWRAP_TYPE_SEAL:
		features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
		break;
	case ADS_SASLWRAP_TYPE_SIGN:
		if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
			features = NTLMSSP_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;
			ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
		}
		break;
	case ADS_SASLWRAP_TYPE_PLAIN:
		break;
	}

	ntlmssp_want_feature(ntlmssp_state, features);

	blob_in = data_blob_null;

	do {
		nt_status = ntlmssp_update(ntlmssp_state, 
					   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) {
				/* and wrap it in a SPNEGO wrapper */
				msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
			} else {
				/* wrap it in SPNEGO */
				msg1 = spnego_gen_auth(blob_out);
			}

			data_blob_free(&blob_out);

			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);
				}

				ntlmssp_end(&ntlmssp_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 {

			ntlmssp_end(&ntlmssp_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(blob, &blob_in, 
						    &tmp_blob)) {

				ntlmssp_end(&ntlmssp_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, 
							&blob_in)) {

				ntlmssp_end(&ntlmssp_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));
	
	/* 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;
		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);
		if (!ADS_ERR_OK(status)) {
			DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
				ads_errstr(status)));
			ntlmssp_end(&ntlmssp_state);
			return status;
		}
	} else {
		ntlmssp_end(&ntlmssp_state);
	}

	return ADS_ERROR(rc);
}
Exemple #4
0
/* 
   perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
   we fit on one socket??)
*/
static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
{
	DATA_BLOB msg1 = data_blob_null;
	DATA_BLOB blob = data_blob_null;
	DATA_BLOB blob_in = data_blob_null;
	DATA_BLOB blob_out = data_blob_null;
	struct berval cred, *scred = NULL;
	int rc;
	NTSTATUS nt_status;
	ADS_STATUS status;
	int turn = 1;

	struct auth_generic_state *auth_generic_state;

	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);
	}

	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 need to use seal here too
			 */
			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;
	case ADS_SASLWRAP_TYPE_PLAIN:
		break;
	}

	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 = 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 = spnego_gen_negTokenInit(talloc_tos(),
						OIDs_ntlm, &blob_out, NULL);
			} else {
				/* wrap it in SPNEGO */
				msg1 = spnego_gen_auth(talloc_tos(), blob_out);
			}

			data_blob_free(&blob_out);

			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);
}