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

📄 xfrm_user.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* xfrm_user.c: User interface to configure xfrm engine. * * Copyright (C) 2002 David S. Miller (davem@redhat.com) * * Changes: *	Mitsuru KANDA @USAGI * 	Kazunori MIYAZAWA @USAGI * 	Kunihiro Ishiguro <kunihiro@ipinfusion.com> * 		IPv6 support * */#include <linux/crypto.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/socket.h>#include <linux/string.h>#include <linux/net.h>#include <linux/skbuff.h>#include <linux/pfkeyv2.h>#include <linux/ipsec.h>#include <linux/init.h>#include <linux/security.h>#include <net/sock.h>#include <net/xfrm.h>#include <net/netlink.h>#include <asm/uaccess.h>#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)#include <linux/in6.h>#endifstatic int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type){	struct nlattr *rt = attrs[type];	struct xfrm_algo *algp;	if (!rt)		return 0;	algp = nla_data(rt);	if (nla_len(rt) < xfrm_alg_len(algp))		return -EINVAL;	switch (type) {	case XFRMA_ALG_AUTH:		if (!algp->alg_key_len &&		    strcmp(algp->alg_name, "digest_null") != 0)			return -EINVAL;		break;	case XFRMA_ALG_CRYPT:		if (!algp->alg_key_len &&		    strcmp(algp->alg_name, "cipher_null") != 0)			return -EINVAL;		break;	case XFRMA_ALG_COMP:		/* Zero length keys are legal.  */		break;	default:		return -EINVAL;	}	algp->alg_name[CRYPTO_MAX_ALG_NAME - 1] = '\0';	return 0;}static void verify_one_addr(struct nlattr **attrs, enum xfrm_attr_type_t type,			   xfrm_address_t **addrp){	struct nlattr *rt = attrs[type];	if (rt && addrp)		*addrp = nla_data(rt);}static inline int verify_sec_ctx_len(struct nlattr **attrs){	struct nlattr *rt = attrs[XFRMA_SEC_CTX];	struct xfrm_user_sec_ctx *uctx;	if (!rt)		return 0;	uctx = nla_data(rt);	if (uctx->len != (sizeof(struct xfrm_user_sec_ctx) + uctx->ctx_len))		return -EINVAL;	return 0;}static int verify_newsa_info(struct xfrm_usersa_info *p,			     struct nlattr **attrs){	int err;	err = -EINVAL;	switch (p->family) {	case AF_INET:		break;	case AF_INET6:#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)		break;#else		err = -EAFNOSUPPORT;		goto out;#endif	default:		goto out;	}	err = -EINVAL;	switch (p->id.proto) {	case IPPROTO_AH:		if (!attrs[XFRMA_ALG_AUTH]	||		    attrs[XFRMA_ALG_CRYPT]	||		    attrs[XFRMA_ALG_COMP])			goto out;		break;	case IPPROTO_ESP:		if ((!attrs[XFRMA_ALG_AUTH] &&		     !attrs[XFRMA_ALG_CRYPT])	||		    attrs[XFRMA_ALG_COMP])			goto out;		break;	case IPPROTO_COMP:		if (!attrs[XFRMA_ALG_COMP]	||		    attrs[XFRMA_ALG_AUTH]	||		    attrs[XFRMA_ALG_CRYPT])			goto out;		break;#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)	case IPPROTO_DSTOPTS:	case IPPROTO_ROUTING:		if (attrs[XFRMA_ALG_COMP]	||		    attrs[XFRMA_ALG_AUTH]	||		    attrs[XFRMA_ALG_CRYPT]	||		    attrs[XFRMA_ENCAP]		||		    attrs[XFRMA_SEC_CTX]	||		    !attrs[XFRMA_COADDR])			goto out;		break;#endif	default:		goto out;	}	if ((err = verify_one_alg(attrs, XFRMA_ALG_AUTH)))		goto out;	if ((err = verify_one_alg(attrs, XFRMA_ALG_CRYPT)))		goto out;	if ((err = verify_one_alg(attrs, XFRMA_ALG_COMP)))		goto out;	if ((err = verify_sec_ctx_len(attrs)))		goto out;	err = -EINVAL;	switch (p->mode) {	case XFRM_MODE_TRANSPORT:	case XFRM_MODE_TUNNEL:	case XFRM_MODE_ROUTEOPTIMIZATION:	case XFRM_MODE_BEET:		break;	default:		goto out;	}	err = 0;out:	return err;}static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,			   struct xfrm_algo_desc *(*get_byname)(char *, int),			   struct nlattr *rta){	struct xfrm_algo *p, *ualg;	struct xfrm_algo_desc *algo;	if (!rta)		return 0;	ualg = nla_data(rta);	algo = get_byname(ualg->alg_name, 1);	if (!algo)		return -ENOSYS;	*props = algo->desc.sadb_alg_id;	p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);	if (!p)		return -ENOMEM;	strcpy(p->alg_name, algo->name);	*algpp = p;	return 0;}static inline int xfrm_user_sec_ctx_size(struct xfrm_sec_ctx *xfrm_ctx){	int len = 0;	if (xfrm_ctx) {		len += sizeof(struct xfrm_user_sec_ctx);		len += xfrm_ctx->ctx_len;	}	return len;}static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p){	memcpy(&x->id, &p->id, sizeof(x->id));	memcpy(&x->sel, &p->sel, sizeof(x->sel));	memcpy(&x->lft, &p->lft, sizeof(x->lft));	x->props.mode = p->mode;	x->props.replay_window = p->replay_window;	x->props.reqid = p->reqid;	x->props.family = p->family;	memcpy(&x->props.saddr, &p->saddr, sizeof(x->props.saddr));	x->props.flags = p->flags;	/*	 * Set inner address family if the KM left it as zero.	 * See comment in validate_tmpl.	 */	if (!x->sel.family)		x->sel.family = p->family;}/* * someday when pfkey also has support, we could have the code * somehow made shareable and move it to xfrm_state.c - JHS **/static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs){	struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];	struct nlattr *lt = attrs[XFRMA_LTIME_VAL];	struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];	struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];	if (rp) {		struct xfrm_replay_state *replay;		replay = nla_data(rp);		memcpy(&x->replay, replay, sizeof(*replay));		memcpy(&x->preplay, replay, sizeof(*replay));	}	if (lt) {		struct xfrm_lifetime_cur *ltime;		ltime = nla_data(lt);		x->curlft.bytes = ltime->bytes;		x->curlft.packets = ltime->packets;		x->curlft.add_time = ltime->add_time;		x->curlft.use_time = ltime->use_time;	}	if (et)		x->replay_maxage = nla_get_u32(et);	if (rt)		x->replay_maxdiff = nla_get_u32(rt);}static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p,					       struct nlattr **attrs,					       int *errp){	struct xfrm_state *x = xfrm_state_alloc();	int err = -ENOMEM;	if (!x)		goto error_no_put;	copy_from_user_state(x, p);	if ((err = attach_one_algo(&x->aalg, &x->props.aalgo,				   xfrm_aalg_get_byname,				   attrs[XFRMA_ALG_AUTH])))		goto error;	if ((err = attach_one_algo(&x->ealg, &x->props.ealgo,				   xfrm_ealg_get_byname,				   attrs[XFRMA_ALG_CRYPT])))		goto error;	if ((err = attach_one_algo(&x->calg, &x->props.calgo,				   xfrm_calg_get_byname,				   attrs[XFRMA_ALG_COMP])))		goto error;	if (attrs[XFRMA_ENCAP]) {		x->encap = kmemdup(nla_data(attrs[XFRMA_ENCAP]),				   sizeof(*x->encap), GFP_KERNEL);		if (x->encap == NULL)			goto error;	}	if (attrs[XFRMA_COADDR]) {		x->coaddr = kmemdup(nla_data(attrs[XFRMA_COADDR]),				    sizeof(*x->coaddr), GFP_KERNEL);		if (x->coaddr == NULL)			goto error;	}	err = xfrm_init_state(x);	if (err)		goto error;	if (attrs[XFRMA_SEC_CTX] &&	    security_xfrm_state_alloc(x, nla_data(attrs[XFRMA_SEC_CTX])))		goto error;	x->km.seq = p->seq;	x->replay_maxdiff = sysctl_xfrm_aevent_rseqth;	/* sysctl_xfrm_aevent_etime is in 100ms units */	x->replay_maxage = (sysctl_xfrm_aevent_etime*HZ)/XFRM_AE_ETH_M;	x->preplay.bitmap = 0;	x->preplay.seq = x->replay.seq+x->replay_maxdiff;	x->preplay.oseq = x->replay.oseq +x->replay_maxdiff;	/* override default values from above */	xfrm_update_ae_params(x, attrs);	return x;error:	x->km.state = XFRM_STATE_DEAD;	xfrm_state_put(x);error_no_put:	*errp = err;	return NULL;}static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh,		struct nlattr **attrs){	struct xfrm_usersa_info *p = nlmsg_data(nlh);	struct xfrm_state *x;	int err;	struct km_event c;	err = verify_newsa_info(p, attrs);	if (err)		return err;	x = xfrm_state_construct(p, attrs, &err);	if (!x)		return err;	xfrm_state_hold(x);	if (nlh->nlmsg_type == XFRM_MSG_NEWSA)		err = xfrm_state_add(x);	else		err = xfrm_state_update(x);	xfrm_audit_state_add(x, err ? 0 : 1, NETLINK_CB(skb).loginuid,			     NETLINK_CB(skb).sid);	if (err < 0) {		x->km.state = XFRM_STATE_DEAD;		__xfrm_state_put(x);		goto out;	}	c.seq = nlh->nlmsg_seq;	c.pid = nlh->nlmsg_pid;	c.event = nlh->nlmsg_type;	km_state_notify(x, &c);out:	xfrm_state_put(x);	return err;}static struct xfrm_state *xfrm_user_state_lookup(struct xfrm_usersa_id *p,						 struct nlattr **attrs,						 int *errp){	struct xfrm_state *x = NULL;	int err;	if (xfrm_id_proto_match(p->proto, IPSEC_PROTO_ANY)) {		err = -ESRCH;		x = xfrm_state_lookup(&p->daddr, p->spi, p->proto, p->family);	} else {		xfrm_address_t *saddr = NULL;		verify_one_addr(attrs, XFRMA_SRCADDR, &saddr);		if (!saddr) {			err = -EINVAL;			goto out;		}		err = -ESRCH;		x = xfrm_state_lookup_byaddr(&p->daddr, saddr, p->proto,					     p->family);	} out:	if (!x && errp)		*errp = err;	return x;}static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh,		struct nlattr **attrs){	struct xfrm_state *x;	int err = -ESRCH;	struct km_event c;	struct xfrm_usersa_id *p = nlmsg_data(nlh);	x = xfrm_user_state_lookup(p, attrs, &err);	if (x == NULL)		return err;	if ((err = security_xfrm_state_delete(x)) != 0)		goto out;	if (xfrm_state_kern(x)) {		err = -EPERM;		goto out;	}	err = xfrm_state_delete(x);	if (err < 0)		goto out;	c.seq = nlh->nlmsg_seq;	c.pid = nlh->nlmsg_pid;	c.event = nlh->nlmsg_type;	km_state_notify(x, &c);out:	xfrm_audit_state_delete(x, err ? 0 : 1, NETLINK_CB(skb).loginuid,				NETLINK_CB(skb).sid);	xfrm_state_put(x);	return err;}static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p){	memcpy(&p->id, &x->id, sizeof(p->id));	memcpy(&p->sel, &x->sel, sizeof(p->sel));	memcpy(&p->lft, &x->lft, sizeof(p->lft));	memcpy(&p->curlft, &x->curlft, sizeof(p->curlft));	memcpy(&p->stats, &x->stats, sizeof(p->stats));	memcpy(&p->saddr, &x->props.saddr, sizeof(p->saddr));	p->mode = x->props.mode;	p->replay_window = x->props.replay_window;	p->reqid = x->props.reqid;	p->family = x->props.family;	p->flags = x->props.flags;	p->seq = x->km.seq;}struct xfrm_dump_info {	struct sk_buff *in_skb;	struct sk_buff *out_skb;	u32 nlmsg_seq;	u16 nlmsg_flags;	int start_idx;	int this_idx;};static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb){	struct xfrm_user_sec_ctx *uctx;	struct nlattr *attr;	int ctx_size = sizeof(*uctx) + s->ctx_len;	attr = nla_reserve(skb, XFRMA_SEC_CTX, ctx_size);	if (attr == NULL)		return -EMSGSIZE;	uctx = nla_data(attr);	uctx->exttype = XFRMA_SEC_CTX;	uctx->len = ctx_size;	uctx->ctx_doi = s->ctx_doi;	uctx->ctx_alg = s->ctx_alg;	uctx->ctx_len = s->ctx_len;	memcpy(uctx + 1, s->ctx_str, s->ctx_len);	return 0;}/* Don't change this without updating xfrm_sa_len! */static int copy_to_user_state_extra(struct xfrm_state *x,				    struct xfrm_usersa_info *p,				    struct sk_buff *skb){	copy_to_user_state(x, p);	if (x->coaddr)		NLA_PUT(skb, XFRMA_COADDR, sizeof(*x->coaddr), x->coaddr);	if (x->lastused)		NLA_PUT_U64(skb, XFRMA_LASTUSED, x->lastused);	if (x->aalg)		NLA_PUT(skb, XFRMA_ALG_AUTH, xfrm_alg_len(x->aalg), x->aalg);	if (x->ealg)		NLA_PUT(skb, XFRMA_ALG_CRYPT, xfrm_alg_len(x->ealg), x->ealg);	if (x->calg)		NLA_PUT(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg);	if (x->encap)		NLA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap);	if (x->security && copy_sec_ctx(x->security, skb) < 0)		goto nla_put_failure;	return 0;nla_put_failure:	return -EMSGSIZE;}static int dump_one_state(struct xfrm_state *x, int count, void *ptr){	struct xfrm_dump_info *sp = ptr;	struct sk_buff *in_skb = sp->in_skb;	struct sk_buff *skb = sp->out_skb;	struct xfrm_usersa_info *p;	struct nlmsghdr *nlh;	int err;	if (sp->this_idx < sp->start_idx)		goto out;	nlh = nlmsg_put(skb, NETLINK_CB(in_skb).pid, sp->nlmsg_seq,			XFRM_MSG_NEWSA, sizeof(*p), sp->nlmsg_flags);	if (nlh == NULL)		return -EMSGSIZE;	p = nlmsg_data(nlh);	err = copy_to_user_state_extra(x, p, skb);	if (err)		goto nla_put_failure;	nlmsg_end(skb, nlh);out:	sp->this_idx++;	return 0;nla_put_failure:	nlmsg_cancel(skb, nlh);	return err;}static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb){	struct xfrm_dump_info info;	info.in_skb = cb->skb;	info.out_skb = skb;	info.nlmsg_seq = cb->nlh->nlmsg_seq;	info.nlmsg_flags = NLM_F_MULTI;	info.this_idx = 0;	info.start_idx = cb->args[0];	(void) xfrm_state_walk(0, dump_one_state, &info);	cb->args[0] = info.this_idx;	return skb->len;}static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,					  struct xfrm_state *x, u32 seq){	struct xfrm_dump_info info;	struct sk_buff *skb;	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);	if (!skb)		return ERR_PTR(-ENOMEM);	info.in_skb = in_skb;	info.out_skb = skb;	info.nlmsg_seq = seq;	info.nlmsg_flags = 0;	info.this_idx = info.start_idx = 0;	if (dump_one_state(x, 0, &info)) {		kfree_skb(skb);		return NULL;	}	return skb;}static inline size_t xfrm_spdinfo_msgsize(void){	return NLMSG_ALIGN(4)	       + nla_total_size(sizeof(struct xfrmu_spdinfo))

⌨️ 快捷键说明

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