Exemple #1
0
void aes_ccm_128_init(struct aes_ccm_128_context *ctx,
		      const uint8_t K[AES_BLOCK_SIZE],
		      const uint8_t N[AES_CCM_128_NONCE_SIZE],
		      size_t a_total, size_t m_total)
{
	uint8_t B_0[AES_BLOCK_SIZE];

	ZERO_STRUCTP(ctx);

	AES_set_encrypt_key(K, 128, &ctx->aes_key);
	memcpy(ctx->nonce, N, AES_CCM_128_NONCE_SIZE);
	ctx->a_remain = a_total;
	ctx->m_remain = m_total;

	/*
	 * prepare B_0
	 */
	B_0[0]  = L_;
	B_0[0] += 8 * M_;
	if (a_total > 0) {
		B_0[0] += 64;
	}
	memcpy(&B_0[1], ctx->nonce, AES_CCM_128_NONCE_SIZE);
	RSIVAL(B_0, (AES_BLOCK_SIZE - AES_CCM_128_L), m_total);

	/*
	 * prepare X_1
	 */
	AES_encrypt(B_0, ctx->X_i, &ctx->aes_key);

	/*
	 * prepare B_1
	 */
	if (a_total >= UINT32_MAX) {
		RSSVAL(ctx->B_i, 0, 0xFFFF);
		RSBVAL(ctx->B_i, 2, (uint64_t)a_total);
		ctx->B_i_ofs = 10;
	} else if (a_total >= 0xFF00) {
		RSSVAL(ctx->B_i, 0, 0xFFFE);
		RSIVAL(ctx->B_i, 2, a_total);
		ctx->B_i_ofs = 6;
	} else if (a_total > 0) {
		RSSVAL(ctx->B_i, 0, a_total);
		ctx->B_i_ofs = 2;
	}

	ctx->S_i_ofs = AES_BLOCK_SIZE;
}
Exemple #2
0
bool prs_dcerpc_status(const char *name, prs_struct *ps, int depth, NTSTATUS *status)
{
	char *q = prs_mem_get(ps, sizeof(uint32));
	if (q == NULL)
		return False;

	if (UNMARSHALLING(ps)) {
		if (ps->bigendian_data)
			*status = NT_STATUS(RIVAL(q,0));
		else
			*status = NT_STATUS(IVAL(q,0));
	} else {
		if (ps->bigendian_data)
			RSIVAL(q,0,NT_STATUS_V(*status));
		else
			SIVAL(q,0,NT_STATUS_V(*status));
	}

	DEBUGADD(5,("%s%04x %s: %s\n", tab_depth(5,depth), ps->data_offset, name,
		 dcerpc_errstr(talloc_tos(), NT_STATUS_V(*status))));

	ps->data_offset += sizeof(uint32);

	return True;
}
Exemple #3
0
 int buffer_add_uint32(xrtp_buffer_t * buf, uint32 dword){

    if(buf->len_data + sizeof(uint32) > buf->len)
    {
       buffer_log(("buffer_add_uint32: buffer.len(%d) full! Can't add (%d)", buf->len, dword));
       return OS_EREFUSE;
    }

    if(buf->byte_order == BIGEND_ORDER)
    {
       buffer_log(("buffer_add_uint32: Add BE(%u) to buf[%d]@%d\n", dword, buf->pos, (int)(buf->data)));
       RSIVAL(buf->data, buf->len_data, dword);
    }
    else
    {
       buffer_log(("buffer_add_uint32: Add LE(%u) to buf[%d]@%d\n", dword, buf->pos, (int)(buf->data)));
       SIVAL(buf->data, buf->len_data, dword);
    }
       

    buf->len_data += sizeof(uint32);
    buf->pos += sizeof(uint32);

    return OS_OK;
 }
Exemple #4
0
/*
  sign a packet
*/
NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security, 
			      TALLOC_CTX *mem_ctx, 
			      const uint8_t *data, size_t length, 
			      const uint8_t *whole_pdu, size_t pdu_length, 
			      DATA_BLOB *sig)
{
	struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);

	uint8_t digest_final[16];
	uint8_t seq_num[8];
	static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;

	RSIVAL(seq_num, 0, state->seq_num);
	SIVAL(seq_num, 4, state->initiator?0x80:0);

	schannel_digest(state->creds->session_key, 
			netsec_sig, NULL, 
			data, length, digest_final);

	netsec_deal_with_seq_num(state, digest_final, seq_num);

	(*sig) = data_blob_talloc(mem_ctx, NULL, 32);

	memcpy(sig->data, netsec_sig, 8);
	memcpy(sig->data+8, seq_num, 8);
	memcpy(sig->data+16, digest_final, 8);
	memset(sig->data+24, 0, 8);

	dump_data_pw("signature:", sig->data+ 0, 8);
	dump_data_pw("seq_num  :", sig->data+ 8, 8);
	dump_data_pw("digest   :", sig->data+16, 8);
	dump_data_pw("confound :", sig->data+24, 8);

	return NT_STATUS_OK;
}
Exemple #5
0
BOOL prs_ntstatus(const char *name, prs_struct *ps, int depth, NTSTATUS *status)
{
	char *q = prs_mem_get(ps, sizeof(uint32));
	if (q == NULL)
		return False;

	if (UNMARSHALLING(ps)) {
		if (ps->bigendian_data)
			*status = NT_STATUS(RIVAL(q,0));
		else
			*status = NT_STATUS(IVAL(q,0));
	} else {
		if (ps->bigendian_data)
			RSIVAL(q,0,NT_STATUS_V(*status));
		else
			SIVAL(q,0,NT_STATUS_V(*status));
	}

	DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth), ps->data_offset, name, 
		 get_nt_error_msg(*status)));

	ps->data_offset += sizeof(uint32);

	return True;
}
Exemple #6
0
BOOL prs_werror(const char *name, prs_struct *ps, int depth, WERROR *status)
{
	char *q = prs_mem_get(ps, sizeof(uint32));
	if (q == NULL)
		return False;

	if (UNMARSHALLING(ps)) {
		if (ps->bigendian_data)
			*status = W_ERROR(RIVAL(q,0));
		else
			*status = W_ERROR(IVAL(q,0));
	} else {
		if (ps->bigendian_data)
			RSIVAL(q,0,W_ERROR_V(*status));
		else
			SIVAL(q,0,W_ERROR_V(*status));
	}

	DEBUG(5,("%s%04x %s: %s\n", tab_depth(depth), ps->data_offset, name, 
		 dos_errstr(*status)));

	ps->data_offset += sizeof(uint32);

	return True;
}
Exemple #7
0
/*
 * These functions are for use in the deprecated
 * gensec_socket code (public because SPNEGO must
 * use them for recursion)
 */
_PUBLIC_ NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
			     TALLOC_CTX *mem_ctx,
			     const DATA_BLOB *in,
			     DATA_BLOB *out,
			     size_t *len_processed)
{
	if (!gensec_security->ops->wrap_packets) {
		NTSTATUS nt_status;
		size_t max_input_size;
		DATA_BLOB unwrapped, wrapped;
		max_input_size = gensec_max_input_size(gensec_security);
		unwrapped = data_blob_const(in->data, MIN(max_input_size, (size_t)in->length));

		nt_status = gensec_wrap(gensec_security,
					mem_ctx,
					&unwrapped, &wrapped);
		if (!NT_STATUS_IS_OK(nt_status)) {
			return nt_status;
		}

		*out = data_blob_talloc(mem_ctx, NULL, 4);
		if (!out->data) {
			return NT_STATUS_NO_MEMORY;
		}
		RSIVAL(out->data, 0, wrapped.length);

		if (!data_blob_append(mem_ctx, out, wrapped.data, wrapped.length)) {
			return NT_STATUS_NO_MEMORY;
		}
		*len_processed = unwrapped.length;
		return NT_STATUS_OK;
	}
	return gensec_security->ops->wrap_packets(gensec_security, mem_ctx, in, out,
						  len_processed);
}
/* this function is due to be replaced */
void initrpcreply(char *inbuf, char *q)
{
	uint32 callid;

	SCVAL(q, 0, 5); q++; /* RPC version 5 */
	SCVAL(q, 0, 0); q++; /* minor version 0 */
	SCVAL(q, 0, 2); q++; /* RPC response packet */
	SCVAL(q, 0, 3); q++; /* first frag + last frag */
	RSIVAL(q, 0, 0x10000000); q += 4; /* packed data representation */
	RSSVAL(q, 0, 0); q += 2; /* fragment length, fill in later */
	SSVAL(q, 0, 0); q += 2; /* authentication length */
	callid = RIVAL(inbuf, 12);
	RSIVAL(q, 0, callid); q += 4; /* call identifier - match incoming RPC */
	SIVAL(q, 0, 0x18); q += 4; /* allocation hint (no idea) */
	SSVAL(q, 0, 0); q += 2; /* presentation context identifier */
	SCVAL(q, 0, 0); q++; /* cancel count */
	SCVAL(q, 0, 0); q++; /* reserved */
}
Exemple #9
0
static TDB_DATA smbXsrv_session_local_id_to_key(uint32_t id,
						uint8_t *key_buf)
{
	TDB_DATA key;

	RSIVAL(key_buf, 0, id);

	key = make_tdb_data(key_buf, SMBXSRV_SESSION_LOCAL_TDB_KEY_SIZE);

	return key;
}
Exemple #10
0
static TDB_DATA smbXsrv_tcon_global_id_to_key(uint32_t id,
					      uint8_t *key_buf)
{
	TDB_DATA key;

	RSIVAL(key_buf, 0, id);

	key = make_tdb_data(key_buf, SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE);

	return key;
}
Exemple #11
0
static void aes_ccm_128_S_i(struct aes_ccm_128_context *ctx,
			    uint8_t S_i[AES_BLOCK_SIZE],
			    size_t i)
{
	uint8_t A_i[AES_BLOCK_SIZE];

	A_i[0]  = L_;
	memcpy(&A_i[1], ctx->nonce, AES_CCM_128_NONCE_SIZE);
	RSIVAL(A_i, (AES_BLOCK_SIZE - AES_CCM_128_L), i);

	AES_encrypt(A_i, S_i, &ctx->aes_key);
}
Exemple #12
0
/*
  unseal a packet
*/
NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security, 
				TALLOC_CTX *mem_ctx, 
				uint8_t *data, size_t length, 
				const uint8_t *whole_pdu, size_t pdu_length, 
				const DATA_BLOB *sig)
{
	struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
	
	uint8_t digest_final[16];
	uint8_t confounder[8];
	uint8_t seq_num[8];
	uint8_t sealing_key[16];
	static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;

	if (sig->length != 32) {
		return NT_STATUS_ACCESS_DENIED;
	}

	memcpy(confounder, sig->data+24, 8);

	RSIVAL(seq_num, 0, state->seq_num);
	SIVAL(seq_num, 4, state->initiator?0:0x80);

	netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
	arcfour_crypt(confounder, sealing_key, 8);
	arcfour_crypt(data, sealing_key, length);

	schannel_digest(state->creds->session_key, 
			netsec_sig, confounder, 
			data, length, digest_final);

	if (memcmp(digest_final, sig->data+16, 8) != 0) {
		dump_data_pw("calc digest:", digest_final, 8);
		dump_data_pw("wire digest:", sig->data+16, 8);
		return NT_STATUS_ACCESS_DENIED;
	}

	netsec_deal_with_seq_num(state, digest_final, seq_num);

	if (memcmp(seq_num, sig->data+8, 8) != 0) {
		dump_data_pw("calc seq num:", seq_num, 8);
		dump_data_pw("wire seq num:", sig->data+8, 8);
		return NT_STATUS_ACCESS_DENIED;
	}

	return NT_STATUS_OK;
}
Exemple #13
0
/*
  receive a full packet on a KDC connection
*/
static NTSTATUS kdc_tcp_recv(void *private_data, DATA_BLOB blob)
{
	struct kdc_tcp_connection *kdcconn = talloc_get_type(private_data,
							     struct kdc_tcp_connection);
	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
	TALLOC_CTX *tmp_ctx = talloc_new(kdcconn);
	int ret;
	DATA_BLOB input, reply;
	talloc_steal(tmp_ctx, blob.data);

	/* Call krb5 */
	input = data_blob_const(blob.data + 4, blob.length - 4); 

	ret = kdcconn->kdc_socket->process(kdcconn->kdc_socket->kdc,
					   tmp_ctx,
					   &input,
					   &reply,
					   kdcconn->remote_address,
					   kdcconn->local_address,
					   0 /* Not datagram */);
	if (!ret) {
		talloc_free(tmp_ctx);
		return NT_STATUS_INTERNAL_ERROR;
	}

	/* and now encode the reply */
	blob = data_blob_talloc(kdcconn, NULL, reply.length + 4);
	if (!blob.data) {
		talloc_free(tmp_ctx);
		return NT_STATUS_NO_MEMORY;
	}

	RSIVAL(blob.data, 0, reply.length);
	memcpy(blob.data + 4, reply.data, reply.length);	

	status = packet_send(kdcconn->packet, blob);
	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(tmp_ctx);
		return status;
	}

	/* the call isn't needed any more */
	talloc_free(tmp_ctx);
	return NT_STATUS_OK;
}
Exemple #14
0
/*
  check the signature on a packet
*/
NTSTATUS schannel_check_packet(struct gensec_security *gensec_security, 
			       TALLOC_CTX *mem_ctx, 
			       const uint8_t *data, size_t length, 
			       const uint8_t *whole_pdu, size_t pdu_length, 
			       const DATA_BLOB *sig)
{
	struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);

	uint8_t digest_final[16];
	uint8_t seq_num[8];
	static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;

	/* w2k sends just 24 bytes and skip the confounder */
	if (sig->length != 32 && sig->length != 24) {
		return NT_STATUS_ACCESS_DENIED;
	}

	RSIVAL(seq_num, 0, state->seq_num);
	SIVAL(seq_num, 4, state->initiator?0:0x80);

	dump_data_pw("seq_num:\n", seq_num, 8);
	dump_data_pw("sess_key:\n", state->creds->session_key, 16);

	schannel_digest(state->creds->session_key, 
			netsec_sig, NULL, 
			data, length, digest_final);

	netsec_deal_with_seq_num(state, digest_final, seq_num);

	if (memcmp(seq_num, sig->data+8, 8) != 0) {
		dump_data_pw("calc seq num:", seq_num, 8);
		dump_data_pw("wire seq num:", sig->data+8, 8);
		return NT_STATUS_ACCESS_DENIED;
	}

	if (memcmp(digest_final, sig->data+16, 8) != 0) {
		dump_data_pw("calc digest:", digest_final, 8);
		dump_data_pw("wire digest:", sig->data+16, 8);
		return NT_STATUS_ACCESS_DENIED;
	}

	return NT_STATUS_OK;
}
Exemple #15
0
BOOL prs_uint32s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint32 *data32s, int len)
{
	int i;
	char *q = prs_mem_get(ps, len * sizeof(uint32));
	if (q == NULL)
		return False;

	if (UNMARSHALLING(ps)) {
		if (ps->bigendian_data) {
			for (i = 0; i < len; i++)
				data32s[i] = RIVAL(q, 4*i);
		} else {
			for (i = 0; i < len; i++)
				data32s[i] = IVAL(q, 4*i);
		}
	} else {
		if (ps->bigendian_data) {
			for (i = 0; i < len; i++)
				RSIVAL(q, 4*i, data32s[i]);
		} else {
			for (i = 0; i < len; i++)
				SIVAL(q, 4*i, data32s[i]);
		}
	}

	DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
	if (charmode)
		print_asc(5, (unsigned char*)data32s, 4*len);
	else {
		for (i = 0; i < len; i++)
			DEBUG(5,("%08x ", data32s[i]));
	}
    DEBUG(5,("\n"));

	ps->data_offset += (len * sizeof(uint32));

	return True;
}
Exemple #16
0
BOOL prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32)
{
	char *q = prs_mem_get(ps, sizeof(uint32));
	if (q == NULL)
		return False;

	if (UNMARSHALLING(ps)) {
		if (ps->bigendian_data)
			*data32 = RIVAL(q,0);
		else
			*data32 = IVAL(q,0);
	} else {
		if (ps->bigendian_data)
			RSIVAL(q,0,*data32);
		else
			SIVAL(q,0,*data32);
	}

	DEBUG(5,("%s%04x %s: %08x\n", tab_depth(depth), ps->data_offset, name, *data32));

	ps->data_offset += sizeof(uint32);

	return True;
}
NTSTATUS netsec_outgoing_packet(struct schannel_state *state,
				TALLOC_CTX *mem_ctx,
				bool do_seal,
				uint8_t *data, size_t length,
				DATA_BLOB *sig)
{
	uint32_t min_sig_size = 0;
	uint32_t used_sig_size = 0;
	uint8_t header[8];
	uint8_t checksum[32];
	uint32_t checksum_length = sizeof(checksum_length);
	uint8_t _confounder[8];
	uint8_t *confounder = NULL;
	uint32_t confounder_ofs = 0;
	uint8_t seq_num[8];

	netsec_offset_and_sizes(state,
				do_seal,
				&min_sig_size,
				&used_sig_size,
				&checksum_length,
				&confounder_ofs);

	RSIVAL(seq_num, 0, state->seq_num);
	SIVAL(seq_num, 4, state->initiator?0x80:0);

	if (do_seal) {
		confounder = _confounder;
		generate_random_buffer(confounder, 8);
	} else {
		confounder = NULL;
	}

	netsec_do_sign(state, confounder,
		       data, length,
		       header, checksum);

	if (do_seal) {
		netsec_do_seal(state, seq_num,
			       confounder,
			       data, length,
			       true);
	}

	netsec_do_seq_num(state, checksum, checksum_length, seq_num);

	(*sig) = data_blob_talloc_zero(mem_ctx, used_sig_size);

	memcpy(sig->data, header, 8);
	memcpy(sig->data+8, seq_num, 8);
	memcpy(sig->data+16, checksum, checksum_length);

	if (confounder) {
		memcpy(sig->data+confounder_ofs, confounder, 8);
	}

	dump_data_pw("signature:", sig->data+ 0, 8);
	dump_data_pw("seq_num  :", sig->data+ 8, 8);
	dump_data_pw("digest   :", sig->data+16, checksum_length);
	dump_data_pw("confound :", sig->data+confounder_ofs, 8);

	return NT_STATUS_OK;
}
Exemple #18
0
static krb5_error_code build_kpasswd_request(uint16 pversion,
					   krb5_context context,
					   krb5_auth_context auth_context,
					   krb5_data *ap_req,
					   const char *princ,
					   const char *passwd,
					   bool use_tcp,
					   krb5_data *packet)
{
	krb5_error_code ret;
	krb5_data cipherpw;
	krb5_data encoded_setpw;
	krb5_replay_data replay;
	char *p, *msg_start;
	DATA_BLOB setpw;
	unsigned int msg_length;

	ret = krb5_auth_con_setflags(context,
				     auth_context,KRB5_AUTH_CONTEXT_DO_SEQUENCE);
	if (ret) {
		DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
			 error_message(ret)));
		return ret;
	}

	/* handle protocol differences in chpw and setpw */
	if (pversion  == KRB5_KPASSWD_VERS_CHANGEPW)
		setpw = data_blob(passwd, strlen(passwd));
	else if (pversion == KRB5_KPASSWD_VERS_SETPW ||
		 pversion == KRB5_KPASSWD_VERS_SETPW_ALT)
		setpw = encode_krb5_setpw(princ, passwd);
	else
		return EINVAL;

	if (setpw.data == NULL || setpw.length == 0) {
		return EINVAL;
	}

	encoded_setpw.data = (char *)setpw.data;
	encoded_setpw.length = setpw.length;

	ret = krb5_mk_priv(context, auth_context,
			   &encoded_setpw, &cipherpw, &replay);
	
	data_blob_free(&setpw); 	/*from 'encode_krb5_setpw(...)' */
	
	if (ret) {
		DEBUG(1,("krb5_mk_priv failed (%s)\n", error_message(ret)));
		return ret;
	}

	packet->data = (char *)SMB_MALLOC(ap_req->length + cipherpw.length + (use_tcp ? 10 : 6 ));
	if (!packet->data)
		return -1;



	/* see the RFC for details */

	msg_start = p = ((char *)packet->data) + (use_tcp ? 4 : 0);
	p += 2;
	RSSVAL(p, 0, pversion);
	p += 2;
	RSSVAL(p, 0, ap_req->length);
	p += 2;
	memcpy(p, ap_req->data, ap_req->length);
	p += ap_req->length;
	memcpy(p, cipherpw.data, cipherpw.length);
	p += cipherpw.length;
	packet->length = PTR_DIFF(p,packet->data);
	msg_length = PTR_DIFF(p,msg_start);

	if (use_tcp) {
		RSIVAL(packet->data, 0, msg_length);
	}
	RSSVAL(msg_start, 0, msg_length);
	
	free(cipherpw.data);    /* from 'krb5_mk_priv(...)' */

	return 0;
}
Exemple #19
0
/*******************************************************************
 Encode a blob of data using the netsec (schannel) alogrithm, also produceing
 a checksum over the original data.  We currently only support
 signing and sealing togeather - the signing-only code is close, but not
 quite compatible with what MS does.
 ********************************************************************/
void netsec_encode(struct netsec_auth_struct *a, int auth_flags, 
		   enum netsec_direction direction,
		   RPC_AUTH_NETSEC_CHK * verf,
		   char *data, size_t data_len)
{
	uchar digest_final[16];
	uchar confounder[8];
	uchar seq_num[8];
	static const uchar nullbytes[8];

	static const uchar netsec_seal_sig[8] = NETSEC_SEAL_SIGNATURE;
	static const uchar netsec_sign_sig[8] = NETSEC_SIGN_SIGNATURE;
	const uchar *netsec_sig = NULL;

	DEBUG(10,("SCHANNEL: netsec_encode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len));
	
	if (auth_flags & AUTH_PIPE_SEAL) {
		netsec_sig = netsec_seal_sig;
	} else if (auth_flags & AUTH_PIPE_SIGN) {
		netsec_sig = netsec_sign_sig;
	}

	/* fill the 'confounder' with random data */
	generate_random_buffer(confounder, sizeof(confounder), False);

	dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key));

	RSIVAL(seq_num, 0, a->seq_num);

	switch (direction) {
	case SENDER_IS_INITIATOR:
		SIVAL(seq_num, 4, 0x80);
		break;
	case SENDER_IS_ACCEPTOR:
		SIVAL(seq_num, 4, 0x0);
		break;
	}

	dump_data_pw("verf->seq_num:\n", seq_num, sizeof(verf->seq_num));

	init_rpc_auth_netsec_chk(verf, netsec_sig, nullbytes,
				 seq_num, confounder);
				
	/* produce a digest of the packet to prove it's legit (before we seal it) */
	netsec_digest(a, auth_flags, verf, data, data_len, digest_final);
	memcpy(verf->packet_digest, digest_final, sizeof(verf->packet_digest));

	if (auth_flags & AUTH_PIPE_SEAL) {
		uchar sealing_key[16];

		/* get the key to encode the data with */
		netsec_get_sealing_key(a, verf, sealing_key);

		/* encode the verification data */
		dump_data_pw("verf->confounder:\n", verf->confounder, sizeof(verf->confounder));
		SamOEMhash(verf->confounder, sealing_key, 8);

		dump_data_pw("verf->confounder_enc:\n", verf->confounder, sizeof(verf->confounder));
		
		/* encode the packet payload */
		dump_data_pw("data:\n", (const unsigned char *)data, data_len);
		SamOEMhash((unsigned char *)data, sealing_key, data_len);
		dump_data_pw("data_enc:\n", (const unsigned char *)data, data_len);
	}

	/* encode the sequence number (key based on packet digest) */
	/* needs to be done after the sealing, as the original version 
	   is used in the sealing stuff... */
	netsec_deal_with_seq_num(a, verf);

	return;
}
Exemple #20
0
BOOL netsec_decode(struct netsec_auth_struct *a, int auth_flags,
		   enum netsec_direction direction, 
		   RPC_AUTH_NETSEC_CHK * verf, char *data, size_t data_len)
{
	uchar digest_final[16];

	static const uchar netsec_seal_sig[8] = NETSEC_SEAL_SIGNATURE;
	static const uchar netsec_sign_sig[8] = NETSEC_SIGN_SIGNATURE;
	const uchar *netsec_sig = NULL;

	uchar seq_num[8];

	DEBUG(10,("SCHANNEL: netsec_encode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len));
	
	if (auth_flags & AUTH_PIPE_SEAL) {
		netsec_sig = netsec_seal_sig;
	} else if (auth_flags & AUTH_PIPE_SIGN) {
		netsec_sig = netsec_sign_sig;
	}

	/* Create the expected sequence number for comparison */
	RSIVAL(seq_num, 0, a->seq_num);

	switch (direction) {
	case SENDER_IS_INITIATOR:
		SIVAL(seq_num, 4, 0x80);
		break;
	case SENDER_IS_ACCEPTOR:
		SIVAL(seq_num, 4, 0x0);
		break;
	}

	DEBUG(10,("SCHANNEL: netsec_decode seq_num=%d data_len=%lu\n", a->seq_num, (unsigned long)data_len));
	dump_data_pw("a->sess_key:\n", a->sess_key, sizeof(a->sess_key));

	dump_data_pw("seq_num:\n", seq_num, sizeof(seq_num));

	/* extract the sequence number (key based on supplied packet digest) */
	/* needs to be done before the sealing, as the original version 
	   is used in the sealing stuff... */
	netsec_deal_with_seq_num(a, verf);

	if (memcmp(verf->seq_num, seq_num, sizeof(seq_num))) {
		/* don't even bother with the below if the sequence number is out */
		/* The sequence number is MD5'ed with a key based on the whole-packet
		   digest, as supplied by the client.  We check that it's a valid 
		   checksum after the decode, below
		*/
		DEBUG(2, ("netsec_decode: FAILED: packet sequence number:\n"));
		dump_data(2, (const char*)verf->seq_num, sizeof(verf->seq_num));
		DEBUG(2, ("should be:\n"));
		dump_data(2, (const char*)seq_num, sizeof(seq_num));

		return False;
	}

	if (memcmp(verf->sig, netsec_sig, sizeof(verf->sig))) {
		/* Validate that the other end sent the expected header */
		DEBUG(2, ("netsec_decode: FAILED: packet header:\n"));
		dump_data(2, (const char*)verf->sig, sizeof(verf->sig));
		DEBUG(2, ("should be:\n"));
		dump_data(2, (const char*)netsec_sig, sizeof(netsec_sig));
		return False;
	}

	if (auth_flags & AUTH_PIPE_SEAL) {
		uchar sealing_key[16];
		
		/* get the key to extract the data with */
		netsec_get_sealing_key(a, verf, sealing_key);

		/* extract the verification data */
		dump_data_pw("verf->confounder:\n", verf->confounder, 
			     sizeof(verf->confounder));
		SamOEMhash(verf->confounder, sealing_key, 8);

		dump_data_pw("verf->confounder_dec:\n", verf->confounder, 
			     sizeof(verf->confounder));
		
		/* extract the packet payload */
		dump_data_pw("data   :\n", (const unsigned char *)data, data_len);
		SamOEMhash((unsigned char *)data, sealing_key, data_len);
		dump_data_pw("datadec:\n", (const unsigned char *)data, data_len);	
	}

	/* digest includes 'data' after unsealing */
	netsec_digest(a, auth_flags, verf, data, data_len, digest_final);

	dump_data_pw("Calculated digest:\n", digest_final, 
		     sizeof(digest_final));
	dump_data_pw("verf->packet_digest:\n", verf->packet_digest, 
		     sizeof(verf->packet_digest));
	
	/* compare - if the client got the same result as us, then
	   it must know the session key */
	return (memcmp(digest_final, verf->packet_digest, 
		       sizeof(verf->packet_digest)) == 0);
}
Exemple #21
0
static void kdc_tcp_call_loop(struct tevent_req *subreq)
{
	struct kdc_tcp_connection *kdc_conn = tevent_req_callback_data(subreq,
				      struct kdc_tcp_connection);
	struct kdc_tcp_call *call;
	NTSTATUS status;
	enum kdc_process_ret ret;

	call = talloc(kdc_conn, struct kdc_tcp_call);
	if (call == NULL) {
		kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: "
				"no memory for kdc_tcp_call");
		return;
	}
	call->kdc_conn = kdc_conn;

	status = tstream_read_pdu_blob_recv(subreq,
					    call,
					    &call->in);
	TALLOC_FREE(subreq);
	if (!NT_STATUS_IS_OK(status)) {
		const char *reason;

		reason = talloc_asprintf(call, "kdc_tcp_call_loop: "
					 "tstream_read_pdu_blob_recv() - %s",
					 nt_errstr(status));
		if (!reason) {
			reason = nt_errstr(status);
		}

		kdc_tcp_terminate_connection(kdc_conn, reason);
		return;
	}

	DEBUG(10,("Received krb5 TCP packet of length %lu from %s\n",
		 (long) call->in.length,
		 tsocket_address_string(kdc_conn->conn->remote_address, call)));

	/* skip length header */
	call->in.data +=4;
	call->in.length -= 4;

	/* Call krb5 */
	ret = kdc_conn->kdc_socket->process(kdc_conn->kdc_socket->kdc,
					   call,
					   &call->in,
					   &call->out,
					   kdc_conn->conn->remote_address,
					   kdc_conn->conn->local_address,
					   0 /* Stream */);
	if (ret == KDC_PROCESS_FAILED) {
		kdc_tcp_terminate_connection(kdc_conn,
				"kdc_tcp_call_loop: process function failed");
		return;
	}

	if (ret == KDC_PROCESS_PROXY) {
		uint16_t port;

		if (!kdc_conn->kdc_socket->kdc->am_rodc) {
			kdc_tcp_terminate_connection(kdc_conn,
						     "kdc_tcp_call_loop: proxying requested when not RODC");
			return;
		}
		port = tsocket_address_inet_port(kdc_conn->conn->local_address);

		subreq = kdc_tcp_proxy_send(call,
					    kdc_conn->conn->event.ctx,
					    kdc_conn->kdc_socket->kdc,
					    port,
					    call->in);
		if (subreq == NULL) {
			kdc_tcp_terminate_connection(kdc_conn,
				"kdc_tcp_call_loop: kdc_tcp_proxy_send failed");
			return;
		}
		tevent_req_set_callback(subreq, kdc_tcp_call_proxy_done, call);
		return;
	}

	/* First add the length of the out buffer */
	RSIVAL(call->out_hdr, 0, call->out.length);
	call->out_iov[0].iov_base = (char *) call->out_hdr;
	call->out_iov[0].iov_len = 4;

	call->out_iov[1].iov_base = (char *) call->out.data;
	call->out_iov[1].iov_len = call->out.length;

	subreq = tstream_writev_queue_send(call,
					   kdc_conn->conn->event.ctx,
					   kdc_conn->tstream,
					   kdc_conn->send_queue,
					   call->out_iov, 2);
	if (subreq == NULL) {
		kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: "
				"no memory for tstream_writev_queue_send");
		return;
	}
	tevent_req_set_callback(subreq, kdc_tcp_call_writev_done, call);

	/*
	 * The krb5 tcp pdu's has the length as 4 byte (initial_read_size),
	 * packet_full_request_u32 provides the pdu length then.
	 */
	subreq = tstream_read_pdu_blob_send(kdc_conn,
					    kdc_conn->conn->event.ctx,
					    kdc_conn->tstream,
					    4, /* initial_read_size */
					    packet_full_request_u32,
					    kdc_conn);
	if (subreq == NULL) {
		kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: "
				"no memory for tstream_read_pdu_blob_send");
		return;
	}
	tevent_req_set_callback(subreq, kdc_tcp_call_loop, kdc_conn);
}
Exemple #22
0
static void kdc_tcp_call_proxy_done(struct tevent_req *subreq)
{
	struct kdc_tcp_call *call = tevent_req_callback_data(subreq,
			struct kdc_tcp_call);
	struct kdc_tcp_connection *kdc_conn = call->kdc_conn;
	NTSTATUS status;

	status = kdc_tcp_proxy_recv(subreq, call, &call->out);
	TALLOC_FREE(subreq);
	if (!NT_STATUS_IS_OK(status)) {
		/* generate an error packet */
		status = kdc_proxy_unavailable_error(kdc_conn->kdc_socket->kdc,
						     call, &call->out);
	}

	if (!NT_STATUS_IS_OK(status)) {
		const char *reason;

		reason = talloc_asprintf(call, "kdc_tcp_call_proxy_done: "
					 "kdc_proxy_unavailable_error - %s",
					 nt_errstr(status));
		if (!reason) {
			reason = "kdc_tcp_call_proxy_done: kdc_proxy_unavailable_error() failed";
		}

		kdc_tcp_terminate_connection(call->kdc_conn, reason);
		return;
	}

	/* First add the length of the out buffer */
	RSIVAL(call->out_hdr, 0, call->out.length);
	call->out_iov[0].iov_base = (char *) call->out_hdr;
	call->out_iov[0].iov_len = 4;

	call->out_iov[1].iov_base = (char *) call->out.data;
	call->out_iov[1].iov_len = call->out.length;

	subreq = tstream_writev_queue_send(call,
					   kdc_conn->conn->event.ctx,
					   kdc_conn->tstream,
					   kdc_conn->send_queue,
					   call->out_iov, 2);
	if (subreq == NULL) {
		kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: "
				"no memory for tstream_writev_queue_send");
		return;
	}
	tevent_req_set_callback(subreq, kdc_tcp_call_writev_done, call);

	/*
	 * The krb5 tcp pdu's has the length as 4 byte (initial_read_size),
	 * packet_full_request_u32 provides the pdu length then.
	 */
	subreq = tstream_read_pdu_blob_send(kdc_conn,
					    kdc_conn->conn->event.ctx,
					    kdc_conn->tstream,
					    4, /* initial_read_size */
					    packet_full_request_u32,
					    kdc_conn);
	if (subreq == NULL) {
		kdc_tcp_terminate_connection(kdc_conn, "kdc_tcp_call_loop: "
				"no memory for tstream_read_pdu_blob_send");
		return;
	}
	tevent_req_set_callback(subreq, kdc_tcp_call_loop, kdc_conn);
}
Exemple #23
0
static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, 
				     TALLOC_CTX *out_mem_ctx,
				     struct tevent_context *ev,
				     const DATA_BLOB in, DATA_BLOB *out)
{
	struct gensec_gssapi_state *gensec_gssapi_state
		= talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
	NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
	OM_uint32 maj_stat, min_stat;
	OM_uint32 min_stat2;
	gss_buffer_desc input_token = { 0, NULL };
	gss_buffer_desc output_token = { 0, NULL };

	gss_OID gss_oid_p = NULL;
	OM_uint32 time_req = 0;
	OM_uint32 time_rec = 0;
	struct timeval tv;

	time_req = gensec_setting_int(gensec_security->settings,
				      "gensec_gssapi", "requested_life_time",
				      time_req);

	input_token.length = in.length;
	input_token.value = in.data;

	switch (gensec_gssapi_state->sasl_state) {
	case STAGE_GSS_NEG:
	{
		switch (gensec_security->gensec_role) {
		case GENSEC_CLIENT:
		{
#ifdef SAMBA4_USES_HEIMDAL
			struct gsskrb5_send_to_kdc send_to_kdc;
			krb5_error_code ret;
#endif

			nt_status = gensec_gssapi_client_creds(gensec_security, ev);
			if (!NT_STATUS_IS_OK(nt_status)) {
				return nt_status;
			}

#ifdef SAMBA4_USES_HEIMDAL
			send_to_kdc.func = smb_krb5_send_and_recv_func;
			send_to_kdc.ptr = ev;

			min_stat = gsskrb5_set_send_to_kdc(&send_to_kdc);
			if (min_stat) {
				DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n"));
				return NT_STATUS_INTERNAL_ERROR;
			}
#endif
			maj_stat = gss_init_sec_context(&min_stat, 
							gensec_gssapi_state->client_cred->creds,
							&gensec_gssapi_state->gssapi_context, 
							gensec_gssapi_state->server_name, 
							gensec_gssapi_state->gss_oid,
							gensec_gssapi_state->gss_want_flags, 
							time_req,
							gensec_gssapi_state->input_chan_bindings,
							&input_token, 
							&gss_oid_p,
							&output_token, 
							&gensec_gssapi_state->gss_got_flags, /* ret flags */
							&time_rec);
			if (gss_oid_p) {
				gensec_gssapi_state->gss_oid = gss_oid_p;
			}

#ifdef SAMBA4_USES_HEIMDAL
			send_to_kdc.func = smb_krb5_send_and_recv_func;
			send_to_kdc.ptr = NULL;

			ret = gsskrb5_set_send_to_kdc(&send_to_kdc);
			if (ret) {
				DEBUG(1,("gensec_gssapi_update: gsskrb5_set_send_to_kdc failed\n"));
				return NT_STATUS_INTERNAL_ERROR;
			}
#endif
			break;
		}
		case GENSEC_SERVER:
		{
			maj_stat = gss_accept_sec_context(&min_stat, 
							  &gensec_gssapi_state->gssapi_context, 
							  gensec_gssapi_state->server_cred->creds,
							  &input_token, 
							  gensec_gssapi_state->input_chan_bindings,
							  &gensec_gssapi_state->client_name, 
							  &gss_oid_p,
							  &output_token, 
							  &gensec_gssapi_state->gss_got_flags, 
							  &time_rec,
							  &gensec_gssapi_state->delegated_cred_handle);
			if (gss_oid_p) {
				gensec_gssapi_state->gss_oid = gss_oid_p;
			}
			break;
		}
		default:
			return NT_STATUS_INVALID_PARAMETER;
			
		}

		gensec_gssapi_state->gss_exchange_count++;

		if (maj_stat == GSS_S_COMPLETE) {
			*out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
			gss_release_buffer(&min_stat2, &output_token);
			
			if (gensec_gssapi_state->gss_got_flags & GSS_C_DELEG_FLAG &&
			    gensec_gssapi_state->delegated_cred_handle != GSS_C_NO_CREDENTIAL) {
				DEBUG(5, ("gensec_gssapi: credentials were delegated\n"));
			} else {
				DEBUG(5, ("gensec_gssapi: NO credentials were delegated\n"));
			}

			tv = timeval_current_ofs(time_rec, 0);
			gensec_gssapi_state->expire_time = timeval_to_nttime(&tv);

			/* We may have been invoked as SASL, so there
			 * is more work to do */
			if (gensec_gssapi_state->sasl) {
				gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_NEG;
				return NT_STATUS_MORE_PROCESSING_REQUIRED;
			} else {
				gensec_gssapi_state->sasl_state = STAGE_DONE;

				if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
					DEBUG(5, ("GSSAPI Connection will be cryptographically sealed\n"));
				} else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
					DEBUG(5, ("GSSAPI Connection will be cryptographically signed\n"));
				} else {
					DEBUG(5, ("GSSAPI Connection will have no cryptographic protection\n"));
				}

				return NT_STATUS_OK;
			}
		} else if (maj_stat == GSS_S_CONTINUE_NEEDED) {
			*out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
			gss_release_buffer(&min_stat2, &output_token);
			
			return NT_STATUS_MORE_PROCESSING_REQUIRED;
		} else if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
			gss_cred_id_t creds = NULL;
			gss_name_t name;
			gss_buffer_desc buffer;
			OM_uint32 lifetime = 0;
			gss_cred_usage_t usage;
			const char *role = NULL;
			DEBUG(0, ("GSS %s Update(krb5)(%d) Update failed, credentials expired during GSSAPI handshake!\n",
				  role,
				  gensec_gssapi_state->gss_exchange_count));

			
			switch (gensec_security->gensec_role) {
			case GENSEC_CLIENT:
				creds = gensec_gssapi_state->client_cred->creds;
				role = "client";
				break;
			case GENSEC_SERVER:
				creds = gensec_gssapi_state->server_cred->creds;
				role = "server";
				break;
			}

			maj_stat = gss_inquire_cred(&min_stat, 
						    creds,
						    &name, &lifetime, &usage, NULL);

			if (maj_stat == GSS_S_COMPLETE) {
				const char *usage_string = NULL;
				switch (usage) {
				case GSS_C_BOTH:
					usage_string = "GSS_C_BOTH";
					break;
				case GSS_C_ACCEPT:
					usage_string = "GSS_C_ACCEPT";
					break;
				case GSS_C_INITIATE:
					usage_string = "GSS_C_INITIATE";
					break;
				}
				maj_stat = gss_display_name(&min_stat, name, &buffer, NULL);
				if (maj_stat) {
					buffer.value = NULL;
					buffer.length = 0;
				}
				if (lifetime > 0) {
					DEBUG(0, ("GSSAPI gss_inquire_cred indicates expiry of %*.*s in %u sec for %s\n", 
						  (int)buffer.length, (int)buffer.length, (char *)buffer.value, 
						  lifetime, usage_string));
				} else {
					DEBUG(0, ("GSSAPI gss_inquire_cred indicates %*.*s has already expired for %s\n", 
						  (int)buffer.length, (int)buffer.length, (char *)buffer.value, 
						  usage_string));
				}
				gss_release_buffer(&min_stat, &buffer);
				gss_release_name(&min_stat, &name);
			} else if (maj_stat != GSS_S_COMPLETE) {
				DEBUG(0, ("inquiry of credential lifefime via GSSAPI gss_inquire_cred failed: %s\n",
					  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
			}
			return NT_STATUS_INVALID_PARAMETER;
		} else if (smb_gss_oid_equal(gensec_gssapi_state->gss_oid,
					     gss_mech_krb5)) {
			switch (min_stat) {
			case KRB5KRB_AP_ERR_TKT_NYV:
				DEBUG(1, ("Error with ticket to contact %s: possible clock skew between us and the KDC or target server: %s\n",
					  gensec_gssapi_state->target_principal,
					  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
				return NT_STATUS_TIME_DIFFERENCE_AT_DC; /* Make SPNEGO ignore us, we can't go any further here */
			case KRB5KRB_AP_ERR_TKT_EXPIRED:
				DEBUG(1, ("Error with ticket to contact %s: ticket is expired, possible clock skew between us and the KDC or target server: %s\n",
					  gensec_gssapi_state->target_principal,
					  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
				return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
			case KRB5_KDC_UNREACH:
				DEBUG(3, ("Cannot reach a KDC we require in order to obtain a ticket to %s: %s\n",
					  gensec_gssapi_state->target_principal,
					  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
				return NT_STATUS_NO_LOGON_SERVERS; /* Make SPNEGO ignore us, we can't go any further here */
			case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
				DEBUG(3, ("Server %s is not registered with our KDC: %s\n",
					  gensec_gssapi_state->target_principal,
					  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
				return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
			case KRB5KRB_AP_ERR_MSG_TYPE:
				/* garbage input, possibly from the auto-mech detection */
				return NT_STATUS_INVALID_PARAMETER;
			default:
				DEBUG(1, ("GSS %s Update(krb5)(%d) Update failed: %s\n",
					  gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server",
					  gensec_gssapi_state->gss_exchange_count,
					  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
				return NT_STATUS_LOGON_FAILURE;
			}
		} else {
			DEBUG(1, ("GSS %s Update(%d) failed: %s\n",
				  gensec_security->gensec_role == GENSEC_CLIENT ? "client" : "server",
				  gensec_gssapi_state->gss_exchange_count,
				  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
			return NT_STATUS_LOGON_FAILURE;
		}
		break;
	}

	/* These last two stages are only done if we were invoked as SASL */
	case STAGE_SASL_SSF_NEG:
	{
		switch (gensec_security->gensec_role) {
		case GENSEC_CLIENT:
		{
			uint8_t maxlength_proposed[4]; 
			uint8_t maxlength_accepted[4]; 
			uint8_t security_supported;
			int conf_state;
			gss_qop_t qop_state;
			input_token.length = in.length;
			input_token.value = in.data;

			/* As a client, we have just send a
			 * zero-length blob to the server (after the
			 * normal GSSAPI exchange), and it has replied
			 * with it's SASL negotiation */
			
			maj_stat = gss_unwrap(&min_stat, 
					      gensec_gssapi_state->gssapi_context, 
					      &input_token,
					      &output_token, 
					      &conf_state,
					      &qop_state);
			if (GSS_ERROR(maj_stat)) {
				DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", 
					  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
				return NT_STATUS_ACCESS_DENIED;
			}
			
			if (output_token.length < 4) {
				return NT_STATUS_INVALID_PARAMETER;
			}

			memcpy(maxlength_proposed, output_token.value, 4);
			gss_release_buffer(&min_stat, &output_token);

			/* first byte is the proposed security */
			security_supported = maxlength_proposed[0];
			maxlength_proposed[0] = '\0';
			
			/* Rest is the proposed max wrap length */
			gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_proposed, 0), 
								     gensec_gssapi_state->max_wrap_buf_size);
			gensec_gssapi_state->sasl_protection = 0;
			if (security_supported & NEG_SEAL) {
				if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
					gensec_gssapi_state->sasl_protection |= NEG_SEAL;
				}
			}
			if (security_supported & NEG_SIGN) {
				if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
					gensec_gssapi_state->sasl_protection |= NEG_SIGN;
				}
			}
			if (security_supported & NEG_NONE) {
				gensec_gssapi_state->sasl_protection |= NEG_NONE;
			}
			if (gensec_gssapi_state->sasl_protection == 0) {
				DEBUG(1, ("Remote server does not support unprotected connections\n"));
				return NT_STATUS_ACCESS_DENIED;
			}

			/* Send back the negotiated max length */

			RSIVAL(maxlength_accepted, 0, gensec_gssapi_state->max_wrap_buf_size);

			maxlength_accepted[0] = gensec_gssapi_state->sasl_protection;
			
			input_token.value = maxlength_accepted;
			input_token.length = sizeof(maxlength_accepted);

			maj_stat = gss_wrap(&min_stat, 
					    gensec_gssapi_state->gssapi_context, 
					    false,
					    GSS_C_QOP_DEFAULT,
					    &input_token,
					    &conf_state,
					    &output_token);
			if (GSS_ERROR(maj_stat)) {
				DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", 
					  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
				return NT_STATUS_ACCESS_DENIED;
			}
			
			*out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
			gss_release_buffer(&min_stat, &output_token);

			/* quirk:  This changes the value that gensec_have_feature returns, to be that after SASL negotiation */
			gensec_gssapi_state->sasl_state = STAGE_DONE;

			if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
				DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically sealed\n"));
			} else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
				DEBUG(3, ("SASL/GSSAPI Connection to server will be cryptographically signed\n"));
			} else {
				DEBUG(3, ("SASL/GSSAPI Connection to server will have no cryptographically protection\n"));
			}

			return NT_STATUS_OK;
		}
		case GENSEC_SERVER:
		{
			uint8_t maxlength_proposed[4]; 
			uint8_t security_supported = 0x0;
			int conf_state;

			/* As a server, we have just been sent a zero-length blob (note this, but it isn't fatal) */
			if (in.length != 0) {
				DEBUG(1, ("SASL/GSSAPI: client sent non-zero length starting SASL negotiation!\n"));
			}
			
			/* Give the client some idea what we will support */
			  
			RSIVAL(maxlength_proposed, 0, gensec_gssapi_state->max_wrap_buf_size);
			/* first byte is the proposed security */
			maxlength_proposed[0] = '\0';
			
			gensec_gssapi_state->sasl_protection = 0;
			if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
				security_supported |= NEG_SEAL;
			} 
			if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
				security_supported |= NEG_SIGN;
			}
			if (security_supported == 0) {
				/* If we don't support anything, this must be 0 */
				RSIVAL(maxlength_proposed, 0, 0x0);
			}

			/* TODO:  We may not wish to support this */
			security_supported |= NEG_NONE;
			maxlength_proposed[0] = security_supported;
			
			input_token.value = maxlength_proposed;
			input_token.length = sizeof(maxlength_proposed);

			maj_stat = gss_wrap(&min_stat, 
					    gensec_gssapi_state->gssapi_context, 
					    false,
					    GSS_C_QOP_DEFAULT,
					    &input_token,
					    &conf_state,
					    &output_token);
			if (GSS_ERROR(maj_stat)) {
				DEBUG(1, ("GSS Update(SSF_NEG): GSS Wrap failed: %s\n", 
					  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
				return NT_STATUS_ACCESS_DENIED;
			}
			
			*out = data_blob_talloc(out_mem_ctx, output_token.value, output_token.length);
			gss_release_buffer(&min_stat, &output_token);

			gensec_gssapi_state->sasl_state = STAGE_SASL_SSF_ACCEPT;
			return NT_STATUS_MORE_PROCESSING_REQUIRED;
		}
		default:
  			return NT_STATUS_INVALID_PARAMETER;
			
		}
	}
	/* This is s server-only stage */
	case STAGE_SASL_SSF_ACCEPT:
	{
		uint8_t maxlength_accepted[4]; 
		uint8_t security_accepted;
		int conf_state;
		gss_qop_t qop_state;
		input_token.length = in.length;
		input_token.value = in.data;
			
		maj_stat = gss_unwrap(&min_stat, 
				      gensec_gssapi_state->gssapi_context, 
				      &input_token,
				      &output_token, 
				      &conf_state,
				      &qop_state);
		if (GSS_ERROR(maj_stat)) {
			DEBUG(1, ("gensec_gssapi_update: GSS UnWrap of SASL protection negotiation failed: %s\n", 
				  gssapi_error_string(out_mem_ctx, maj_stat, min_stat, gensec_gssapi_state->gss_oid)));
			return NT_STATUS_ACCESS_DENIED;
		}
			
		if (output_token.length < 4) {
			return NT_STATUS_INVALID_PARAMETER;
		}

		memcpy(maxlength_accepted, output_token.value, 4);
		gss_release_buffer(&min_stat, &output_token);
		
		/* first byte is the proposed security */
		security_accepted = maxlength_accepted[0];
		maxlength_accepted[0] = '\0';

		/* Rest is the proposed max wrap length */
		gensec_gssapi_state->max_wrap_buf_size = MIN(RIVAL(maxlength_accepted, 0), 
							     gensec_gssapi_state->max_wrap_buf_size);

		gensec_gssapi_state->sasl_protection = 0;
		if (security_accepted & NEG_SEAL) {
			if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
				DEBUG(1, ("Remote client wanted seal, but gensec refused\n"));
				return NT_STATUS_ACCESS_DENIED;
			}
			gensec_gssapi_state->sasl_protection |= NEG_SEAL;
		}
		if (security_accepted & NEG_SIGN) {
			if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
				DEBUG(1, ("Remote client wanted sign, but gensec refused\n"));
				return NT_STATUS_ACCESS_DENIED;
			}
			gensec_gssapi_state->sasl_protection |= NEG_SIGN;
		}
		if (security_accepted & NEG_NONE) {
			gensec_gssapi_state->sasl_protection |= NEG_NONE;
		}

		/* quirk:  This changes the value that gensec_have_feature returns, to be that after SASL negotiation */
		gensec_gssapi_state->sasl_state = STAGE_DONE;
		if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
			DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically sealed\n"));
		} else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
			DEBUG(5, ("SASL/GSSAPI Connection from client will be cryptographically signed\n"));
		} else {
			DEBUG(5, ("SASL/GSSAPI Connection from client will have no cryptographic protection\n"));
		}

		*out = data_blob(NULL, 0);
		return NT_STATUS_OK;	
	}
	default:
		return NT_STATUS_INVALID_PARAMETER;
	}
}
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
					    void *data,
					    krb5_krbhst_info *hi,
					    time_t timeout,
					    const krb5_data *send_buf,
					    krb5_data *recv_buf)
{
	krb5_error_code ret;
	NTSTATUS status;
	const char *name;
	struct addrinfo *ai, *a;
	struct smb_krb5_socket *smb_krb5;

	DATA_BLOB send_blob;

	struct tevent_context *ev;
	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
	if (!tmp_ctx) {
		return ENOMEM;
	}

	if (!data) {
		/* If no event context was available, then create one for this loop */
		ev = samba_tevent_context_init(tmp_ctx);
		if (!ev) {
			talloc_free(tmp_ctx);
			return ENOMEM;
		}
	} else {
		ev = talloc_get_type_abort(data, struct tevent_context);
	}

	send_blob = data_blob_const(send_buf->data, send_buf->length);

	ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
	if (ret) {
		talloc_free(tmp_ctx);
		return ret;
	}

	for (a = ai; a; a = a->ai_next) {
		struct socket_address *remote_addr;
		smb_krb5 = talloc(tmp_ctx, struct smb_krb5_socket);
		if (!smb_krb5) {
			talloc_free(tmp_ctx);
			return ENOMEM;
		}
		smb_krb5->hi = hi;

		switch (a->ai_family) {
		case PF_INET:
			name = "ipv4";
			break;
#ifdef HAVE_IPV6
		case PF_INET6:
			name = "ipv6";
			break;
#endif
		default:
			talloc_free(tmp_ctx);
			return EINVAL;
		}

		status = NT_STATUS_INVALID_PARAMETER;
		switch (hi->proto) {
		case KRB5_KRBHST_UDP:
			status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
			break;
		case KRB5_KRBHST_TCP:
			status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
			break;
		case KRB5_KRBHST_HTTP:
			talloc_free(tmp_ctx);
			return EINVAL;
		}
		if (!NT_STATUS_IS_OK(status)) {
			talloc_free(smb_krb5);
			continue;
		}

		talloc_steal(smb_krb5, smb_krb5->sock);

		remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
		if (!remote_addr) {
			talloc_free(smb_krb5);
			continue;
		}

		status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
		if (!NT_STATUS_IS_OK(status)) {
			talloc_free(smb_krb5);
			continue;
		}

		/* Setup the FDE, start listening for read events
		 * from the start (otherwise we may miss a socket
		 * drop) and mark as AUTOCLOSE along with the fde */

		/* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */
		smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock,
					      socket_get_fd(smb_krb5->sock),
					      TEVENT_FD_READ,
					      smb_krb5_socket_handler, smb_krb5);
		/* its now the job of the event layer to close the socket */
		tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn);
		socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE);

		tevent_add_timer(ev, smb_krb5,
				 timeval_current_ofs(timeout, 0),
				 smb_krb5_request_timeout, smb_krb5);

		smb_krb5->status = NT_STATUS_OK;
		smb_krb5->reply = data_blob(NULL, 0);

		switch (hi->proto) {
		case KRB5_KRBHST_UDP:
			TEVENT_FD_WRITEABLE(smb_krb5->fde);
			smb_krb5->request = send_blob;
			break;
		case KRB5_KRBHST_TCP:

			smb_krb5->packet = packet_init(smb_krb5);
			if (smb_krb5->packet == NULL) {
				talloc_free(smb_krb5);
				return ENOMEM;
			}
			packet_set_private(smb_krb5->packet, smb_krb5);
			packet_set_socket(smb_krb5->packet, smb_krb5->sock);
			packet_set_callback(smb_krb5->packet, smb_krb5_full_packet);
			packet_set_full_request(smb_krb5->packet, packet_full_request_u32);
			packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler);
			packet_set_event_context(smb_krb5->packet, ev);
			packet_set_fde(smb_krb5->packet, smb_krb5->fde);

			smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
			RSIVAL(smb_krb5->request.data, 0, send_blob.length);
			memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
			packet_send(smb_krb5->packet, smb_krb5->request);
			break;
		case KRB5_KRBHST_HTTP:
			talloc_free(tmp_ctx);
			return EINVAL;
		}
		while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
			if (tevent_loop_once(ev) != 0) {
				talloc_free(tmp_ctx);
				return EINVAL;
			}

			/* After each and every event loop, reset the
			 * send_to_kdc pointers to what they were when
			 * we entered this loop.  That way, if a
			 * nested event has invalidated them, we put
			 * it back before we return to the heimdal
			 * code */
			ret = krb5_set_send_to_kdc_func(context,
							smb_krb5_send_and_recv_func,
							data);
			if (ret != 0) {
				talloc_free(tmp_ctx);
				return ret;
			}
		}
		if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) {
			talloc_free(smb_krb5);
			continue;
		}

		if (!NT_STATUS_IS_OK(smb_krb5->status)) {
			struct tsocket_address *addr = socket_address_to_tsocket_address(smb_krb5, remote_addr);
			const char *addr_string = NULL;
			if (addr) {
				addr_string = tsocket_address_inet_addr_string(addr, smb_krb5);
			} else {
				addr_string = NULL;
			}
			DEBUG(2,("Error reading smb_krb5 reply packet: %s from %s\n", nt_errstr(smb_krb5->status),
				 addr_string));
			talloc_free(smb_krb5);
			continue;
		}

		ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
		if (ret) {
			talloc_free(tmp_ctx);
			return ret;
		}
		talloc_free(smb_krb5);

		break;
	}
	talloc_free(tmp_ctx);
	if (a) {
		return 0;
	}
	return KRB5_KDC_UNREACH;
}
NTSTATUS netsec_incoming_packet(struct schannel_state *state,
				bool do_unseal,
				uint8_t *data, size_t length,
				const DATA_BLOB *sig)
{
	uint32_t min_sig_size = 0;
	uint8_t header[8];
	uint8_t checksum[32];
	uint32_t checksum_length = sizeof(checksum_length);
	uint8_t _confounder[8];
	uint8_t *confounder = NULL;
	uint32_t confounder_ofs = 0;
	uint8_t seq_num[8];
	int ret;

	netsec_offset_and_sizes(state,
				do_unseal,
				&min_sig_size,
				NULL,
				&checksum_length,
				&confounder_ofs);

	if (sig->length < min_sig_size) {
		return NT_STATUS_ACCESS_DENIED;
	}

	if (do_unseal) {
		confounder = _confounder;
		memcpy(confounder, sig->data+confounder_ofs, 8);
	} else {
		confounder = NULL;
	}

	RSIVAL(seq_num, 0, state->seq_num);
	SIVAL(seq_num, 4, state->initiator?0:0x80);

	if (do_unseal) {
		netsec_do_seal(state, seq_num,
			       confounder,
			       data, length,
			       false);
	}

	netsec_do_sign(state, confounder,
		       data, length,
		       header, checksum);

	ret = memcmp(checksum, sig->data+16, checksum_length);
	if (ret != 0) {
		dump_data_pw("calc digest:", checksum, checksum_length);
		dump_data_pw("wire digest:", sig->data+16, checksum_length);
		return NT_STATUS_ACCESS_DENIED;
	}

	netsec_do_seq_num(state, checksum, checksum_length, seq_num);

	ret = memcmp(seq_num, sig->data+8, 8);
	if (ret != 0) {
		dump_data_pw("calc seq num:", seq_num, 8);
		dump_data_pw("wire seq num:", sig->data+8, 8);
		return NT_STATUS_ACCESS_DENIED;
	}

	return NT_STATUS_OK;
}
Exemple #26
0
/* 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;
}
Exemple #27
0
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
					    void *data,
					    krb5_krbhst_info *hi,
					    time_t timeout,
					    const krb5_data *send_buf,
					    krb5_data *recv_buf)
{
	krb5_error_code ret;
	NTSTATUS status;
	struct socket_address *remote_addr;
	const char *name;
	struct addrinfo *ai, *a;
	struct smb_krb5_socket *smb_krb5;

	struct tevent_context *ev = talloc_get_type(data, struct tevent_context);

	DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length);

	ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
	if (ret) {
		return ret;
	}

	for (a = ai; a; a = ai->ai_next) {
		smb_krb5 = talloc(NULL, struct smb_krb5_socket);
		if (!smb_krb5) {
			return ENOMEM;
		}
		smb_krb5->hi = hi;

		switch (a->ai_family) {
		case PF_INET:
			name = "ipv4";
			break;
#ifdef HAVE_IPV6
		case PF_INET6:
			name = "ipv6";
			break;
#endif
		default:
			talloc_free(smb_krb5);
			return EINVAL;
		}

		status = NT_STATUS_INVALID_PARAMETER;
		switch (hi->proto) {
		case KRB5_KRBHST_UDP:
			status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
			break;
		case KRB5_KRBHST_TCP:
			status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
			break;
		case KRB5_KRBHST_HTTP:
			talloc_free(smb_krb5);
			return EINVAL;
		}
		if (!NT_STATUS_IS_OK(status)) {
			talloc_free(smb_krb5);
			continue;
		}

		talloc_steal(smb_krb5, smb_krb5->sock);

		remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
		if (!remote_addr) {
			talloc_free(smb_krb5);
			continue;
		}

		status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
		if (!NT_STATUS_IS_OK(status)) {
			talloc_free(smb_krb5);
			continue;
		}
		talloc_free(remote_addr);

		/* Setup the FDE, start listening for read events
		 * from the start (otherwise we may miss a socket
		 * drop) and mark as AUTOCLOSE along with the fde */

		/* Ths is equivilant to EVENT_FD_READABLE(smb_krb5->fde) */
		smb_krb5->fde = tevent_add_fd(ev, smb_krb5->sock,
					      socket_get_fd(smb_krb5->sock),
					      TEVENT_FD_READ,
					      smb_krb5_socket_handler, smb_krb5);
		/* its now the job of the event layer to close the socket */
		tevent_fd_set_close_fn(smb_krb5->fde, socket_tevent_fd_close_fn);
		socket_set_flags(smb_krb5->sock, SOCKET_FLAG_NOCLOSE);

		tevent_add_timer(ev, smb_krb5,
				 timeval_current_ofs(timeout, 0),
				 smb_krb5_request_timeout, smb_krb5);

		smb_krb5->status = NT_STATUS_OK;
		smb_krb5->reply = data_blob(NULL, 0);

		switch (hi->proto) {
		case KRB5_KRBHST_UDP:
			TEVENT_FD_WRITEABLE(smb_krb5->fde);
			smb_krb5->request = send_blob;
			break;
		case KRB5_KRBHST_TCP:

			smb_krb5->packet = packet_init(smb_krb5);
			if (smb_krb5->packet == NULL) {
				talloc_free(smb_krb5);
				return ENOMEM;
			}
			packet_set_private(smb_krb5->packet, smb_krb5);
			packet_set_socket(smb_krb5->packet, smb_krb5->sock);
			packet_set_callback(smb_krb5->packet, smb_krb5_full_packet);
			packet_set_full_request(smb_krb5->packet, packet_full_request_u32);
			packet_set_error_handler(smb_krb5->packet, smb_krb5_error_handler);
			packet_set_event_context(smb_krb5->packet, ev);
			packet_set_fde(smb_krb5->packet, smb_krb5->fde);

			smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
			RSIVAL(smb_krb5->request.data, 0, send_blob.length);
			memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
			packet_send(smb_krb5->packet, smb_krb5->request);
			break;
		case KRB5_KRBHST_HTTP:
			talloc_free(smb_krb5);
			return EINVAL;
		}
		while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
			if (tevent_loop_once(ev) != 0) {
				talloc_free(smb_krb5);
				return EINVAL;
			}
		}
		if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) {
			talloc_free(smb_krb5);
			continue;
		}

		if (!NT_STATUS_IS_OK(smb_krb5->status)) {
			DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status)));
			talloc_free(smb_krb5);
			continue;
		}

		ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
		if (ret) {
			talloc_free(smb_krb5);
			return ret;
		}
		talloc_free(smb_krb5);

		break;
	}
	if (a) {
		return 0;
	}
	return KRB5_KDC_UNREACH;
}