diff options
Diffstat (limited to 'net/key')
-rw-r--r-- | net/key/af_key.c | 589 |
1 files changed, 506 insertions, 83 deletions
diff --git a/net/key/af_key.c b/net/key/af_key.c index 5dd5094659a..1c58204d767 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -152,7 +152,7 @@ static int pfkey_create(struct socket *sock, int protocol) sk = sk_alloc(PF_KEY, GFP_KERNEL, &key_proto, 1); if (sk == NULL) goto out; - + sock->ops = &pfkey_ops; sock_init_data(sock, sk); @@ -487,7 +487,7 @@ static int parse_exthdrs(struct sk_buff *skb, struct sadb_msg *hdr, void **ext_h ext_type == SADB_X_EXT_NAT_T_OA) { if (verify_address_len(p)) return -EINVAL; - } + } if (ext_type == SADB_X_EXT_SEC_CTX) { if (verify_sec_ctx_len(p)) return -EINVAL; @@ -556,12 +556,12 @@ static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr, { switch (((struct sockaddr*)(addr + 1))->sa_family) { case AF_INET: - xaddr->a4 = + xaddr->a4 = ((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr; return AF_INET; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case AF_INET6: - memcpy(xaddr->a6, + memcpy(xaddr->a6, &((struct sockaddr_in6 *)(addr + 1))->sin6_addr, sizeof(struct in6_addr)); return AF_INET6; @@ -659,11 +659,11 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, /* base, SA, (lifetime (HSC),) address(SD), (address(P),) key(AE), (identity(SD),) (sensitivity)> */ - size = sizeof(struct sadb_msg) +sizeof(struct sadb_sa) + + size = sizeof(struct sadb_msg) +sizeof(struct sadb_sa) + sizeof(struct sadb_lifetime) + ((hsc & 1) ? sizeof(struct sadb_lifetime) : 0) + ((hsc & 2) ? sizeof(struct sadb_lifetime) : 0) + - sizeof(struct sadb_address)*2 + + sizeof(struct sadb_address)*2 + sockaddr_size*2 + sizeof(struct sadb_x_sa2); @@ -685,13 +685,13 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, if (add_keys) { if (x->aalg && x->aalg->alg_key_len) { - auth_key_size = - PFKEY_ALIGN8((x->aalg->alg_key_len + 7) / 8); + auth_key_size = + PFKEY_ALIGN8((x->aalg->alg_key_len + 7) / 8); size += sizeof(struct sadb_key) + auth_key_size; } if (x->ealg && x->ealg->alg_key_len) { - encrypt_key_size = - PFKEY_ALIGN8((x->ealg->alg_key_len+7) / 8); + encrypt_key_size = + PFKEY_ALIGN8((x->ealg->alg_key_len+7) / 8); size += sizeof(struct sadb_key) + encrypt_key_size; } } @@ -758,7 +758,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, /* hard time */ if (hsc & 2) { - lifetime = (struct sadb_lifetime *) skb_put(skb, + lifetime = (struct sadb_lifetime *) skb_put(skb, sizeof(struct sadb_lifetime)); lifetime->sadb_lifetime_len = sizeof(struct sadb_lifetime)/sizeof(uint64_t); @@ -770,7 +770,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, } /* soft time */ if (hsc & 1) { - lifetime = (struct sadb_lifetime *) skb_put(skb, + lifetime = (struct sadb_lifetime *) skb_put(skb, sizeof(struct sadb_lifetime)); lifetime->sadb_lifetime_len = sizeof(struct sadb_lifetime)/sizeof(uint64_t); @@ -791,16 +791,16 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, lifetime->sadb_lifetime_addtime = x->curlft.add_time; lifetime->sadb_lifetime_usetime = x->curlft.use_time; /* src address */ - addr = (struct sadb_address*) skb_put(skb, + addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); - addr->sadb_address_len = + addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; - /* "if the ports are non-zero, then the sadb_address_proto field, - normally zero, MUST be filled in with the transport + /* "if the ports are non-zero, then the sadb_address_proto field, + normally zero, MUST be filled in with the transport protocol's number." - RFC2367 */ - addr->sadb_address_proto = 0; + addr->sadb_address_proto = 0; addr->sadb_address_reserved = 0; if (x->props.family == AF_INET) { addr->sadb_address_prefixlen = 32; @@ -813,29 +813,29 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (x->props.family == AF_INET6) { - addr->sadb_address_prefixlen = 128; + addr->sadb_address_prefixlen = 128; sin6 = (struct sockaddr_in6 *) (addr + 1); sin6->sin6_family = AF_INET6; sin6->sin6_port = 0; sin6->sin6_flowinfo = 0; - memcpy(&sin6->sin6_addr, x->props.saddr.a6, + memcpy(&sin6->sin6_addr, x->props.saddr.a6, sizeof(struct in6_addr)); sin6->sin6_scope_id = 0; - } + } #endif else BUG(); /* dst address */ - addr = (struct sadb_address*) skb_put(skb, + addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); - addr->sadb_address_len = + addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST; - addr->sadb_address_proto = 0; - addr->sadb_address_prefixlen = 32; /* XXX */ + addr->sadb_address_proto = 0; + addr->sadb_address_prefixlen = 32; /* XXX */ addr->sadb_address_reserved = 0; if (x->props.family == AF_INET) { sin = (struct sockaddr_in *) (addr + 1); @@ -845,9 +845,9 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); if (x->sel.saddr.a4 != x->props.saddr.a4) { - addr = (struct sadb_address*) skb_put(skb, + addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); - addr->sadb_address_len = + addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY; @@ -876,9 +876,9 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, if (memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof(struct in6_addr))) { - addr = (struct sadb_address *) skb_put(skb, + addr = (struct sadb_address *) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); - addr->sadb_address_len = + addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY; @@ -902,7 +902,7 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, /* auth key */ if (add_keys && auth_key_size) { - key = (struct sadb_key *) skb_put(skb, + key = (struct sadb_key *) skb_put(skb, sizeof(struct sadb_key)+auth_key_size); key->sadb_key_len = (sizeof(struct sadb_key) + auth_key_size) / sizeof(uint64_t); @@ -913,14 +913,14 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, } /* encrypt key */ if (add_keys && encrypt_key_size) { - key = (struct sadb_key *) skb_put(skb, + key = (struct sadb_key *) skb_put(skb, sizeof(struct sadb_key)+encrypt_key_size); - key->sadb_key_len = (sizeof(struct sadb_key) + + key->sadb_key_len = (sizeof(struct sadb_key) + encrypt_key_size) / sizeof(uint64_t); key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT; key->sadb_key_bits = x->ealg->alg_key_len; key->sadb_key_reserved = 0; - memcpy(key + 1, x->ealg->alg_key, + memcpy(key + 1, x->ealg->alg_key, (x->ealg->alg_key_len+7)/8); } @@ -979,17 +979,17 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, return skb; } -static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, +static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, void **ext_hdrs) { - struct xfrm_state *x; + struct xfrm_state *x; struct sadb_lifetime *lifetime; struct sadb_sa *sa; struct sadb_key *key; struct sadb_x_sec_ctx *sec_ctx; uint16_t proto; int err; - + sa = (struct sadb_sa *) ext_hdrs[SADB_EXT_SA-1]; if (!sa || @@ -1022,7 +1022,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, SADB_SASTATE_MATURE and the kernel MUST return an error if this is not true. - However, KAME setkey always uses SADB_SASTATE_LARVAL. + However, KAME setkey always uses SADB_SASTATE_LARVAL. Hence, we have to _ignore_ sadb_sa_state, which is also reasonable. */ if (sa->sadb_sa_auth > SADB_AALG_MAX || @@ -1144,13 +1144,13 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr, } /* x->algo.flags = sa->sadb_sa_flags; */ - x->props.family = pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_SRC-1], + x->props.family = pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_SRC-1], &x->props.saddr); if (!x->props.family) { err = -EAFNOSUPPORT; goto out; } - pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1], + pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1], &x->id.daddr); if (ext_hdrs[SADB_X_EXT_SA2-1]) { @@ -1410,7 +1410,7 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, struct km_event c; xfrm_probe_algs(); - + x = pfkey_msg2xfrm_state(hdr, ext_hdrs); if (IS_ERR(x)) return PTR_ERR(x); @@ -1530,13 +1530,13 @@ static struct sk_buff *compose_sadb_supported(struct sadb_msg *orig, auth_len *= sizeof(struct sadb_alg); auth_len += sizeof(struct sadb_supported); } - + enc_len = xfrm_count_enc_supported(); if (enc_len) { enc_len *= sizeof(struct sadb_alg); enc_len += sizeof(struct sadb_supported); } - + len = enc_len + auth_len + sizeof(struct sadb_msg); skb = alloc_skb(len + 16, allocation); @@ -1605,7 +1605,7 @@ static int pfkey_register(struct sock *sk, struct sk_buff *skb, struct sadb_msg } xfrm_probe_algs(); - + supp_skb = compose_sadb_supported(hdr, GFP_KERNEL); if (!supp_skb) { if (hdr->sadb_msg_satype != SADB_SATYPE_UNSPEC) @@ -1856,7 +1856,7 @@ static int pfkey_xfrm_policy2msg_size(struct xfrm_policy *xp) return sizeof(struct sadb_msg) + (sizeof(struct sadb_lifetime) * 3) + - (sizeof(struct sadb_address) * 2) + + (sizeof(struct sadb_address) * 2) + (sockaddr_size * 2) + sizeof(struct sadb_x_policy) + (xp->xfrm_nr * sizeof(struct sadb_x_ipsecrequest)) + @@ -1904,9 +1904,9 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i memset(hdr, 0, size); /* XXX do we need this ? */ /* src address */ - addr = (struct sadb_address*) skb_put(skb, + addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); - addr->sadb_address_len = + addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; @@ -1936,14 +1936,14 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i BUG(); /* dst address */ - addr = (struct sadb_address*) skb_put(skb, + addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST; addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto); - addr->sadb_address_prefixlen = xp->selector.prefixlen_d; + addr->sadb_address_prefixlen = xp->selector.prefixlen_d; addr->sadb_address_reserved = 0; if (xp->family == AF_INET) { sin = (struct sockaddr_in *) (addr + 1); @@ -1967,7 +1967,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i BUG(); /* hard time */ - lifetime = (struct sadb_lifetime *) skb_put(skb, + lifetime = (struct sadb_lifetime *) skb_put(skb, sizeof(struct sadb_lifetime)); lifetime->sadb_lifetime_len = sizeof(struct sadb_lifetime)/sizeof(uint64_t); @@ -1977,7 +1977,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i lifetime->sadb_lifetime_addtime = xp->lft.hard_add_expires_seconds; lifetime->sadb_lifetime_usetime = xp->lft.hard_use_expires_seconds; /* soft time */ - lifetime = (struct sadb_lifetime *) skb_put(skb, + lifetime = (struct sadb_lifetime *) skb_put(skb, sizeof(struct sadb_lifetime)); lifetime->sadb_lifetime_len = sizeof(struct sadb_lifetime)/sizeof(uint64_t); @@ -1987,7 +1987,7 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i lifetime->sadb_lifetime_addtime = xp->lft.soft_add_expires_seconds; lifetime->sadb_lifetime_usetime = xp->lft.soft_use_expires_seconds; /* current time */ - lifetime = (struct sadb_lifetime *) skb_put(skb, + lifetime = (struct sadb_lifetime *) skb_put(skb, sizeof(struct sadb_lifetime)); lifetime->sadb_lifetime_len = sizeof(struct sadb_lifetime)/sizeof(uint64_t); @@ -2019,8 +2019,8 @@ static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, i req_size = sizeof(struct sadb_x_ipsecrequest); if (t->mode == XFRM_MODE_TUNNEL) req_size += ((t->encap_family == AF_INET ? - sizeof(struct sockaddr_in) : - sizeof(struct sockaddr_in6)) * 2); + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6)) * 2); else size -= 2*socklen; rq = (void*)skb_put(skb, req_size); @@ -2150,7 +2150,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW); xp->priority = pol->sadb_x_policy_priority; - sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1], + sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1], xp->family = pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr); if (!xp->family) { err = -EINVAL; @@ -2163,7 +2163,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h if (xp->selector.sport) xp->selector.sport_mask = htons(0xffff); - sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1], + sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1], pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.daddr); xp->selector.prefixlen_d = sa->sadb_address_prefixlen; @@ -2224,7 +2224,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h if (hdr->sadb_msg_type == SADB_X_SPDUPDATE) c.event = XFRM_MSG_UPDPOLICY; - else + else c.event = XFRM_MSG_NEWPOLICY; c.seq = hdr->sadb_msg_seq; @@ -2261,7 +2261,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg memset(&sel, 0, sizeof(sel)); - sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1], + sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1], sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr); sel.prefixlen_s = sa->sadb_address_prefixlen; sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto); @@ -2269,7 +2269,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg if (sel.sport) sel.sport_mask = htons(0xffff); - sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1], + sa = ext_hdrs[SADB_EXT_ADDRESS_DST-1], pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr); sel.prefixlen_d = sa->sadb_address_prefixlen; sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto); @@ -2297,16 +2297,17 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg &sel, tmp.security, 1); security_xfrm_policy_free(&tmp); - xfrm_audit_log(audit_get_loginuid(current->audit_context), 0, - AUDIT_MAC_IPSEC_DELSPD, (xp) ? 1 : 0, xp, NULL); - if (xp == NULL) return -ENOENT; - err = 0; + err = security_xfrm_policy_delete(xp); + + xfrm_audit_log(audit_get_loginuid(current->audit_context), 0, + AUDIT_MAC_IPSEC_DELSPD, err ? 0 : 1, xp, NULL); - if ((err = security_xfrm_policy_delete(xp))) + if (err) goto out; + c.seq = hdr->sadb_msg_seq; c.pid = hdr->sadb_msg_pid; c.event = XFRM_MSG_DELPOLICY; @@ -2345,6 +2346,196 @@ out: return err; } +#ifdef CONFIG_NET_KEY_MIGRATE +static int pfkey_sockaddr_pair_size(sa_family_t family) +{ + switch (family) { + case AF_INET: + return PFKEY_ALIGN8(sizeof(struct sockaddr_in) * 2); +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + return PFKEY_ALIGN8(sizeof(struct sockaddr_in6) * 2); +#endif + default: + return 0; + } + /* NOTREACHED */ +} + +static int parse_sockaddr_pair(struct sadb_x_ipsecrequest *rq, + xfrm_address_t *saddr, xfrm_address_t *daddr, + u16 *family) +{ + struct sockaddr *sa = (struct sockaddr *)(rq + 1); + if (rq->sadb_x_ipsecrequest_len < + pfkey_sockaddr_pair_size(sa->sa_family)) + return -EINVAL; + + switch (sa->sa_family) { + case AF_INET: + { + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)sa; + if ((sin+1)->sin_family != AF_INET) + return -EINVAL; + memcpy(&saddr->a4, &sin->sin_addr, sizeof(saddr->a4)); + sin++; + memcpy(&daddr->a4, &sin->sin_addr, sizeof(daddr->a4)); + *family = AF_INET; + break; + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)sa; + if ((sin6+1)->sin6_family != AF_INET6) + return -EINVAL; + memcpy(&saddr->a6, &sin6->sin6_addr, + sizeof(saddr->a6)); + sin6++; + memcpy(&daddr->a6, &sin6->sin6_addr, + sizeof(daddr->a6)); + *family = AF_INET6; + break; + } +#endif + default: + return -EINVAL; + } + + return 0; +} + +static int ipsecrequests_to_migrate(struct sadb_x_ipsecrequest *rq1, int len, + struct xfrm_migrate *m) +{ + int err; + struct sadb_x_ipsecrequest *rq2; + + if (len <= sizeof(struct sadb_x_ipsecrequest) || + len < rq1->sadb_x_ipsecrequest_len) + return -EINVAL; + + /* old endoints */ + err = parse_sockaddr_pair(rq1, &m->old_saddr, &m->old_daddr, + &m->old_family); + if (err) + return err; + + rq2 = (struct sadb_x_ipsecrequest *)((u8 *)rq1 + rq1->sadb_x_ipsecrequest_len); + len -= rq1->sadb_x_ipsecrequest_len; + + if (len <= sizeof(struct sadb_x_ipsecrequest) || + len < rq2->sadb_x_ipsecrequest_len) + return -EINVAL; + + /* new endpoints */ + err = parse_sockaddr_pair(rq2, &m->new_saddr, &m->new_daddr, + &m->new_family); + if (err) + return err; + + if (rq1->sadb_x_ipsecrequest_proto != rq2->sadb_x_ipsecrequest_proto || + rq1->sadb_x_ipsecrequest_mode != rq2->sadb_x_ipsecrequest_mode || + rq1->sadb_x_ipsecrequest_reqid != rq2->sadb_x_ipsecrequest_reqid) + return -EINVAL; + + m->proto = rq1->sadb_x_ipsecrequest_proto; + m->mode = rq1->sadb_x_ipsecrequest_mode - 1; + m->reqid = rq1->sadb_x_ipsecrequest_reqid; + + return ((int)(rq1->sadb_x_ipsecrequest_len + + rq2->sadb_x_ipsecrequest_len)); +} + +static int pfkey_migrate(struct sock *sk, struct sk_buff *skb, + struct sadb_msg *hdr, void **ext_hdrs) +{ + int i, len, ret, err = -EINVAL; + u8 dir; + struct sadb_address *sa; + struct sadb_x_policy *pol; + struct sadb_x_ipsecrequest *rq; + struct xfrm_selector sel; + struct xfrm_migrate m[XFRM_MAX_DEPTH]; + + if (!present_and_same_family(ext_hdrs[SADB_EXT_ADDRESS_SRC - 1], + ext_hdrs[SADB_EXT_ADDRESS_DST - 1]) || + !ext_hdrs[SADB_X_EXT_POLICY - 1]) { + err = -EINVAL; + goto out; + } + + pol = ext_hdrs[SADB_X_EXT_POLICY - 1]; + if (!pol) { + err = -EINVAL; + goto out; + } + + if (pol->sadb_x_policy_dir >= IPSEC_DIR_MAX) { + err = -EINVAL; + goto out; + } + + dir = pol->sadb_x_policy_dir - 1; + memset(&sel, 0, sizeof(sel)); + + /* set source address info of selector */ + sa = ext_hdrs[SADB_EXT_ADDRESS_SRC - 1]; + sel.family = pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr); + sel.prefixlen_s = sa->sadb_address_prefixlen; + sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto); + sel.sport = ((struct sockaddr_in *)(sa + 1))->sin_port; + if (sel.sport) + sel.sport_mask = ~0; + + /* set destination address info of selector */ + sa = ext_hdrs[SADB_EXT_ADDRESS_DST - 1], + pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr); + sel.prefixlen_d = sa->sadb_address_prefixlen; + sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto); + sel.dport = ((struct sockaddr_in *)(sa + 1))->sin_port; + if (sel.dport) + sel.dport_mask = ~0; + + rq = (struct sadb_x_ipsecrequest *)(pol + 1); + + /* extract ipsecrequests */ + i = 0; + len = pol->sadb_x_policy_len * 8 - sizeof(struct sadb_x_policy); + + while (len > 0 && i < XFRM_MAX_DEPTH) { + ret = ipsecrequests_to_migrate(rq, len, &m[i]); + if (ret < 0) { + err = ret; + goto out; + } else { + rq = (struct sadb_x_ipsecrequest *)((u8 *)rq + ret); + len -= ret; + i++; + } + } + + if (!i || len > 0) { + err = -EINVAL; + goto out; + } + + return xfrm_migrate(&sel, dir, XFRM_POLICY_TYPE_MAIN, m, i); + + out: + return err; +} +#else +static int pfkey_migrate(struct sock *sk, struct sk_buff *skb, + struct sadb_msg *hdr, void **ext_hdrs) +{ + return -ENOPROTOOPT; +} +#endif + + static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs) { unsigned int dir; @@ -2473,6 +2664,7 @@ static pfkey_handler pfkey_funcs[SADB_MAX + 1] = { [SADB_X_SPDFLUSH] = pfkey_spdflush, [SADB_X_SPDSETIDX] = pfkey_spdadd, [SADB_X_SPDDELETE2] = pfkey_spdget, + [SADB_X_MIGRATE] = pfkey_migrate, }; static int pfkey_process(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr) @@ -2552,15 +2744,15 @@ static int count_esp_combs(struct xfrm_tmpl *t) struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i); if (!ealg) break; - + if (!(ealg_tmpl_set(t, ealg) && ealg->available)) continue; - + for (k = 1; ; k++) { struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k); if (!aalg) break; - + if (aalg_tmpl_set(t, aalg) && aalg->available) sz += sizeof(struct sadb_comb); } @@ -2615,10 +2807,10 @@ static void dump_esp_combs(struct sk_buff *skb, struct xfrm_tmpl *t) struct xfrm_algo_desc *ealg = xfrm_ealg_get_byidx(i); if (!ealg) break; - + if (!(ealg_tmpl_set(t, ealg) && ealg->available)) continue; - + for (k = 1; ; k++) { struct sadb_comb *c; struct xfrm_algo_desc *aalg = xfrm_aalg_get_byidx(k); @@ -2750,7 +2942,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct struct sadb_x_sec_ctx *sec_ctx; struct xfrm_sec_ctx *xfrm_ctx; int ctx_size = 0; - + sockaddr_size = pfkey_sockaddr_size(x->props.family); if (!sockaddr_size) return -EINVAL; @@ -2759,7 +2951,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct (sizeof(struct sadb_address) * 2) + (sockaddr_size * 2) + sizeof(struct sadb_x_policy); - + if (x->id.proto == IPPROTO_AH) size += count_ah_combs(t); else if (x->id.proto == IPPROTO_ESP) @@ -2773,7 +2965,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct skb = alloc_skb(size + 16, GFP_ATOMIC); if (skb == NULL) return -ENOMEM; - + hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg)); hdr->sadb_msg_version = PF_KEY_V2; hdr->sadb_msg_type = SADB_ACQUIRE; @@ -2785,9 +2977,9 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct hdr->sadb_msg_pid = 0; /* src address */ - addr = (struct sadb_address*) skb_put(skb, + addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); - addr->sadb_address_len = + addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; @@ -2817,9 +3009,9 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct #endif else BUG(); - + /* dst address */ - addr = (struct sadb_address*) skb_put(skb, + addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ @@ -2828,7 +3020,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct addr->sadb_address_proto = 0; addr->sadb_address_reserved = 0; if (x->props.family == AF_INET) { - addr->sadb_address_prefixlen = 32; + addr->sadb_address_prefixlen = 32; sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; @@ -2838,7 +3030,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (x->props.family == AF_INET6) { - addr->sadb_address_prefixlen = 128; + addr->sadb_address_prefixlen = 128; sin6 = (struct sockaddr_in6 *) (addr + 1); sin6->sin6_family = AF_INET6; @@ -2883,7 +3075,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct } static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt, - u8 *data, int len, int *dir) + u8 *data, int len, int *dir) { struct xfrm_policy *xp; struct sadb_x_policy *pol = (struct sadb_x_policy*)data; @@ -3002,17 +3194,17 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, * HDR | SA | ADDRESS_SRC (old addr) | NAT_T_SPORT (old port) | * ADDRESS_DST (new addr) | NAT_T_DPORT (new port) */ - + size = sizeof(struct sadb_msg) + sizeof(struct sadb_sa) + (sizeof(struct sadb_address) * 2) + (sockaddr_size * 2) + (sizeof(struct sadb_x_nat_t_port) * 2); - + skb = alloc_skb(size + 16, GFP_ATOMIC); if (skb == NULL) return -ENOMEM; - + hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg)); hdr->sadb_msg_version = PF_KEY_V2; hdr->sadb_msg_type = SADB_X_NAT_T_NEW_MAPPING; @@ -3037,7 +3229,7 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, /* ADDRESS_SRC (old addr) */ addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); - addr->sadb_address_len = + addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; @@ -3078,7 +3270,7 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, /* ADDRESS_DST (new addr) */ addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); - addr->sadb_address_len = + addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST; @@ -3118,6 +3310,236 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL); } +#ifdef CONFIG_NET_KEY_MIGRATE +static int set_sadb_address(struct sk_buff *skb, int sasize, int type, + struct xfrm_selector *sel) +{ + struct sadb_address *addr; + struct sockaddr_in *sin; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct sockaddr_in6 *sin6; +#endif + addr = (struct sadb_address *)skb_put(skb, sizeof(struct sadb_address) + sasize); + addr->sadb_address_len = (sizeof(struct sadb_address) + sasize)/8; + addr->sadb_address_exttype = type; + addr->sadb_address_proto = sel->proto; + addr->sadb_address_reserved = 0; + + switch (type) { + case SADB_EXT_ADDRESS_SRC: + if (sel->family == AF_INET) { + addr->sadb_address_prefixlen = sel->prefixlen_s; + sin = (struct sockaddr_in *)(addr + 1); + sin->sin_family = AF_INET; + memcpy(&sin->sin_addr.s_addr, &sel->saddr, + sizeof(sin->sin_addr.s_addr)); + sin->sin_port = 0; + memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else if (sel->family == AF_INET6) { + addr->sadb_address_prefixlen = sel->prefixlen_s; + sin6 = (struct sockaddr_in6 *)(addr + 1); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + memcpy(&sin6->sin6_addr.s6_addr, &sel->saddr, + sizeof(sin6->sin6_addr.s6_addr)); + } +#endif + break; + case SADB_EXT_ADDRESS_DST: + if (sel->family == AF_INET) { + addr->sadb_address_prefixlen = sel->prefixlen_d; + sin = (struct sockaddr_in *)(addr + 1); + sin->sin_family = AF_INET; + memcpy(&sin->sin_addr.s_addr, &sel->daddr, + sizeof(sin->sin_addr.s_addr)); + sin->sin_port = 0; + memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + else if (sel->family == AF_INET6) { + addr->sadb_address_prefixlen = sel->prefixlen_d; + sin6 = (struct sockaddr_in6 *)(addr + 1); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + memcpy(&sin6->sin6_addr.s6_addr, &sel->daddr, + sizeof(sin6->sin6_addr.s6_addr)); + } +#endif + break; + default: + return -EINVAL; + } + + return 0; +} + +static int set_ipsecrequest(struct sk_buff *skb, + uint8_t proto, uint8_t mode, int level, + uint32_t reqid, uint8_t family, + xfrm_address_t *src, xfrm_address_t *dst) +{ + struct sadb_x_ipsecrequest *rq; + struct sockaddr_in *sin; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct sockaddr_in6 *sin6; +#endif + int size_req; + + size_req = sizeof(struct sadb_x_ipsecrequest) + + pfkey_sockaddr_pair_size(family); + + rq = (struct sadb_x_ipsecrequest *)skb_put(skb, size_req); + memset(rq, 0, size_req); + rq->sadb_x_ipsecrequest_len = size_req; + rq->sadb_x_ipsecrequest_proto = proto; + rq->sadb_x_ipsecrequest_mode = mode; + rq->sadb_x_ipsecrequest_level = level; + rq->sadb_x_ipsecrequest_reqid = reqid; + + switch (family) { + case AF_INET: + sin = (struct sockaddr_in *)(rq + 1); + sin->sin_family = AF_INET; + memcpy(&sin->sin_addr.s_addr, src, + sizeof(sin->sin_addr.s_addr)); + sin++; + sin->sin_family = AF_INET; + memcpy(&sin->sin_addr.s_addr, dst, + sizeof(sin->sin_addr.s_addr)); + break; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + sin6 = (struct sockaddr_in6 *)(rq + 1); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + memcpy(&sin6->sin6_addr.s6_addr, src, + sizeof(sin6->sin6_addr.s6_addr)); + sin6++; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + sin6->sin6_flowinfo = 0; + sin6->sin6_scope_id = 0; + memcpy(&sin6->sin6_addr.s6_addr, dst, + sizeof(sin6->sin6_addr.s6_addr)); + break; +#endif + default: + return -EINVAL; + } + + return 0; +} +#endif + +#ifdef CONFIG_NET_KEY_MIGRATE +static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, + struct xfrm_migrate *m, int num_bundles) +{ + int i; + int sasize_sel; + int size = 0; + int size_pol = 0; + struct sk_buff *skb; + struct sadb_msg *hdr; + struct sadb_x_policy *pol; + struct xfrm_migrate *mp; + + if (type != XFRM_POLICY_TYPE_MAIN) + return 0; + + if (num_bundles <= 0 || num_bundles > XFRM_MAX_DEPTH) + return -EINVAL; + + /* selector */ + sasize_sel = pfkey_sockaddr_size(sel->family); + if (!sasize_sel) + return -EINVAL; + size += (sizeof(struct sadb_address) + sasize_sel) * 2; + + /* policy info */ + size_pol += sizeof(struct sadb_x_policy); + + /* ipsecrequests */ + for (i = 0, mp = m; i < num_bundles; i++, mp++) { + /* old locator pair */ + size_pol += sizeof(struct sadb_x_ipsecrequest) + + pfkey_sockaddr_pair_size(mp->old_family); + /* new locator pair */ + size_pol += sizeof(struct sadb_x_ipsecrequest) + + pfkey_sockaddr_pair_size(mp->new_family); + } + + size += sizeof(struct sadb_msg) + size_pol; + + /* alloc buffer */ + skb = alloc_skb(size, GFP_ATOMIC); + if (skb == NULL) + return -ENOMEM; + + hdr = (struct sadb_msg *)skb_put(skb, sizeof(struct sadb_msg)); + hdr->sadb_msg_version = PF_KEY_V2; + hdr->sadb_msg_type = SADB_X_MIGRATE; + hdr->sadb_msg_satype = pfkey_proto2satype(m->proto); + hdr->sadb_msg_len = size / 8; + hdr->sadb_msg_errno = 0; + hdr->sadb_msg_reserved = 0; + hdr->sadb_msg_seq = 0; + hdr->sadb_msg_pid = 0; + + /* selector src */ + set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_SRC, sel); + + /* selector dst */ + set_sadb_address(skb, sasize_sel, SADB_EXT_ADDRESS_DST, sel); + + /* policy information */ + pol = (struct sadb_x_policy *)skb_put(skb, sizeof(struct sadb_x_policy)); + pol->sadb_x_policy_len = size_pol / 8; + pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY; + pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC; + pol->sadb_x_policy_dir = dir + 1; + pol->sadb_x_policy_id = 0; + pol->sadb_x_policy_priority = 0; + + for (i = 0, mp = m; i < num_bundles; i++, mp++) { + /* old ipsecrequest */ + if (set_ipsecrequest(skb, mp->proto, mp->mode + 1, + (mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE), + mp->reqid, mp->old_family, + &mp->old_saddr, &mp->old_daddr) < 0) { + return -EINVAL; + } + + /* new ipsecrequest */ + if (set_ipsecrequest(skb, mp->proto, mp->mode + 1, + (mp->reqid ? IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_REQUIRE), + mp->reqid, mp->new_family, + &mp->new_saddr, &mp->new_daddr) < 0) { + return -EINVAL; + } + } + + /* broadcast migrate message to sockets */ + pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL); + + return 0; +} +#else +static int pfkey_send_migrate(struct xfrm_selector *sel, u8 dir, u8 type, + struct xfrm_migrate *m, int num_bundles) +{ + return -ENOPROTOOPT; +} +#endif + static int pfkey_sendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len) { @@ -3253,7 +3675,7 @@ static int pfkey_read_proc(char *buffer, char **start, off_t offset, ); buffer[len++] = '\n'; - + pos = begin + len; if (pos < offset) { len = 0; @@ -3287,6 +3709,7 @@ static struct xfrm_mgr pfkeyv2_mgr = .compile_policy = pfkey_compile_policy, .new_mapping = pfkey_send_new_mapping, .notify_policy = pfkey_send_policy_notify, + .migrate = pfkey_send_migrate, }; static void __exit ipsec_pfkey_exit(void) |