📄 xfrm_user.c
字号:
/* 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/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/netlink.h>#include <linux/rtnetlink.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 <asm/uaccess.h>static struct sock *xfrm_nl;static int verify_one_alg(struct rtattr **xfrma, enum xfrm_attr_type_t type){ struct rtattr *rt = xfrma[type - 1]; struct xfrm_algo *algp; if (!rt) return 0; if ((rt->rta_len - sizeof(*rt)) < sizeof(*algp)) return -EINVAL; algp = RTA_DATA(rt); 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 int verify_encap_tmpl(struct rtattr **xfrma){ struct rtattr *rt = xfrma[XFRMA_ENCAP - 1]; struct xfrm_encap_tmpl *encap; if (!rt) return 0; if ((rt->rta_len - sizeof(*rt)) < sizeof(*encap)) return -EINVAL; return 0;}static int verify_newsa_info(struct xfrm_usersa_info *p, struct rtattr **xfrma){ 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 (!xfrma[XFRMA_ALG_AUTH-1] || xfrma[XFRMA_ALG_CRYPT-1] || xfrma[XFRMA_ALG_COMP-1]) goto out; break; case IPPROTO_ESP: if ((!xfrma[XFRMA_ALG_AUTH-1] && !xfrma[XFRMA_ALG_CRYPT-1]) || xfrma[XFRMA_ALG_COMP-1]) goto out; break; case IPPROTO_COMP: if (!xfrma[XFRMA_ALG_COMP-1] || xfrma[XFRMA_ALG_AUTH-1] || xfrma[XFRMA_ALG_CRYPT-1]) goto out; break; default: goto out; }; if ((err = verify_one_alg(xfrma, XFRMA_ALG_AUTH))) goto out; if ((err = verify_one_alg(xfrma, XFRMA_ALG_CRYPT))) goto out; if ((err = verify_one_alg(xfrma, XFRMA_ALG_COMP))) goto out; if ((err = verify_encap_tmpl(xfrma))) goto out; err = -EINVAL; switch (p->mode) { case 0: case 1: 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 *), struct rtattr *u_arg){ struct rtattr *rta = u_arg; struct xfrm_algo *p, *ualg; struct xfrm_algo_desc *algo; if (!rta) return 0; ualg = RTA_DATA(rta); algo = get_byname(ualg->alg_name); if (!algo) return -ENOSYS; *props = algo->desc.sadb_alg_id; p = kmalloc(sizeof(*ualg) + ualg->alg_key_len, GFP_KERNEL); if (!p) return -ENOMEM; memcpy(p, ualg, sizeof(*ualg) + ualg->alg_key_len); *algpp = p; return 0;}static int attach_encap_tmpl(struct xfrm_encap_tmpl **encapp, struct rtattr *u_arg){ struct rtattr *rta = u_arg; struct xfrm_encap_tmpl *p, *uencap; if (!rta) return 0; uencap = RTA_DATA(rta); p = kmalloc(sizeof(*p), GFP_KERNEL); if (!p) return -ENOMEM; memcpy(p, uencap, sizeof(*p)); *encapp = p; return 0;}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; x->props.saddr = p->saddr; x->props.flags = p->flags;}static struct xfrm_state *xfrm_state_construct(struct xfrm_usersa_info *p, struct rtattr **xfrma, 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, xfrma[XFRMA_ALG_AUTH-1]))) goto error; if ((err = attach_one_algo(&x->ealg, &x->props.ealgo, xfrm_ealg_get_byname, xfrma[XFRMA_ALG_CRYPT-1]))) goto error; if ((err = attach_one_algo(&x->calg, &x->props.calgo, xfrm_calg_get_byname, xfrma[XFRMA_ALG_COMP-1]))) goto error; if ((err = attach_encap_tmpl(&x->encap, xfrma[XFRMA_ENCAP-1]))) goto error; err = -ENOENT; x->type = xfrm_get_type(x->id.proto, x->props.family); if (x->type == NULL) goto error; err = x->type->init_state(x, NULL); if (err) goto error; x->curlft.add_time = (unsigned long) xtime.tv_sec; x->km.state = XFRM_STATE_VALID; x->km.seq = p->seq; 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, void **xfrma){ struct xfrm_usersa_info *p = NLMSG_DATA(nlh); struct xfrm_state *x; int err; err = verify_newsa_info(p, (struct rtattr **) xfrma); if (err) return err; xfrm_probe_algs(); x = xfrm_state_construct(p, (struct rtattr **) xfrma, &err); if (!x) return err; if (nlh->nlmsg_type == XFRM_MSG_NEWSA) err = xfrm_state_add(x); else err = xfrm_state_update(x); if (err < 0) { x->km.state = XFRM_STATE_DEAD; xfrm_state_put(x); } return err;}static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma){ struct xfrm_state *x; struct xfrm_usersa_id *p = NLMSG_DATA(nlh); x = xfrm_state_lookup(&p->daddr, p->spi, p->proto, p->family); if (x == NULL) return -ESRCH; if (xfrm_state_kern(x)) { xfrm_state_put(x); return -EPERM; } xfrm_state_delete(x); xfrm_state_put(x); return 0;}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)); p->saddr = x->props.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; int start_idx; int this_idx;};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; unsigned char *b = skb->tail; 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)); nlh->nlmsg_flags = 0; p = NLMSG_DATA(nlh); copy_to_user_state(x, p); if (x->aalg) RTA_PUT(skb, XFRMA_ALG_AUTH, sizeof(*(x->aalg))+(x->aalg->alg_key_len+7)/8, x->aalg); if (x->ealg) RTA_PUT(skb, XFRMA_ALG_CRYPT, sizeof(*(x->ealg))+(x->ealg->alg_key_len+7)/8, x->ealg); if (x->calg) RTA_PUT(skb, XFRMA_ALG_COMP, sizeof(*(x->calg)), x->calg); if (x->encap) RTA_PUT(skb, XFRMA_ENCAP, sizeof(*x->encap), x->encap); nlh->nlmsg_len = skb->tail - b;out: sp->this_idx++; return 0;nlmsg_failure:rtattr_failure: skb_trim(skb, b - skb->data); return -1;}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.this_idx = 0; info.start_idx = cb->args[0]; (void) xfrm_state_walk(IPSEC_PROTO_ANY, 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 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); if (!skb) return ERR_PTR(-ENOMEM); NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid; info.in_skb = in_skb; info.out_skb = skb; info.nlmsg_seq = seq; info.this_idx = info.start_idx = 0; if (dump_one_state(x, 0, &info)) { kfree_skb(skb); return NULL; } return skb;}static int xfrm_get_sa(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma){ struct xfrm_usersa_id *p = NLMSG_DATA(nlh); struct xfrm_state *x; struct sk_buff *resp_skb; int err; x = xfrm_state_lookup(&p->daddr, p->spi, p->proto, p->family); err = -ESRCH; if (x == NULL) goto out_noput; resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); if (IS_ERR(resp_skb)) { err = PTR_ERR(resp_skb); } else { err = netlink_unicast(xfrm_nl, resp_skb, NETLINK_CB(skb).pid, MSG_DONTWAIT); } xfrm_state_put(x);out_noput: return err;}static int verify_userspi_info(struct xfrm_userspi_info *p){ switch (p->info.id.proto) { case IPPROTO_AH: case IPPROTO_ESP: break; case IPPROTO_COMP: /* IPCOMP spi is 16-bits. */ if (p->max >= 0x10000) return -EINVAL; break; default: return -EINVAL; }; if (p->min > p->max) return -EINVAL; return 0;}static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh, void **xfrma){ struct xfrm_state *x; struct xfrm_userspi_info *p; struct sk_buff *resp_skb; xfrm_address_t *daddr; int family; int err; p = NLMSG_DATA(nlh); err = verify_userspi_info(p); if (err) goto out_noput; family = p->info.family; daddr = &p->info.id.daddr; x = NULL; if (p->info.seq) { x = xfrm_find_acq_byseq(p->info.seq); if (x && xfrm_addr_cmp(&x->id.daddr, daddr, family)) { xfrm_state_put(x); x = NULL; } } if (!x) x = xfrm_find_acq(p->info.mode, p->info.reqid, p->info.id.proto, daddr, &p->info.saddr, 1, family); err = -ENOENT; if (x == NULL) goto out_noput; resp_skb = ERR_PTR(-ENOENT); spin_lock_bh(&x->lock); if (x->km.state != XFRM_STATE_DEAD) { xfrm_alloc_spi(x, htonl(p->min), htonl(p->max)); if (x->id.spi) resp_skb = xfrm_state_netlink(skb, x, nlh->nlmsg_seq); } spin_unlock_bh(&x->lock); if (IS_ERR(resp_skb)) { err = PTR_ERR(resp_skb); goto out; } err = netlink_unicast(xfrm_nl, resp_skb, NETLINK_CB(skb).pid, MSG_DONTWAIT);out: xfrm_state_put(x);out_noput: return err;}static int verify_policy_dir(__u8 dir){ switch (dir) { case XFRM_POLICY_IN: case XFRM_POLICY_OUT: case XFRM_POLICY_FWD: break; default: return -EINVAL; }; return 0;}static int verify_newpolicy_info(struct xfrm_userpolicy_info *p){ switch (p->share) { case XFRM_SHARE_ANY: case XFRM_SHARE_SESSION: case XFRM_SHARE_USER: case XFRM_SHARE_UNIQUE: break; default: return -EINVAL; }; switch (p->action) { case XFRM_POLICY_ALLOW: case XFRM_POLICY_BLOCK: break; default: return -EINVAL; }; switch (p->sel.family) { case AF_INET: break; case AF_INET6:#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) break;#else return -EAFNOSUPPORT;#endif default: return -EINVAL; }; return verify_policy_dir(p->dir);}static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, int nr){ int i; xp->xfrm_nr = nr; for (i = 0; i < nr; i++, ut++) { struct xfrm_tmpl *t = &xp->xfrm_vec[i]; memcpy(&t->id, &ut->id, sizeof(struct xfrm_id)); memcpy(&t->saddr, &ut->saddr, sizeof(xfrm_address_t)); t->reqid = ut->reqid; t->mode = ut->mode; t->share = ut->share; t->optional = ut->optional; t->aalgos = ut->aalgos; t->ealgos = ut->ealgos; t->calgos = ut->calgos; }}static int copy_from_user_tmpl(struct xfrm_policy *pol, struct rtattr **xfrma){ struct rtattr *rt = xfrma[XFRMA_TMPL-1]; struct xfrm_user_tmpl *utmpl; int nr; if (!rt) { pol->xfrm_nr = 0; } else { nr = (rt->rta_len - sizeof(*rt)) / sizeof(*utmpl); if (nr > XFRM_MAX_DEPTH) return -EINVAL; copy_templates(pol, RTA_DATA(rt), nr); } return 0;}static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p){ xp->priority = p->priority; xp->index = p->index;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -