📄 af_key.c
字号:
/* * net/key/af_key.c An implementation of PF_KEYv2 sockets. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Authors: Maxim Giryaev <gem@asplinux.ru> * David S. Miller <davem@redhat.com> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * Kunihiro Ishiguro <kunihiro@ipinfusion.com> * Kazunori MIYAZAWA / USAGI Project <miyazawa@linux-ipv6.org> * Derek Atkins <derek@ihtfp.com> */#include <linux/capability.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/socket.h>#include <linux/pfkeyv2.h>#include <linux/ipsec.h>#include <linux/skbuff.h>#include <linux/rtnetlink.h>#include <linux/in.h>#include <linux/in6.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <net/net_namespace.h>#include <net/xfrm.h>#include <net/sock.h>#define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x))#define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x))/* List of all pfkey sockets. */static HLIST_HEAD(pfkey_table);static DECLARE_WAIT_QUEUE_HEAD(pfkey_table_wait);static DEFINE_RWLOCK(pfkey_table_lock);static atomic_t pfkey_table_users = ATOMIC_INIT(0);static atomic_t pfkey_socks_nr = ATOMIC_INIT(0);struct pfkey_sock { /* struct sock must be the first member of struct pfkey_sock */ struct sock sk; int registered; int promisc;};static inline struct pfkey_sock *pfkey_sk(struct sock *sk){ return (struct pfkey_sock *)sk;}static void pfkey_sock_destruct(struct sock *sk){ skb_queue_purge(&sk->sk_receive_queue); if (!sock_flag(sk, SOCK_DEAD)) { printk("Attempt to release alive pfkey socket: %p\n", sk); return; } BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc)); BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc)); atomic_dec(&pfkey_socks_nr);}static void pfkey_table_grab(void){ write_lock_bh(&pfkey_table_lock); if (atomic_read(&pfkey_table_users)) { DECLARE_WAITQUEUE(wait, current); add_wait_queue_exclusive(&pfkey_table_wait, &wait); for(;;) { set_current_state(TASK_UNINTERRUPTIBLE); if (atomic_read(&pfkey_table_users) == 0) break; write_unlock_bh(&pfkey_table_lock); schedule(); write_lock_bh(&pfkey_table_lock); } __set_current_state(TASK_RUNNING); remove_wait_queue(&pfkey_table_wait, &wait); }}static __inline__ void pfkey_table_ungrab(void){ write_unlock_bh(&pfkey_table_lock); wake_up(&pfkey_table_wait);}static __inline__ void pfkey_lock_table(void){ /* read_lock() synchronizes us to pfkey_table_grab */ read_lock(&pfkey_table_lock); atomic_inc(&pfkey_table_users); read_unlock(&pfkey_table_lock);}static __inline__ void pfkey_unlock_table(void){ if (atomic_dec_and_test(&pfkey_table_users)) wake_up(&pfkey_table_wait);}static const struct proto_ops pfkey_ops;static void pfkey_insert(struct sock *sk){ pfkey_table_grab(); sk_add_node(sk, &pfkey_table); pfkey_table_ungrab();}static void pfkey_remove(struct sock *sk){ pfkey_table_grab(); sk_del_node_init(sk); pfkey_table_ungrab();}static struct proto key_proto = { .name = "KEY", .owner = THIS_MODULE, .obj_size = sizeof(struct pfkey_sock),};static int pfkey_create(struct net *net, struct socket *sock, int protocol){ struct sock *sk; int err; if (net != &init_net) return -EAFNOSUPPORT; if (!capable(CAP_NET_ADMIN)) return -EPERM; if (sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; if (protocol != PF_KEY_V2) return -EPROTONOSUPPORT; err = -ENOMEM; sk = sk_alloc(net, PF_KEY, GFP_KERNEL, &key_proto); if (sk == NULL) goto out; sock->ops = &pfkey_ops; sock_init_data(sock, sk); sk->sk_family = PF_KEY; sk->sk_destruct = pfkey_sock_destruct; atomic_inc(&pfkey_socks_nr); pfkey_insert(sk); return 0;out: return err;}static int pfkey_release(struct socket *sock){ struct sock *sk = sock->sk; if (!sk) return 0; pfkey_remove(sk); sock_orphan(sk); sock->sk = NULL; skb_queue_purge(&sk->sk_write_queue); sock_put(sk); return 0;}static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2, gfp_t allocation, struct sock *sk){ int err = -ENOBUFS; sock_hold(sk); if (*skb2 == NULL) { if (atomic_read(&skb->users) != 1) { *skb2 = skb_clone(skb, allocation); } else { *skb2 = skb; atomic_inc(&skb->users); } } if (*skb2 != NULL) { if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) { skb_orphan(*skb2); skb_set_owner_r(*skb2, sk); skb_queue_tail(&sk->sk_receive_queue, *skb2); sk->sk_data_ready(sk, (*skb2)->len); *skb2 = NULL; err = 0; } } sock_put(sk); return err;}/* Send SKB to all pfkey sockets matching selected criteria. */#define BROADCAST_ALL 0#define BROADCAST_ONE 1#define BROADCAST_REGISTERED 2#define BROADCAST_PROMISC_ONLY 4static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, int broadcast_flags, struct sock *one_sk){ struct sock *sk; struct hlist_node *node; struct sk_buff *skb2 = NULL; int err = -ESRCH; /* XXX Do we need something like netlink_overrun? I think * XXX PF_KEY socket apps will not mind current behavior. */ if (!skb) return -ENOMEM; pfkey_lock_table(); sk_for_each(sk, node, &pfkey_table) { struct pfkey_sock *pfk = pfkey_sk(sk); int err2; /* Yes, it means that if you are meant to receive this * pfkey message you receive it twice as promiscuous * socket. */ if (pfk->promisc) pfkey_broadcast_one(skb, &skb2, allocation, sk); /* the exact target will be processed later */ if (sk == one_sk) continue; if (broadcast_flags != BROADCAST_ALL) { if (broadcast_flags & BROADCAST_PROMISC_ONLY) continue; if ((broadcast_flags & BROADCAST_REGISTERED) && !pfk->registered) continue; if (broadcast_flags & BROADCAST_ONE) continue; } err2 = pfkey_broadcast_one(skb, &skb2, allocation, sk); /* Error is cleare after succecful sending to at least one * registered KM */ if ((broadcast_flags & BROADCAST_REGISTERED) && err) err = err2; } pfkey_unlock_table(); if (one_sk != NULL) err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk); if (skb2) kfree_skb(skb2); kfree_skb(skb); return err;}static inline void pfkey_hdr_dup(struct sadb_msg *new, struct sadb_msg *orig){ *new = *orig;}static int pfkey_error(struct sadb_msg *orig, int err, struct sock *sk){ struct sk_buff *skb = alloc_skb(sizeof(struct sadb_msg) + 16, GFP_KERNEL); struct sadb_msg *hdr; if (!skb) return -ENOBUFS; /* Woe be to the platform trying to support PFKEY yet * having normal errnos outside the 1-255 range, inclusive. */ err = -err; if (err == ERESTARTSYS || err == ERESTARTNOHAND || err == ERESTARTNOINTR) err = EINTR; if (err >= 512) err = EINVAL; BUG_ON(err <= 0 || err >= 256); hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg)); pfkey_hdr_dup(hdr, orig); hdr->sadb_msg_errno = (uint8_t) err; hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk); return 0;}static u8 sadb_ext_min_len[] = { [SADB_EXT_RESERVED] = (u8) 0, [SADB_EXT_SA] = (u8) sizeof(struct sadb_sa), [SADB_EXT_LIFETIME_CURRENT] = (u8) sizeof(struct sadb_lifetime), [SADB_EXT_LIFETIME_HARD] = (u8) sizeof(struct sadb_lifetime), [SADB_EXT_LIFETIME_SOFT] = (u8) sizeof(struct sadb_lifetime), [SADB_EXT_ADDRESS_SRC] = (u8) sizeof(struct sadb_address), [SADB_EXT_ADDRESS_DST] = (u8) sizeof(struct sadb_address), [SADB_EXT_ADDRESS_PROXY] = (u8) sizeof(struct sadb_address), [SADB_EXT_KEY_AUTH] = (u8) sizeof(struct sadb_key), [SADB_EXT_KEY_ENCRYPT] = (u8) sizeof(struct sadb_key), [SADB_EXT_IDENTITY_SRC] = (u8) sizeof(struct sadb_ident), [SADB_EXT_IDENTITY_DST] = (u8) sizeof(struct sadb_ident), [SADB_EXT_SENSITIVITY] = (u8) sizeof(struct sadb_sens), [SADB_EXT_PROPOSAL] = (u8) sizeof(struct sadb_prop), [SADB_EXT_SUPPORTED_AUTH] = (u8) sizeof(struct sadb_supported), [SADB_EXT_SUPPORTED_ENCRYPT] = (u8) sizeof(struct sadb_supported), [SADB_EXT_SPIRANGE] = (u8) sizeof(struct sadb_spirange), [SADB_X_EXT_KMPRIVATE] = (u8) sizeof(struct sadb_x_kmprivate), [SADB_X_EXT_POLICY] = (u8) sizeof(struct sadb_x_policy), [SADB_X_EXT_SA2] = (u8) sizeof(struct sadb_x_sa2), [SADB_X_EXT_NAT_T_TYPE] = (u8) sizeof(struct sadb_x_nat_t_type), [SADB_X_EXT_NAT_T_SPORT] = (u8) sizeof(struct sadb_x_nat_t_port), [SADB_X_EXT_NAT_T_DPORT] = (u8) sizeof(struct sadb_x_nat_t_port), [SADB_X_EXT_NAT_T_OA] = (u8) sizeof(struct sadb_address), [SADB_X_EXT_SEC_CTX] = (u8) sizeof(struct sadb_x_sec_ctx),};/* Verify sadb_address_{len,prefixlen} against sa_family. */static int verify_address_len(void *p){ struct sadb_address *sp = p; struct sockaddr *addr = (struct sockaddr *)(sp + 1); struct sockaddr_in *sin;#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) struct sockaddr_in6 *sin6;#endif int len; switch (addr->sa_family) { case AF_INET: len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin), sizeof(uint64_t)); if (sp->sadb_address_len != len || sp->sadb_address_prefixlen > 32) return -EINVAL; break;#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case AF_INET6: len = DIV_ROUND_UP(sizeof(*sp) + sizeof(*sin6), sizeof(uint64_t)); if (sp->sadb_address_len != len || sp->sadb_address_prefixlen > 128) return -EINVAL; break;#endif default: /* It is user using kernel to keep track of security * associations for another protocol, such as * OSPF/RSVP/RIPV2/MIP. It is user's job to verify * lengths. * * XXX Actually, association/policy database is not yet * XXX able to cope with arbitrary sockaddr families. * XXX When it can, remove this -EINVAL. -DaveM */ return -EINVAL; break; } return 0;}static inline int pfkey_sec_ctx_len(struct sadb_x_sec_ctx *sec_ctx){ return DIV_ROUND_UP(sizeof(struct sadb_x_sec_ctx) + sec_ctx->sadb_x_ctx_len, sizeof(uint64_t));}static inline int verify_sec_ctx_len(void *p){ struct sadb_x_sec_ctx *sec_ctx = (struct sadb_x_sec_ctx *)p; int len = sec_ctx->sadb_x_ctx_len; if (len > PAGE_SIZE) return -EINVAL; len = pfkey_sec_ctx_len(sec_ctx); if (sec_ctx->sadb_x_sec_len != len) return -EINVAL; return 0;}static inline struct xfrm_user_sec_ctx *pfkey_sadb2xfrm_user_sec_ctx(struct sadb_x_sec_ctx *sec_ctx){ struct xfrm_user_sec_ctx *uctx = NULL; int ctx_size = sec_ctx->sadb_x_ctx_len; uctx = kmalloc((sizeof(*uctx)+ctx_size), GFP_KERNEL); if (!uctx) return NULL; uctx->len = pfkey_sec_ctx_len(sec_ctx); uctx->exttype = sec_ctx->sadb_x_sec_exttype; uctx->ctx_doi = sec_ctx->sadb_x_ctx_doi; uctx->ctx_alg = sec_ctx->sadb_x_ctx_alg; uctx->ctx_len = sec_ctx->sadb_x_ctx_len; memcpy(uctx + 1, sec_ctx + 1, uctx->ctx_len); return uctx;}static int present_and_same_family(struct sadb_address *src, struct sadb_address *dst){ struct sockaddr *s_addr, *d_addr; if (!src || !dst) return 0; s_addr = (struct sockaddr *)(src + 1); d_addr = (struct sockaddr *)(dst + 1); if (s_addr->sa_family != d_addr->sa_family) return 0; if (s_addr->sa_family != AF_INET#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) && s_addr->sa_family != AF_INET6#endif ) return 0; return 1;}static int parse_exthdrs(struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs){ char *p = (char *) hdr; int len = skb->len; len -= sizeof(*hdr); p += sizeof(*hdr); while (len > 0) { struct sadb_ext *ehdr = (struct sadb_ext *) p; uint16_t ext_type; int ext_len; ext_len = ehdr->sadb_ext_len; ext_len *= sizeof(uint64_t); ext_type = ehdr->sadb_ext_type; if (ext_len < sizeof(uint64_t) || ext_len > len || ext_type == SADB_EXT_RESERVED) return -EINVAL; if (ext_type <= SADB_EXT_MAX) { int min = (int) sadb_ext_min_len[ext_type]; if (ext_len < min) return -EINVAL; if (ext_hdrs[ext_type-1] != NULL) return -EINVAL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -