From: marta Date: Mon, 2 Nov 2009 15:11:31 +0000 (+0000) Subject: More work on the uid lookup. X-Git-Tag: ipfw-0.9-5~8 X-Git-Url: http://git.onelab.eu/?p=ipfw.git;a=commitdiff_plain;h=7a117b941275e2471bf4e035c1042640f1d24f04 More work on the uid lookup. --- diff --git a/dummynet/ip_fw2.c b/dummynet/ip_fw2.c index 4e8961d..039e084 100644 --- a/dummynet/ip_fw2.c +++ b/dummynet/ip_fw2.c @@ -2032,6 +2032,12 @@ check_uidgid(ipfw_insn_u32 *insn, int proto, struct ifnet *oif, return 0; } + /* check for slice_id matching */ + if (insn->o.opcode == O_GID) { + if (filp->GID != (gid_t)insn->d[0]) + return 0; + } + return 1; #else /* FreeBSD original code */ diff --git a/dummynet/ipfw2_mod.c b/dummynet/ipfw2_mod.c index 0da1904..8910cbc 100644 --- a/dummynet/ipfw2_mod.c +++ b/dummynet/ipfw2_mod.c @@ -47,6 +47,7 @@ #include #include /* sizeof struct mbuf */ +#include /* NGROUPS */ #ifdef __linux__ #include @@ -67,7 +68,9 @@ #include /* in_addr */ #include /* ip_fw_ctl_t, ip_fw_chk_t */ #include /* ip_dn_ctl_t, ip_dn_io_t */ -#include /* PFIL_IN, PFIL_OUT */ +#include /* PFIL_IN, PFIL_OUT */ +#include /* inet_lookup */ +#include /* inet_iif */ /* * Here we allocate some global variables used in the firewall. @@ -80,7 +83,7 @@ ip_fw_chk_t *ip_fw_chk_ptr; void (*bridge_dn_p)(struct mbuf *, struct ifnet *); -/* +/*--- * Glue code to implement the registration of children with the parent. * Each child should call my_mod_register() when linking, so that * module_init() and module_exit() can call init_children() and @@ -96,6 +99,19 @@ struct mod_args { static unsigned int mod_idx; static struct mod_args mods[10]; /* hard limit to 10 modules */ +/* + * Data structure to cache our ucred related + * information. This structure only gets used if + * the user specified UID/GID based constraints in + * a firewall rule. + */ +struct ip_fw_ugid { + gid_t fw_groups[NGROUPS]; + int fw_ngroups; + uid_t fw_uid; + int fw_prid; +}; + /* * my_mod_register should be called automatically as the init * functions in the submodules. Unfortunately this compiler/linker @@ -140,9 +156,9 @@ fini_children(void) mods[i].mod->evhand(NULL, MOD_UNLOAD, mods[i].mod->priv); } } -/* end of module bindinghelper functions */ +/*--- end of module bindinghelper functions ---*/ -/* +/*--- * Control hooks: * ipfw_ctl_h() is a wrapper for linux to FreeBSD sockopt call convention. * then call the ipfw handler in order to manage requests. @@ -233,6 +249,25 @@ static struct nf_sockopt_ops ipfw_sockopts = { #endif }; +/*---- + * We need a number of macros to adapt to the various APIs in + * different linux versions. Among them: + * + * - the hook names change between macros (NF_IP*) and enum NF_INET_* + * + * - the second argument to the netfilter hook is + * struct sk_buff ** in kernels <= 2.6.22 + * struct sk_buff * in kernels > 2.6.22 + * + * - NF_STOP is not defined before 2.6 so we remap it to NF_ACCEPT + * + * - the packet descriptor passed to the queue handler is + * struct nf_info in kernels <= 2.6.24 + * struct nf_queue_entry in kernels <= 2.6.24 + * + * - the arguments to the queue handler also change; + */ + /* * declare hook to grab packets from the netfilter interface. * The NF_* names change in different versions of linux, in some @@ -251,7 +286,8 @@ static struct nf_sockopt_ops ipfw_sockopts = { * To make life simple, we queue everything and then do all the * decision in the queue handler. * - * XXX note that in 2.4 the skbuf is passed as sk_buff** + * XXX note that in 2.4 and up to 2.6.22 the skbuf is passed as sk_buff** + * so we have an #ifdef to set the proper argument type. */ static unsigned int call_ipfw(unsigned int __unused hooknum, @@ -273,7 +309,13 @@ call_ipfw(unsigned int __unused hooknum, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) +/* + * nf_queue_entry is a recent addition, in previous versions + * of the code the struct is called nf_info. + */ #define nf_queue_entry nf_info /* for simplicity */ + +/* also, 2.4 and perhaps something else have different arguments */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* unsure on the exact boundary */ /* on 2.4 we use nf_info */ #define QH_ARGS struct sk_buff *skb, struct nf_info *info, void *data @@ -339,6 +381,7 @@ ipfw2_queue_handler(QH_ARGS) #else m->m_data = skb_network_header(skb); #endif + /* XXX add the interface */ if (info->hook == NF_IP_PRE_ROUTING) { ret = ipfw_check_in(NULL, &m, info->indev, PFIL_IN, NULL); @@ -378,6 +421,7 @@ netisr_dispatch(int num, struct mbuf *m) struct sk_buff *skb = m->m_skb; /* always used */ m_freem(m); + KASSERT((info != NULL), ("%s info null!\n", __FUNCTION__)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) // XXX above 2.6.x ? __net_timestamp(skb); /* update timestamp */ @@ -396,6 +440,88 @@ ip_output(struct mbuf *m, struct mbuf __unused *opt, return 0; } +/* + * socket lookup function for linux. + * This code is used to associate uid, gid, jail/xid to packets, + * and store the info in a cache *ugp where they can be accessed quickly. + * The function returns 1 if the info is found, -1 otherwise. + * + * We do this only on selected protocols: TCP, ... + * + * Note- for locally generated, outgoing packets we don't need to + * do a lookup because the sk_buff already points to the socket where + * the info is. + */ +extern struct inet_hashinfo tcp_hashinfo; +int +linux_lookup(const int proto, const __be32 saddr, const __be16 sport, + const __be32 daddr, const __be16 dport, + struct sk_buff *skb, int dir, struct ip_fw_ugid *ugp) +{ + struct sock *sk; + int ret = -1; /* default return value */ + + if (proto != IPPROTO_TCP) + return -1; + + if ((dir ? (void *)skb->dst : (void *)skb->dev) == NULL) { + panic(" -- this should not happen\n"); + return -1; + } + + /* + * inet_lookup above 2.6.24 has an additional 'net' parameter + * so we use a macro to conditionally supply it. + * Also we need to switch dst and src depending on the direction. + */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24) +#define _OPT_NET_ARG +#else /* 2.6.25 and above */ +#define _OPT_NET_ARG dev_net(skb->dev), +#endif + + sk = (dir) ? + inet_lookup(_OPT_NET_ARG &tcp_hashinfo, + daddr, dport, saddr, sport, // matches outgoing for server sockets + net_iif(skb)) : + inet_lookup(_OPT_NET_ARG &tcp_hashinfo, + saddr, sport, daddr, dport, // matches incoming for server sockets + skb->dev->ifindex); + +#undef _OPT_NET_ARG + /* no match, nothing to be done */ + if (sk == NULL) + return -1; + + /* + * On a match, sk is returned with a refcount. + * In tcp states less that TCP_TIME_WAIT sk references a struct sock + * which is what we want, + * otherwise it references a struct inet_timewait_sock which does + * not point to credentials. + * Once again we need conditional code because the UID and GID + * location changes between the two kernels. + */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) +/* use the current's real uid/gid */ +#define _CURR_UID f_uid +#define _CURR_GID f_gid +#else /* 2.6.29 and above */ +/* use the current's file access real uid/gid */ +#define _CURR_UID f_cred->fsuid +#define _CURR_GID f_cred->fsgid +#endif + if (sk->sk_state < TCP_TIME_WAIT && sk->sk_socket && sk->sk_socket->file) { + ugp->fw_uid = sk->sk_socket->file->_CURR_UID; + ret = 1; + } + sock_put(sk); +#undef _CURR_UID +#undef _CURR_GID + + //printf("%s dir %d skb->dst %p skb->dev %p ret %d\n", __FUNCTION__, dir, skb->dst, skb->dev, ret); + return ret; +} /* * Now prepare to hook the various functions. @@ -541,5 +667,5 @@ ipfw_module_exit(void) #ifdef __linux__ module_init(ipfw_module_init) module_exit(ipfw_module_exit) -MODULE_LICENSE("GPL"); /* mandatory */ +MODULE_LICENSE("Dual BSD/GPL"); /* the code here is all BSD. */ #endif