Revert "netfilter: xt_qtaguid: fix crash on non-full sks"
[firefly-linux-kernel-4.4.55.git] / net / netfilter / xt_qtaguid.c
index 1f576dab947257631ad8e034a67af7e69615b4f2..e1442bfb668dbaf9ecd3f7f425cc70a49b500a1a 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/file.h>
 #include <linux/inetdevice.h>
 #include <linux/module.h>
+#include <linux/miscdevice.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_qtaguid.h>
 #include <linux/ratelimit.h>
@@ -145,22 +146,22 @@ static bool can_manipulate_uids(void)
 {
        /* root pwnd */
        return in_egroup_p(xt_qtaguid_ctrl_file->gid)
-               || unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_limited)
-               || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid);
+               || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || unlikely(!proc_ctrl_write_limited)
+               || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid));
 }
 
-static bool can_impersonate_uid(uid_t uid)
+static bool can_impersonate_uid(kuid_t uid)
 {
-       return uid == current_fsuid() || can_manipulate_uids();
+       return uid_eq(uid, current_fsuid()) || can_manipulate_uids();
 }
 
-static bool can_read_other_uid_stats(uid_t uid)
+static bool can_read_other_uid_stats(kuid_t uid)
 {
        /* root pwnd */
        return in_egroup_p(xt_qtaguid_stats_file->gid)
-               || unlikely(!current_fsuid()) || uid == current_fsuid()
+               || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || uid_eq(uid, current_fsuid())
                || unlikely(!proc_stats_readall_limited)
-               || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid);
+               || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid));
 }
 
 static inline void dc_add_byte_packets(struct data_counters *counters, int set,
@@ -542,7 +543,7 @@ static void put_utd_entry(struct uid_tag_data *utd_entry)
                         "erase utd_entry=%p uid=%u "
                         "by pid=%u tgid=%u uid=%u\n", __func__,
                         utd_entry, utd_entry->uid,
-                        current->pid, current->tgid, current_fsuid());
+                        current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
                BUG_ON(utd_entry->num_active_tags);
                rb_erase(&utd_entry->node, &uid_tag_data_tree);
                kfree(utd_entry);
@@ -702,7 +703,7 @@ static void pp_iface_stat_line(struct seq_file *m,
 }
 
 struct proc_iface_stat_fmt_info {
-       long fmt;
+       int fmt;
 };
 
 static void *iface_stat_fmt_proc_start(struct seq_file *m, loff_t *pos)
@@ -744,7 +745,7 @@ static int iface_stat_fmt_proc_show(struct seq_file *m, void *v)
 
 
        CT_DEBUG("qtaguid:proc iface_stat_fmt pid=%u tgid=%u uid=%u\n",
-                current->pid, current->tgid, current_fsuid());
+                current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
 
        iface_entry = list_entry(v, struct iface_stat, list);
 
@@ -1290,11 +1291,12 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
                "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n",
                 ifname, uid, sk, direction, proto, bytes);
 
-
+       spin_lock_bh(&iface_stat_list_lock);
        iface_entry = get_iface_entry(ifname);
        if (!iface_entry) {
                pr_err_ratelimited("qtaguid: iface_stat: stat_update() "
                                   "%s not found\n", ifname);
+               spin_unlock_bh(&iface_stat_list_lock);
                return;
        }
        /* It is ok to process data when an iface_entry is inactive */
@@ -1330,8 +1332,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
                 * {0, uid_tag} will also get updated.
                 */
                tag_stat_update(tag_stat_entry, direction, proto, bytes);
-               spin_unlock_bh(&iface_entry->tag_stat_list_lock);
-               return;
+               goto unlock;
        }
 
        /* Loop over tag list under this interface for {0,uid_tag} */
@@ -1371,11 +1372,12 @@ static void if_tag_stat_update(const char *ifname, uid_t uid,
        tag_stat_update(new_tag_stat, direction, proto, bytes);
 unlock:
        spin_unlock_bh(&iface_entry->tag_stat_list_lock);
+       spin_unlock_bh(&iface_stat_list_lock);
 }
 
 static int iface_netdev_event_handler(struct notifier_block *nb,
                                      unsigned long event, void *ptr) {
-       struct net_device *dev = ptr;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
        if (unlikely(module_passive))
                return NOTIFY_DONE;
@@ -1588,10 +1590,10 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb,
 
        switch (par->family) {
        case NFPROTO_IPV6:
-               sk = xt_socket_get6_sk(skb, par);
+               sk = xt_socket_lookup_slow_v6(dev_net(skb->dev), skb, par->in);
                break;
        case NFPROTO_IPV4:
-               sk = xt_socket_get4_sk(skb, par);
+               sk = xt_socket_lookup_slow_v4(dev_net(skb->dev), skb, par->in);
                break;
        default:
                return NULL;
@@ -1605,7 +1607,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb,
                 * "struct inet_timewait_sock" which is missing fields.
                 */
                if (sk->sk_state  == TCP_TIME_WAIT) {
-                       xt_socket_put_sk(sk);
+                       sock_gen_put(sk);
                        sk = NULL;
                }
        }
@@ -1656,7 +1658,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
        const struct file *filp;
        bool got_sock = false;
        struct sock *sk;
-       uid_t sock_uid;
+       kuid_t sock_uid;
        bool res;
        bool set_sk_callback_lock = false;
 
@@ -1723,7 +1725,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
                        sk->sk_socket ? sk->sk_socket->file : (void *)-1LL);
                filp = sk->sk_socket ? sk->sk_socket->file : NULL;
                MT_DEBUG("qtaguid[%d]: filp...uid=%u\n",
-                       par->hooknum, filp ? filp->f_cred->fsuid : -1);
+                       par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1);
        }
 
        if (sk == NULL || sk->sk_socket == NULL) {
@@ -1764,7 +1766,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
         * For now we only do iface stats when the uid-owner is not requested
         */
        if (!(info->match & XT_QTAGUID_UID))
-               account_for_uid(skb, sk, sock_uid, par);
+               account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par);
 
        /*
         * The following two tests fail the match when:
@@ -1772,31 +1774,38 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
         * or id     in range AND    inverted condition requested
         * Thus (!a && b) || (a && !b) == a ^ b
         */
-       if (info->match & XT_QTAGUID_UID)
-               if ((filp->f_cred->fsuid >= info->uid_min &&
-                    filp->f_cred->fsuid <= info->uid_max) ^
+       if (info->match & XT_QTAGUID_UID) {
+               kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min);
+               kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max);
+
+               if ((uid_gte(filp->f_cred->fsuid, uid_min) &&
+                    uid_lte(filp->f_cred->fsuid, uid_max)) ^
                    !(info->invert & XT_QTAGUID_UID)) {
                        MT_DEBUG("qtaguid[%d]: leaving uid not matching\n",
                                 par->hooknum);
                        res = false;
                        goto put_sock_ret_res;
                }
-       if (info->match & XT_QTAGUID_GID)
-               if ((filp->f_cred->fsgid >= info->gid_min &&
-                               filp->f_cred->fsgid <= info->gid_max) ^
+       }
+       if (info->match & XT_QTAGUID_GID) {
+               kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min);
+               kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max);
+
+               if ((gid_gte(filp->f_cred->fsgid, gid_min) &&
+                               gid_lte(filp->f_cred->fsgid, gid_max)) ^
                        !(info->invert & XT_QTAGUID_GID)) {
                        MT_DEBUG("qtaguid[%d]: leaving gid not matching\n",
                                par->hooknum);
                        res = false;
                        goto put_sock_ret_res;
                }
-
+       }
        MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum);
        res = true;
 
 put_sock_ret_res:
        if (got_sock)
-               xt_socket_put_sk(sk);
+               sock_gen_put(sk);
        if (set_sk_callback_lock)
                read_unlock_bh(&sk->sk_callback_lock);
 ret_res:
@@ -1924,7 +1933,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v)
        long f_count;
 
        CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n",
-                current->pid, current->tgid, current_fsuid());
+                current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
 
        if (sock_tag_entry != SEQ_START_TOKEN) {
                uid = get_uid_from_tag(sock_tag_entry->tag);
@@ -1982,7 +1991,8 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v)
 static int ctrl_cmd_delete(const char *input)
 {
        char cmd;
-       uid_t uid;
+       int uid_int;
+       kuid_t uid;
        uid_t entry_uid;
        tag_t acct_tag;
        tag_t tag;
@@ -1996,10 +2006,11 @@ static int ctrl_cmd_delete(const char *input)
        struct tag_ref *tr_entry;
        struct uid_tag_data *utd_entry;
 
-       argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid);
+       argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid_int);
+       uid = make_kuid(&init_user_ns, uid_int);
        CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c "
                 "user_tag=0x%llx uid=%u\n", input, argc, cmd,
-                acct_tag, uid);
+                acct_tag, uid_int);
        if (argc < 2) {
                res = -EINVAL;
                goto err;
@@ -2011,18 +2022,19 @@ static int ctrl_cmd_delete(const char *input)
        }
        if (argc < 3) {
                uid = current_fsuid();
+               uid_int = from_kuid(&init_user_ns, uid);
        } else if (!can_impersonate_uid(uid)) {
                pr_info("qtaguid: ctrl_delete(%s): "
                        "insufficient priv from pid=%u tgid=%u uid=%u\n",
-                       input, current->pid, current->tgid, current_fsuid());
+                       input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
                res = -EPERM;
                goto err;
        }
 
-       tag = combine_atag_with_uid(acct_tag, uid);
+       tag = combine_atag_with_uid(acct_tag, uid_int);
        CT_DEBUG("qtaguid: ctrl_delete(%s): "
                 "looking for tag=0x%llx (uid=%u)\n",
-                input, tag, uid);
+                input, tag, uid_int);
 
        /* Delete socket tags */
        spin_lock_bh(&sock_tag_list_lock);
@@ -2031,7 +2043,7 @@ static int ctrl_cmd_delete(const char *input)
                st_entry = rb_entry(node, struct sock_tag, sock_node);
                entry_uid = get_uid_from_tag(st_entry->tag);
                node = rb_next(node);
-               if (entry_uid != uid)
+               if (entry_uid != uid_int)
                        continue;
 
                CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n",
@@ -2092,7 +2104,7 @@ static int ctrl_cmd_delete(const char *input)
                                 "ts tag=0x%llx (uid=%u)\n",
                                 input, ts_entry->tn.tag, entry_uid);
 
-                       if (entry_uid != uid)
+                       if (entry_uid != uid_int)
                                continue;
                        if (!acct_tag || ts_entry->tn.tag == tag) {
                                CT_DEBUG("qtaguid: ctrl_delete(%s): "
@@ -2121,7 +2133,7 @@ static int ctrl_cmd_delete(const char *input)
                         "utd uid=%u\n",
                         input, entry_uid);
 
-               if (entry_uid != uid)
+               if (entry_uid != uid_int)
                        continue;
                /*
                 * Go over the tag_refs, and those that don't have
@@ -2165,7 +2177,7 @@ static int ctrl_cmd_counter_set(const char *input)
        if (!can_manipulate_uids()) {
                pr_info("qtaguid: ctrl_counterset(%s): "
                        "insufficient priv from pid=%u tgid=%u uid=%u\n",
-                       input, current->pid, current->tgid, current_fsuid());
+                       input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
                res = -EPERM;
                goto err;
        }
@@ -2202,7 +2214,8 @@ static int ctrl_cmd_tag(const char *input)
 {
        char cmd;
        int sock_fd = 0;
-       uid_t uid = 0;
+       kuid_t uid;
+       unsigned int uid_int = 0;
        tag_t acct_tag = make_atag_from_value(0);
        tag_t full_tag;
        struct socket *el_socket;
@@ -2213,10 +2226,11 @@ static int ctrl_cmd_tag(const char *input)
        struct proc_qtu_data *pqd_entry;
 
        /* Unassigned args will get defaulted later. */
-       argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid);
+       argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid_int);
+       uid = make_kuid(&init_user_ns, uid_int);
        CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d "
                 "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd,
-                acct_tag, uid);
+                acct_tag, uid_int);
        if (argc < 2) {
                res = -EINVAL;
                goto err;
@@ -2226,7 +2240,7 @@ static int ctrl_cmd_tag(const char *input)
                pr_info("qtaguid: ctrl_tag(%s): failed to lookup"
                        " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
                        input, sock_fd, res, current->pid, current->tgid,
-                       current_fsuid());
+                       from_kuid(&init_user_ns, current_fsuid()));
                goto err;
        }
        CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n",
@@ -2242,21 +2256,24 @@ static int ctrl_cmd_tag(const char *input)
        CT_DEBUG("qtaguid: ctrl_tag(%s): "
                 "pid=%u tgid=%u uid=%u euid=%u fsuid=%u "
                 "ctrl.gid=%u in_group()=%d in_egroup()=%d\n",
-                input, current->pid, current->tgid, current_uid(),
-                current_euid(), current_fsuid(),
-                xt_qtaguid_ctrl_file->gid,
+                input, current->pid, current->tgid,
+                from_kuid(&init_user_ns, current_uid()),
+                from_kuid(&init_user_ns, current_euid()),
+                from_kuid(&init_user_ns, current_fsuid()),
+                from_kgid(&init_user_ns, xt_qtaguid_ctrl_file->gid),
                 in_group_p(xt_qtaguid_ctrl_file->gid),
                 in_egroup_p(xt_qtaguid_ctrl_file->gid));
        if (argc < 4) {
                uid = current_fsuid();
+               uid_int = from_kuid(&init_user_ns, uid);
        } else if (!can_impersonate_uid(uid)) {
                pr_info("qtaguid: ctrl_tag(%s): "
                        "insufficient priv from pid=%u tgid=%u uid=%u\n",
-                       input, current->pid, current->tgid, current_fsuid());
+                       input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
                res = -EPERM;
                goto err_put;
        }
-       full_tag = combine_atag_with_uid(acct_tag, uid);
+       full_tag = combine_atag_with_uid(acct_tag, uid_int);
 
        spin_lock_bh(&sock_tag_list_lock);
        sock_tag_entry = get_sock_stat_nl(el_socket->sk);
@@ -2303,8 +2320,7 @@ static int ctrl_cmd_tag(const char *input)
                sock_tag_entry->sk = el_socket->sk;
                sock_tag_entry->socket = el_socket;
                sock_tag_entry->pid = current->tgid;
-               sock_tag_entry->tag = combine_atag_with_uid(acct_tag,
-                                                           uid);
+               sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int);
                spin_lock_bh(&uid_tag_data_tree_lock);
                pqd_entry = proc_qtu_data_tree_search(
                        &proc_qtu_data_tree, current->tgid);
@@ -2319,7 +2335,7 @@ static int ctrl_cmd_tag(const char *input)
                                "User space forgot to open /dev/xt_qtaguid? "
                                "pid=%u tgid=%u uid=%u\n", __func__,
                                current->pid, current->tgid,
-                               current_fsuid());
+                               from_kuid(&init_user_ns, current_fsuid()));
                else
                        list_add(&sock_tag_entry->list,
                                 &pqd_entry->sock_tag_list);
@@ -2374,7 +2390,7 @@ static int ctrl_cmd_untag(const char *input)
                pr_info("qtaguid: ctrl_untag(%s): failed to lookup"
                        " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
                        input, sock_fd, res, current->pid, current->tgid,
-                       current_fsuid());
+                       from_kuid(&init_user_ns, current_fsuid()));
                goto err;
        }
        CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n",
@@ -2408,7 +2424,7 @@ static int ctrl_cmd_untag(const char *input)
                pr_warn_once("qtaguid: %s(): "
                             "User space forgot to open /dev/xt_qtaguid? "
                             "pid=%u tgid=%u uid=%u\n", __func__,
-                            current->pid, current->tgid, current_fsuid());
+                            current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
        else
                list_del(&sock_tag_entry->list);
        spin_unlock_bh(&uid_tag_data_tree_lock);
@@ -2451,7 +2467,7 @@ static ssize_t qtaguid_ctrl_parse(const char *input, size_t count)
        ssize_t res;
 
        CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n",
-                input, current->pid, current->tgid, current_fsuid());
+                input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
 
        cmd = input[0];
        /* Collect params for commands */
@@ -2527,25 +2543,25 @@ static void pp_stats_header(struct seq_file *m)
 static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry,
                         int cnt_set)
 {
-       int ret;
        struct data_counters *cnts;
        tag_t tag = ts_entry->tn.tag;
        uid_t stat_uid = get_uid_from_tag(tag);
        struct proc_print_info *ppi = m->private;
        /* Detailed tags are not available to everybody */
-       if (get_atag_from_tag(tag) && !can_read_other_uid_stats(stat_uid)) {
+       if (get_atag_from_tag(tag) && !can_read_other_uid_stats(
+                                               make_kuid(&init_user_ns,stat_uid))) {
                CT_DEBUG("qtaguid: stats line: "
                         "%s 0x%llx %u: insufficient priv "
                         "from pid=%u tgid=%u uid=%u stats.gid=%u\n",
                         ppi->iface_entry->ifname,
                         get_atag_from_tag(tag), stat_uid,
-                        current->pid, current->tgid, current_fsuid(),
-                        xt_qtaguid_stats_file->gid);
+                        current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()),
+                        from_kgid(&init_user_ns,xt_qtaguid_stats_file->gid));
                return 0;
        }
        ppi->item_index++;
        cnts = &ts_entry->counters;
-       ret = seq_printf(m, "%d %s 0x%llx %u %u "
+       seq_printf(m, "%d %s 0x%llx %u %u "
                "%llu %llu "
                "%llu %llu "
                "%llu %llu "
@@ -2575,7 +2591,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry,
                cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets,
                cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes,
                cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets);
-       return ret ?: 1;
+       return seq_has_overflowed(m) ? -ENOSPC : 1;
 }
 
 static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry)
@@ -2742,12 +2758,12 @@ static int qtudev_open(struct inode *inode, struct file *file)
                return 0;
 
        DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n",
-                current->pid, current->tgid, current_fsuid());
+                current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
 
        spin_lock_bh(&uid_tag_data_tree_lock);
 
        /* Look for existing uid data, or alloc one. */
-       utd_entry = get_uid_data(current_fsuid(), &utd_entry_found);
+       utd_entry = get_uid_data(from_kuid(&init_user_ns, current_fsuid()), &utd_entry_found);
        if (IS_ERR_OR_NULL(utd_entry)) {
                res = PTR_ERR(utd_entry);
                goto err_unlock;
@@ -2759,7 +2775,7 @@ static int qtudev_open(struct inode *inode, struct file *file)
        if (pqd_entry) {
                pr_err("qtaguid: qtudev_open(): %u/%u %u "
                       "%s already opened\n",
-                      current->pid, current->tgid, current_fsuid(),
+                      current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()),
                       QTU_DEV_NAME);
                res = -EBUSY;
                goto err_unlock_free_utd;
@@ -2769,7 +2785,7 @@ static int qtudev_open(struct inode *inode, struct file *file)
        if (!new_pqd_entry) {
                pr_err("qtaguid: qtudev_open(): %u/%u %u: "
                       "proc data alloc failed\n",
-                      current->pid, current->tgid, current_fsuid());
+                      current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
                res = -ENOMEM;
                goto err_unlock_free_utd;
        }
@@ -2783,7 +2799,7 @@ static int qtudev_open(struct inode *inode, struct file *file)
 
        spin_unlock_bh(&uid_tag_data_tree_lock);
        DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n",
-                current_fsuid(), new_pqd_entry);
+                from_kuid(&init_user_ns, current_fsuid()), new_pqd_entry);
        file->private_data = new_pqd_entry;
        return 0;