示例#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;
}
示例#2
0
文件: parse_prs.c 项目: jophxy/samba
static void dbg_rw_punival(BOOL charmode, const char *name, int depth, prs_struct *ps,
							char *in_buf, char *out_buf, int len)
{
	int i;

	if (UNMARSHALLING(ps)) {
		if (ps->bigendian_data) {
			for (i = 0; i < len; i++)
				SSVAL(out_buf,2*i,RSVAL(in_buf, 2*i));
		} else {
			for (i = 0; i < len; i++)
				SSVAL(out_buf, 2*i, SVAL(in_buf, 2*i));
		}
	} else {
		if (ps->bigendian_data) {
			for (i = 0; i < len; i++)
				RSSVAL(in_buf, 2*i, SVAL(out_buf,2*i));
		} else {
			for (i = 0; i < len; i++)
				SSVAL(in_buf, 2*i, SVAL(out_buf,2*i));
		}
	}

	DEBUG(5,("%s%04x %s: ", tab_depth(depth), ps->data_offset, name));
	if (charmode)
		print_asc(5, (unsigned char*)out_buf, 2*len);
	else {
		for (i = 0; i < len; i++)
			DEBUG(5,("%04x ", out_buf[i]));
	}
    DEBUG(5,("\n"));
}
示例#3
0
void dcerpc_set_auth_length(DATA_BLOB *blob, uint16_t v)
{
	if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
		SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
	} else {
		RSSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
	}
}
示例#4
0
/* 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 */
}
示例#5
0
/*
  send a WACK reply
*/
void nbtd_wack_reply(struct nbt_name_socket *nbtsock, 
		     struct nbt_name_packet *request_packet, 
		     struct socket_address *src,
		     uint32_t ttl)
{
	struct nbt_name_packet *packet;
	struct nbt_name *name = &request_packet->questions[0].name;
	struct nbtd_interface *iface = talloc_get_type(nbtsock->incoming.private, 
						       struct nbtd_interface);
	struct nbtd_server *nbtsrv = iface->nbtsrv;

	packet = talloc_zero(nbtsock, struct nbt_name_packet);
	if (packet == NULL) return;

	packet->name_trn_id = request_packet->name_trn_id;
	packet->ancount = 1;
	packet->operation = 
		NBT_FLAG_REPLY | 
		NBT_OPCODE_WACK |
		NBT_FLAG_AUTHORITIVE;
	
	packet->answers = talloc_array(packet, struct nbt_res_rec, 1);
	if (packet->answers == NULL) goto failed;

	packet->answers[0].name              = *name;
	packet->answers[0].rr_type           = NBT_QTYPE_NETBIOS;
	packet->answers[0].rr_class          = NBT_QCLASS_IP;
	packet->answers[0].ttl               = ttl;
	packet->answers[0].rdata.data.length = 2;
	packet->answers[0].rdata.data.data   = talloc_size(packet, 2);
	if (packet->answers[0].rdata.data.data == NULL) goto failed;
	RSSVAL(packet->answers[0].rdata.data.data, 0, request_packet->operation);

	DEBUG(7,("Sending WACK reply for %s to %s:%d\n", 
		 nbt_name_string(packet, name), src->addr, src->port));
	
	nbtsrv->stats.total_sent++;
	nbt_name_reply_send(nbtsock, src, packet);

failed:
	talloc_free(packet);
}
示例#6
0
文件: parse_prs.c 项目: jophxy/samba
BOOL prs_uint16s(BOOL charmode, const char *name, prs_struct *ps, int depth, uint16 *data16s, int len)
{
	int i;
	char *q = prs_mem_get(ps, len * sizeof(uint16));
	if (q == NULL)
		return False;

	if (UNMARSHALLING(ps)) {
		if (ps->bigendian_data) {
			for (i = 0; i < len; i++)
				data16s[i] = RSVAL(q, 2*i);
		} else {
			for (i = 0; i < len; i++)
				data16s[i] = SVAL(q, 2*i);
		}
	} else {
		if (ps->bigendian_data) {
			for (i = 0; i < len; i++)
				RSSVAL(q, 2*i, data16s[i]);
		} else {
			for (i = 0; i < len; i++)
				SSVAL(q, 2*i, data16s[i]);
		}
	}

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

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

	return True;
}
示例#7
0
 int buffer_add_uint16(xrtp_buffer_t * buf, uint16 word){

    if(buf->len_data + sizeof(uint16) > buf->len){

       return OS_EREFUSE;
    }

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

    buf->len_data += sizeof(uint16);
    buf->pos += sizeof(uint16);
    
    return OS_OK;
 }
示例#8
0
/* Return true if there is a valid error packet formed in the error_blob */
static bool kpasswdd_make_error_reply(struct kdc_server *kdc,
				     TALLOC_CTX *mem_ctx,
				     uint16_t result_code,
				     const char *error_string,
				     DATA_BLOB *error_blob)
{
	char *error_string_utf8;
	size_t len;

	DEBUG(result_code ? 3 : 10, ("kpasswdd: %s\n", error_string));

	if (!push_utf8_talloc(mem_ctx, &error_string_utf8, error_string, &len)) {
		return false;
	}

	*error_blob = data_blob_talloc(mem_ctx, NULL, 2 + len + 1);
	if (!error_blob->data) {
		return false;
	}
	RSSVAL(error_blob->data, 0, result_code);
	memcpy(error_blob->data + 2, error_string_utf8, len + 1);
	return true;
}
示例#9
0
文件: parse_prs.c 项目: jophxy/samba
BOOL prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16)
{
	char *q = prs_mem_get(ps, sizeof(uint16));
	if (q == NULL)
		return False;

    if (UNMARSHALLING(ps)) {
		if (ps->bigendian_data)
			*data16 = RSVAL(q,0);
		else
			*data16 = SVAL(q,0);
    } else {
		if (ps->bigendian_data)
			RSSVAL(q,0,*data16);
		else
			SSVAL(q,0,*data16);
	}

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

	ps->data_offset += sizeof(uint16);

	return True;
}
示例#10
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;
}
示例#11
0
bool kpasswdd_process(struct kdc_server *kdc,
		      TALLOC_CTX *mem_ctx,
		      DATA_BLOB *input,
		      DATA_BLOB *reply,
		      struct tsocket_address *peer_addr,
		      struct tsocket_address *my_addr,
		      int datagram_reply)
{
	bool ret;
	const uint16_t header_len = 6;
	uint16_t len;
	uint16_t ap_req_len;
	uint16_t krb_priv_len;
	uint16_t version;
	NTSTATUS nt_status;
	DATA_BLOB ap_req, krb_priv_req;
	DATA_BLOB krb_priv_rep = data_blob(NULL, 0);
	DATA_BLOB ap_rep = data_blob(NULL, 0);
	DATA_BLOB kpasswd_req, kpasswd_rep;
	struct cli_credentials *server_credentials;
	struct gensec_security *gensec_security;
	TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);

	char *keytab_name;

	if (!tmp_ctx) {
		return false;
	}

	/* Be parinoid.  We need to ensure we don't just let the
	 * caller lead us into a buffer overflow */
	if (input->length <= header_len) {
		talloc_free(tmp_ctx);
		return false;
	}

	len = RSVAL(input->data, 0);
	if (input->length != len) {
		talloc_free(tmp_ctx);
		return false;
	}

	/* There are two different versions of this protocol so far,
	 * plus others in the standards pipe.  Fortunetly they all
	 * take a very similar framing */
	version = RSVAL(input->data, 2);
	ap_req_len = RSVAL(input->data, 4);
	if ((ap_req_len >= len) || (ap_req_len + header_len) >= len) {
		talloc_free(tmp_ctx);
		return false;
	}

	krb_priv_len = len - ap_req_len;
	ap_req = data_blob_const(&input->data[header_len], ap_req_len);
	krb_priv_req = data_blob_const(&input->data[header_len + ap_req_len], krb_priv_len);

	server_credentials = cli_credentials_init(tmp_ctx);
	if (!server_credentials) {
		DEBUG(1, ("Failed to init server credentials\n"));
		return false;
	}

	/* We want the credentials subsystem to use the krb5 context
	 * we already have, rather than a new context */
	cli_credentials_set_krb5_context(server_credentials, kdc->smb_krb5_context);
	cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);

	keytab_name = talloc_asprintf(server_credentials, "HDB:samba4&%p", kdc->base_ctx);

	cli_credentials_set_username(server_credentials, "kadmin/changepw", CRED_SPECIFIED);
	ret = cli_credentials_set_keytab_name(server_credentials, kdc->task->event_ctx, kdc->task->lp_ctx, keytab_name, CRED_SPECIFIED);
	if (ret != 0) {
		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
						       KRB5_KPASSWD_HARDERROR,
						       talloc_asprintf(mem_ctx,
								       "Failed to obtain server credentials for kadmin/changepw: %s\n",
								       nt_errstr(nt_status)),
						       &krb_priv_rep);
		ap_rep.length = 0;
		if (ret) {
			goto reply;
		}
		talloc_free(tmp_ctx);
		return ret;
	}

	/* We don't strictly need to call this wrapper, and could call
	 * gensec_server_start directly, as we have no need for NTLM
	 * and we have a PAC, but this ensures that the wrapper can be
	 * safely extended for other helpful things in future */
	nt_status = samba_server_gensec_start(tmp_ctx, kdc->task->event_ctx,
					      kdc->task->msg_ctx,
					      kdc->task->lp_ctx,
					      server_credentials,
					      "kpasswd",
					      &gensec_security);
	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(tmp_ctx);
		return false;
	}

	/* The kerberos PRIV packets include these addresses.  MIT
	 * clients check that they are present */
#if 0
	/* Skip this part for now, it breaks with a NetAPP filer and
	 * in any case where the client address is behind NAT.  If
	 * older MIT clients need this, we might have to insert more
	 * complex code */

	nt_status = gensec_set_local_address(gensec_security, peer_addr);
	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(tmp_ctx);
		return false;
	}
#endif

	nt_status = gensec_set_local_address(gensec_security, my_addr);
	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(tmp_ctx);
		return false;
	}

	/* We want the GENSEC wrap calls to generate PRIV tokens */
	gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);

	nt_status = gensec_start_mech_by_name(gensec_security, "krb5");
	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(tmp_ctx);
		return false;
	}

	/* Accept the AP-REQ and generate teh AP-REP we need for the reply */
	nt_status = gensec_update(gensec_security, tmp_ctx, ap_req, &ap_rep);
	if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {

		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
						       KRB5_KPASSWD_HARDERROR,
						       talloc_asprintf(mem_ctx,
								       "gensec_update failed: %s",
								       nt_errstr(nt_status)),
						       &krb_priv_rep);
		ap_rep.length = 0;
		if (ret) {
			goto reply;
		}
		talloc_free(tmp_ctx);
		return ret;
	}

	/* Extract the data from the KRB-PRIV half of the message */
	nt_status = gensec_unwrap(gensec_security, tmp_ctx, &krb_priv_req, &kpasswd_req);
	if (!NT_STATUS_IS_OK(nt_status)) {
		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
						       KRB5_KPASSWD_HARDERROR,
						       talloc_asprintf(mem_ctx,
								       "gensec_unwrap failed: %s",
								       nt_errstr(nt_status)),
						       &krb_priv_rep);
		ap_rep.length = 0;
		if (ret) {
			goto reply;
		}
		talloc_free(tmp_ctx);
		return ret;
	}

	/* Figure out something to do with it (probably changing a password...) */
	ret = kpasswd_process_request(kdc, tmp_ctx,
				      gensec_security,
				      version,
				      &kpasswd_req, &kpasswd_rep);
	if (!ret) {
		/* Argh! */
		return false;
	}

	/* And wrap up the reply: This ensures that the error message
	 * or success can be verified by the client */
	nt_status = gensec_wrap(gensec_security, tmp_ctx,
				&kpasswd_rep, &krb_priv_rep);
	if (!NT_STATUS_IS_OK(nt_status)) {
		ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx,
						       KRB5_KPASSWD_HARDERROR,
						       talloc_asprintf(mem_ctx,
								       "gensec_wrap failed: %s",
								       nt_errstr(nt_status)),
						       &krb_priv_rep);
		ap_rep.length = 0;
		if (ret) {
			goto reply;
		}
		talloc_free(tmp_ctx);
		return ret;
	}

reply:
	*reply = data_blob_talloc(mem_ctx, NULL, krb_priv_rep.length + ap_rep.length + header_len);
	if (!reply->data) {
		return false;
	}

	RSSVAL(reply->data, 0, reply->length);
	RSSVAL(reply->data, 2, 1); /* This is a version 1 reply, MS change/set or otherwise */
	RSSVAL(reply->data, 4, ap_rep.length);
	memcpy(reply->data + header_len,
	       ap_rep.data,
	       ap_rep.length);
	memcpy(reply->data + header_len + ap_rep.length,
	       krb_priv_rep.data,
	       krb_priv_rep.length);

	talloc_free(tmp_ctx);
	return ret;
}
示例#12
0
WERROR dns_verify_tsig(struct dns_server *dns,
		       TALLOC_CTX *mem_ctx,
		       struct dns_request_state *state,
		       struct dns_name_packet *packet,
		       DATA_BLOB *in)
{
	WERROR werror;
	NTSTATUS status;
	enum ndr_err_code ndr_err;
	bool found_tsig = false;
	uint16_t i, arcount = 0;
	DATA_BLOB tsig_blob, fake_tsig_blob, sig;
	uint8_t *buffer = NULL;
	size_t buffer_len = 0, packet_len = 0;
	struct dns_server_tkey *tkey = NULL;
	struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx,
			struct dns_fake_tsig_rec);


	/* Find the first TSIG record in the additional records */
	for (i=0; i < packet->arcount; i++) {
		if (packet->additional[i].rr_type == DNS_QTYPE_TSIG) {
			found_tsig = true;
			break;
		}
	}

	if (!found_tsig) {
		return WERR_OK;
	}

	/* The TSIG record needs to be the last additional record */
	if (found_tsig && i + 1 != packet->arcount) {
		DEBUG(1, ("TSIG record not the last additional record!\n"));
		return DNS_ERR(FORMAT_ERROR);
	}

	/* We got a TSIG, so we need to sign our reply */
	state->sign = true;

	state->tsig = talloc_zero(state->mem_ctx, struct dns_res_rec);
	if (state->tsig == NULL) {
		return WERR_NOT_ENOUGH_MEMORY;
	}

	werror = dns_copy_tsig(state->tsig, &packet->additional[i],
			       state->tsig);
	if (!W_ERROR_IS_OK(werror)) {
		return werror;
	}

	packet->arcount--;

	tkey = dns_find_tkey(dns->tkeys, state->tsig->name);
	if (tkey == NULL) {
		/*
		 * We must save the name for use in the TSIG error
		 * response and have no choice here but to save the
		 * keyname from the TSIG request.
		 */
		state->key_name = talloc_strdup(state->mem_ctx,
						state->tsig->name);
		if (state->key_name == NULL) {
			return WERR_NOT_ENOUGH_MEMORY;
		}
		state->tsig_error = DNS_RCODE_BADKEY;
		return DNS_ERR(NOTAUTH);
	}

	/*
	 * Remember the keyname that found an existing tkey, used
	 * later to fetch the key with dns_find_tkey() when signing
	 * and adding a TSIG record with MAC.
	 */
	state->key_name = talloc_strdup(state->mem_ctx, tkey->name);
	if (state->key_name == NULL) {
		return WERR_NOT_ENOUGH_MEMORY;
	}

	/* FIXME: check TSIG here */
	if (check_rec == NULL) {
		return WERR_NOT_ENOUGH_MEMORY;
	}

	/* first build and verify check packet */
	check_rec->name = talloc_strdup(check_rec, tkey->name);
	if (check_rec->name == NULL) {
		return WERR_NOT_ENOUGH_MEMORY;
	}
	check_rec->rr_class = DNS_QCLASS_ANY;
	check_rec->ttl = 0;
	check_rec->algorithm_name = talloc_strdup(check_rec, tkey->algorithm);
	if (check_rec->algorithm_name == NULL) {
		return WERR_NOT_ENOUGH_MEMORY;
	}
	check_rec->time_prefix = 0;
	check_rec->time = state->tsig->rdata.tsig_record.time;
	check_rec->fudge = state->tsig->rdata.tsig_record.fudge;
	check_rec->error = 0;
	check_rec->other_size = 0;
	check_rec->other_data = NULL;

	ndr_err = ndr_push_struct_blob(&tsig_blob, mem_ctx, state->tsig,
		(ndr_push_flags_fn_t)ndr_push_dns_res_rec);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		DEBUG(1, ("Failed to push packet: %s!\n",
			  ndr_errstr(ndr_err)));
		return DNS_ERR(SERVER_FAILURE);
	}

	ndr_err = ndr_push_struct_blob(&fake_tsig_blob, mem_ctx, check_rec,
		(ndr_push_flags_fn_t)ndr_push_dns_fake_tsig_rec);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		DEBUG(1, ("Failed to push packet: %s!\n",
			  ndr_errstr(ndr_err)));
		return DNS_ERR(SERVER_FAILURE);
	}

	/* we need to work some magic here. we need to keep the input packet
	 * exactly like we got it, but we need to cut off the tsig record */
	packet_len = in->length - tsig_blob.length;
	buffer_len = packet_len + fake_tsig_blob.length;
	buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
	if (buffer == NULL) {
		return WERR_NOT_ENOUGH_MEMORY;
	}

	memcpy(buffer, in->data, packet_len);
	memcpy(buffer + packet_len, fake_tsig_blob.data, fake_tsig_blob.length);

	sig.length = state->tsig->rdata.tsig_record.mac_size;
	sig.data = talloc_memdup(mem_ctx, state->tsig->rdata.tsig_record.mac, sig.length);
	if (sig.data == NULL) {
		return WERR_NOT_ENOUGH_MEMORY;
	}

	/* Now we also need to count down the additional record counter */
	arcount = RSVAL(buffer, 10);
	RSSVAL(buffer, 10, arcount-1);

	status = gensec_check_packet(tkey->gensec, buffer, buffer_len,
				    buffer, buffer_len, &sig);
	if (NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
		state->tsig_error = DNS_RCODE_BADSIG;
		return DNS_ERR(NOTAUTH);
	}

	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("Verifying tsig failed: %s\n", nt_errstr(status)));
		return ntstatus_to_werror(status);
	}

	state->authenticated = true;

	return WERR_OK;
}