⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rxkad.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	challenge.version	= htonl(2);	challenge.nonce		= htonl(conn->security_nonce);	challenge.min_level	= htonl(0);	challenge.__padding	= 0;	msg.msg_name	= &conn->trans->peer->srx.transport.sin;	msg.msg_namelen	= sizeof(conn->trans->peer->srx.transport.sin);	msg.msg_control	= NULL;	msg.msg_controllen = 0;	msg.msg_flags	= 0;	hdr.epoch	= conn->epoch;	hdr.cid		= conn->cid;	hdr.callNumber	= 0;	hdr.seq		= 0;	hdr.type	= RXRPC_PACKET_TYPE_CHALLENGE;	hdr.flags	= conn->out_clientflag;	hdr.userStatus	= 0;	hdr.securityIndex = conn->security_ix;	hdr._rsvd	= 0;	hdr.serviceId	= conn->service_id;	iov[0].iov_base	= &hdr;	iov[0].iov_len	= sizeof(hdr);	iov[1].iov_base	= &challenge;	iov[1].iov_len	= sizeof(challenge);	len = iov[0].iov_len + iov[1].iov_len;	hdr.serial = htonl(atomic_inc_return(&conn->serial));	_proto("Tx CHALLENGE %%%u", ntohl(hdr.serial));	ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len);	if (ret < 0) {		_debug("sendmsg failed: %d", ret);		return -EAGAIN;	}	_leave(" = 0");	return 0;}/* * send a Kerberos security response */static int rxkad_send_response(struct rxrpc_connection *conn,			       struct rxrpc_header *hdr,			       struct rxkad_response *resp,			       const struct rxkad_key *s2){	struct msghdr msg;	struct kvec iov[3];	size_t len;	int ret;	_enter("");	msg.msg_name	= &conn->trans->peer->srx.transport.sin;	msg.msg_namelen	= sizeof(conn->trans->peer->srx.transport.sin);	msg.msg_control	= NULL;	msg.msg_controllen = 0;	msg.msg_flags	= 0;	hdr->epoch	= conn->epoch;	hdr->seq	= 0;	hdr->type	= RXRPC_PACKET_TYPE_RESPONSE;	hdr->flags	= conn->out_clientflag;	hdr->userStatus	= 0;	hdr->_rsvd	= 0;	iov[0].iov_base	= hdr;	iov[0].iov_len	= sizeof(*hdr);	iov[1].iov_base	= resp;	iov[1].iov_len	= sizeof(*resp);	iov[2].iov_base	= (void *) s2->ticket;	iov[2].iov_len	= s2->ticket_len;	len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len;	hdr->serial = htonl(atomic_inc_return(&conn->serial));	_proto("Tx RESPONSE %%%u", ntohl(hdr->serial));	ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 3, len);	if (ret < 0) {		_debug("sendmsg failed: %d", ret);		return -EAGAIN;	}	_leave(" = 0");	return 0;}/* * calculate the response checksum */static void rxkad_calc_response_checksum(struct rxkad_response *response){	u32 csum = 1000003;	int loop;	u8 *p = (u8 *) response;	for (loop = sizeof(*response); loop > 0; loop--)		csum = csum * 0x10204081 + *p++;	response->encrypted.checksum = htonl(csum);}/* * load a scatterlist with a potentially split-page buffer */static void rxkad_sg_set_buf2(struct scatterlist sg[2],			      void *buf, size_t buflen){	int nsg = 1;	sg_init_table(sg, 2);	sg_set_buf(&sg[0], buf, buflen);	if (sg[0].offset + buflen > PAGE_SIZE) {		/* the buffer was split over two pages */		sg[0].length = PAGE_SIZE - sg[0].offset;		sg_set_buf(&sg[1], buf + sg[0].length, buflen - sg[0].length);		nsg++;	}	sg_mark_end(&sg[nsg - 1]);	ASSERTCMP(sg[0].length + sg[1].length, ==, buflen);}/* * encrypt the response packet */static void rxkad_encrypt_response(struct rxrpc_connection *conn,				   struct rxkad_response *resp,				   const struct rxkad_key *s2){	struct blkcipher_desc desc;	struct rxrpc_crypt iv;	struct scatterlist sg[2];	/* continue encrypting from where we left off */	memcpy(&iv, s2->session_key, sizeof(iv));	desc.tfm = conn->cipher;	desc.info = iv.x;	desc.flags = 0;	rxkad_sg_set_buf2(sg, &resp->encrypted, sizeof(resp->encrypted));	crypto_blkcipher_encrypt_iv(&desc, sg, sg, sizeof(resp->encrypted));}/* * respond to a challenge packet */static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,				      struct sk_buff *skb,				      u32 *_abort_code){	const struct rxrpc_key_payload *payload;	struct rxkad_challenge challenge;	struct rxkad_response resp		__attribute__((aligned(8))); /* must be aligned for crypto */	struct rxrpc_skb_priv *sp;	u32 version, nonce, min_level, abort_code;	int ret;	_enter("{%d,%x}", conn->debug_id, key_serial(conn->key));	if (!conn->key) {		_leave(" = -EPROTO [no key]");		return -EPROTO;	}	ret = key_validate(conn->key);	if (ret < 0) {		*_abort_code = RXKADEXPIRED;		return ret;	}	abort_code = RXKADPACKETSHORT;	sp = rxrpc_skb(skb);	if (skb_copy_bits(skb, 0, &challenge, sizeof(challenge)) < 0)		goto protocol_error;	version = ntohl(challenge.version);	nonce = ntohl(challenge.nonce);	min_level = ntohl(challenge.min_level);	_proto("Rx CHALLENGE %%%u { v=%u n=%u ml=%u }",	       ntohl(sp->hdr.serial), version, nonce, min_level);	abort_code = RXKADINCONSISTENCY;	if (version != RXKAD_VERSION)		goto protocol_error;	abort_code = RXKADLEVELFAIL;	if (conn->security_level < min_level)		goto protocol_error;	payload = conn->key->payload.data;	/* build the response packet */	memset(&resp, 0, sizeof(resp));	resp.version = RXKAD_VERSION;	resp.encrypted.epoch = conn->epoch;	resp.encrypted.cid = conn->cid;	resp.encrypted.securityIndex = htonl(conn->security_ix);	resp.encrypted.call_id[0] =		(conn->channels[0] ? conn->channels[0]->call_id : 0);	resp.encrypted.call_id[1] =		(conn->channels[1] ? conn->channels[1]->call_id : 0);	resp.encrypted.call_id[2] =		(conn->channels[2] ? conn->channels[2]->call_id : 0);	resp.encrypted.call_id[3] =		(conn->channels[3] ? conn->channels[3]->call_id : 0);	resp.encrypted.inc_nonce = htonl(nonce + 1);	resp.encrypted.level = htonl(conn->security_level);	resp.kvno = htonl(payload->k.kvno);	resp.ticket_len = htonl(payload->k.ticket_len);	/* calculate the response checksum and then do the encryption */	rxkad_calc_response_checksum(&resp);	rxkad_encrypt_response(conn, &resp, &payload->k);	return rxkad_send_response(conn, &sp->hdr, &resp, &payload->k);protocol_error:	*_abort_code = abort_code;	_leave(" = -EPROTO [%d]", abort_code);	return -EPROTO;}/* * decrypt the kerberos IV ticket in the response */static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,				void *ticket, size_t ticket_len,				struct rxrpc_crypt *_session_key,				time_t *_expiry,				u32 *_abort_code){	struct blkcipher_desc desc;	struct rxrpc_crypt iv, key;	struct scatterlist sg[1];	struct in_addr addr;	unsigned life;	time_t issue, now;	bool little_endian;	int ret;	u8 *p, *q, *name, *end;	_enter("{%d},{%x}", conn->debug_id, key_serial(conn->server_key));	*_expiry = 0;	ret = key_validate(conn->server_key);	if (ret < 0) {		switch (ret) {		case -EKEYEXPIRED:			*_abort_code = RXKADEXPIRED;			goto error;		default:			*_abort_code = RXKADNOAUTH;			goto error;		}	}	ASSERT(conn->server_key->payload.data != NULL);	ASSERTCMP((unsigned long) ticket & 7UL, ==, 0);	memcpy(&iv, &conn->server_key->type_data, sizeof(iv));	desc.tfm = conn->server_key->payload.data;	desc.info = iv.x;	desc.flags = 0;	sg_init_one(&sg[0], ticket, ticket_len);	crypto_blkcipher_decrypt_iv(&desc, sg, sg, ticket_len);	p = ticket;	end = p + ticket_len;#define Z(size)						\	({						\		u8 *__str = p;				\		q = memchr(p, 0, end - p);		\		if (!q || q - p > (size))		\			goto bad_ticket;		\		for (; p < q; p++)			\			if (!isprint(*p))		\				goto bad_ticket;	\		p++;					\		__str;					\	})	/* extract the ticket flags */	_debug("KIV FLAGS: %x", *p);	little_endian = *p & 1;	p++;	/* extract the authentication name */	name = Z(ANAME_SZ);	_debug("KIV ANAME: %s", name);	/* extract the principal's instance */	name = Z(INST_SZ);	_debug("KIV INST : %s", name);	/* extract the principal's authentication domain */	name = Z(REALM_SZ);	_debug("KIV REALM: %s", name);	if (end - p < 4 + 8 + 4 + 2)		goto bad_ticket;	/* get the IPv4 address of the entity that requested the ticket */	memcpy(&addr, p, sizeof(addr));	p += 4;	_debug("KIV ADDR : "NIPQUAD_FMT, NIPQUAD(addr));	/* get the session key from the ticket */	memcpy(&key, p, sizeof(key));	p += 8;	_debug("KIV KEY  : %08x %08x", ntohl(key.n[0]), ntohl(key.n[1]));	memcpy(_session_key, &key, sizeof(key));	/* get the ticket's lifetime */	life = *p++ * 5 * 60;	_debug("KIV LIFE : %u", life);	/* get the issue time of the ticket */	if (little_endian) {		__le32 stamp;		memcpy(&stamp, p, 4);		issue = le32_to_cpu(stamp);	} else {		__be32 stamp;		memcpy(&stamp, p, 4);		issue = be32_to_cpu(stamp);	}	p += 4;	now = get_seconds();	_debug("KIV ISSUE: %lx [%lx]", issue, now);	/* check the ticket is in date */	if (issue > now) {		*_abort_code = RXKADNOAUTH;		ret = -EKEYREJECTED;		goto error;	}	if (issue < now - life) {		*_abort_code = RXKADEXPIRED;		ret = -EKEYEXPIRED;		goto error;	}	*_expiry = issue + life;	/* get the service name */	name = Z(SNAME_SZ);	_debug("KIV SNAME: %s", name);	/* get the service instance name */	name = Z(INST_SZ);	_debug("KIV SINST: %s", name);	ret = 0;error:	_leave(" = %d", ret);	return ret;bad_ticket:	*_abort_code = RXKADBADTICKET;	ret = -EBADMSG;	goto error;}/* * decrypt the response packet */static void rxkad_decrypt_response(struct rxrpc_connection *conn,				   struct rxkad_response *resp,				   const struct rxrpc_crypt *session_key){	struct blkcipher_desc desc;	struct scatterlist sg[2];	struct rxrpc_crypt iv;	_enter(",,%08x%08x",	       ntohl(session_key->n[0]), ntohl(session_key->n[1]));	ASSERT(rxkad_ci != NULL);	mutex_lock(&rxkad_ci_mutex);	if (crypto_blkcipher_setkey(rxkad_ci, session_key->x,				    sizeof(*session_key)) < 0)		BUG();	memcpy(&iv, session_key, sizeof(iv));	desc.tfm = rxkad_ci;	desc.info = iv.x;	desc.flags = 0;	rxkad_sg_set_buf2(sg, &resp->encrypted, sizeof(resp->encrypted));	crypto_blkcipher_decrypt_iv(&desc, sg, sg, sizeof(resp->encrypted));	mutex_unlock(&rxkad_ci_mutex);	_leave("");}/* * verify a response */static int rxkad_verify_response(struct rxrpc_connection *conn,				 struct sk_buff *skb,				 u32 *_abort_code){	struct rxkad_response response		__attribute__((aligned(8))); /* must be aligned for crypto */	struct rxrpc_skb_priv *sp;	struct rxrpc_crypt session_key;	time_t expiry;	void *ticket;	u32 abort_code, version, kvno, ticket_len, csum, level;	int ret;	_enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key));	abort_code = RXKADPACKETSHORT;	if (skb_copy_bits(skb, 0, &response, sizeof(response)) < 0)		goto protocol_error;	if (!pskb_pull(skb, sizeof(response)))		BUG();	version = ntohl(response.version);	ticket_len = ntohl(response.ticket_len);	kvno = ntohl(response.kvno);	sp = rxrpc_skb(skb);	_proto("Rx RESPONSE %%%u { v=%u kv=%u tl=%u }",	       ntohl(sp->hdr.serial), version, kvno, ticket_len);	abort_code = RXKADINCONSISTENCY;	if (version != RXKAD_VERSION)		goto protocol_error;	abort_code = RXKADTICKETLEN;	if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN)		goto protocol_error;	abort_code = RXKADUNKNOWNKEY;	if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5)		goto protocol_error;	/* extract the kerberos ticket and decrypt and decode it */	ticket = kmalloc(ticket_len, GFP_NOFS);	if (!ticket)		return -ENOMEM;	abort_code = RXKADPACKETSHORT;	if (skb_copy_bits(skb, 0, ticket, ticket_len) < 0)		goto protocol_error_free;	ret = rxkad_decrypt_ticket(conn, ticket, ticket_len, &session_key,				   &expiry, &abort_code);	if (ret < 0) {		*_abort_code = abort_code;		kfree(ticket);		return ret;	}	/* use the session key from inside the ticket to decrypt the	 * response */	rxkad_decrypt_response(conn, &response, &session_key);	abort_code = RXKADSEALEDINCON;	if (response.encrypted.epoch != conn->epoch)		goto protocol_error_free;	if (response.encrypted.cid != conn->cid)		goto protocol_error_free;	if (ntohl(response.encrypted.securityIndex) != conn->security_ix)		goto protocol_error_free;	csum = response.encrypted.checksum;	response.encrypted.checksum = 0;	rxkad_calc_response_checksum(&response);	if (response.encrypted.checksum != csum)		goto protocol_error_free;	if (ntohl(response.encrypted.call_id[0]) > INT_MAX ||	    ntohl(response.encrypted.call_id[1]) > INT_MAX ||	    ntohl(response.encrypted.call_id[2]) > INT_MAX ||	    ntohl(response.encrypted.call_id[3]) > INT_MAX)		goto protocol_error_free;	abort_code = RXKADOUTOFSEQUENCE;	if (response.encrypted.inc_nonce != htonl(conn->security_nonce + 1))		goto protocol_error_free;	abort_code = RXKADLEVELFAIL;	level = ntohl(response.encrypted.level);	if (level > RXRPC_SECURITY_ENCRYPT)		goto protocol_error_free;	conn->security_level = level;	/* create a key to hold the security data and expiration time - after	 * this the connection security can be handled in exactly the same way	 * as for a client connection */	ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno);	if (ret < 0) {		kfree(ticket);		return ret;	}	kfree(ticket);	_leave(" = 0");	return 0;protocol_error_free:	kfree(ticket);protocol_error:	*_abort_code = abort_code;	_leave(" = -EPROTO [%d]", abort_code);	return -EPROTO;}/* * clear the connection security */static void rxkad_clear(struct rxrpc_connection *conn){	_enter("");	if (conn->cipher)		crypto_free_blkcipher(conn->cipher);}/* * RxRPC Kerberos-based security */static struct rxrpc_security rxkad = {	.owner				= THIS_MODULE,	.name				= "rxkad",	.security_index			= RXKAD_VERSION,	.init_connection_security	= rxkad_init_connection_security,	.prime_packet_security		= rxkad_prime_packet_security,	.secure_packet			= rxkad_secure_packet,	.verify_packet			= rxkad_verify_packet,	.issue_challenge		= rxkad_issue_challenge,	.respond_to_challenge		= rxkad_respond_to_challenge,	.verify_response		= rxkad_verify_response,	.clear				= rxkad_clear,};static __init int rxkad_init(void){	_enter("");	/* pin the cipher we need so that the crypto layer doesn't invoke	 * keventd to go get it */	rxkad_ci = crypto_alloc_blkcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC);	if (IS_ERR(rxkad_ci))		return PTR_ERR(rxkad_ci);	return rxrpc_register_security(&rxkad);}module_init(rxkad_init);static __exit void rxkad_exit(void){	_enter("");	rxrpc_unregister_security(&rxkad);	crypto_free_blkcipher(rxkad_ci);}module_exit(rxkad_exit);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -