📄 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/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 + -