#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>
{
/* 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,
"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);
}
}
-static int read_proc_u64(struct file *file, char __user *buf,
+static ssize_t read_proc_u64(struct file *file, char __user *buf,
size_t size, loff_t *ppos)
{
uint64_t *valuep = PDE_DATA(file_inode(file));
return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size);
}
-static int read_proc_bool(struct file *file, char __user *buf,
+static ssize_t read_proc_bool(struct file *file, char __user *buf,
size_t size, loff_t *ppos)
{
bool *valuep = PDE_DATA(file_inode(file));
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);
"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 */
* {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} */
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;
if (!s)
return -ENOMEM;
- s->fmt = (int)PDE_DATA(inode);
+ s->fmt = (uintptr_t)PDE_DATA(inode);
return 0;
}
.open = proc_iface_stat_fmt_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_private,
};
static int __init iface_stat_init(struct proc_dir_entry *parent_procdir)
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;
* "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;
}
}
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;
if (unlikely(module_passive))
return (info->match ^ info->invert) == 0;
MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n",
par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par));
if (sk != NULL) {
+ set_sk_callback_lock = true;
+ read_lock_bh(&sk->sk_callback_lock);
MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n",
par->hooknum, sk, sk->sk_socket,
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) {
* 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:
* 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:
MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res);
return res;
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);
"match_found_no_sk_in_ct=%llu "
"match_no_sk=%llu "
"match_no_sk_file=%llu\n",
- atomic64_read(&qtu_events.sockets_tagged),
- atomic64_read(&qtu_events.sockets_untagged),
- atomic64_read(&qtu_events.counter_set_changes),
- atomic64_read(&qtu_events.delete_cmds),
- atomic64_read(&qtu_events.iface_events),
- atomic64_read(&qtu_events.match_calls),
- atomic64_read(&qtu_events.match_calls_prepost),
- atomic64_read(&qtu_events.match_found_sk),
- atomic64_read(&qtu_events.match_found_sk_in_ct),
- atomic64_read(&qtu_events.match_found_no_sk_in_ct),
- atomic64_read(&qtu_events.match_no_sk),
- atomic64_read(&qtu_events.match_no_sk_file));
+ (u64)atomic64_read(&qtu_events.sockets_tagged),
+ (u64)atomic64_read(&qtu_events.sockets_untagged),
+ (u64)atomic64_read(&qtu_events.counter_set_changes),
+ (u64)atomic64_read(&qtu_events.delete_cmds),
+ (u64)atomic64_read(&qtu_events.iface_events),
+ (u64)atomic64_read(&qtu_events.match_calls),
+ (u64)atomic64_read(&qtu_events.match_calls_prepost),
+ (u64)atomic64_read(&qtu_events.match_found_sk),
+ (u64)atomic64_read(&qtu_events.match_found_sk_in_ct),
+ (u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct),
+ (u64)atomic64_read(&qtu_events.match_no_sk),
+ (u64)atomic64_read(&qtu_events.match_no_sk_file));
/* Count the following as part of the last item_index */
prdebug_full_state(0, "proc ctrl");
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;
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;
}
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);
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",
"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): "
"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
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;
}
{
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;
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;
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",
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);
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);
"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);
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",
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);
return res;
}
-static int qtaguid_ctrl_parse(const char *input, int count)
+static ssize_t qtaguid_ctrl_parse(const char *input, size_t count)
{
char cmd;
- int res;
+ 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 */
if (!res)
res = count;
err:
- CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res);
+ CT_DEBUG("qtaguid: ctrl(%s): res=%zd\n", input, res);
return res;
}
#define MAX_QTAGUID_CTRL_INPUT_LEN 255
-static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer,
+static ssize_t qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *offp)
{
char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN];
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 "
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)
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;
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;
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;
}
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;
.read = seq_read,
.write = qtaguid_ctrl_proc_write,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_private,
};
static const struct seq_operations proc_qtaguid_stats_seqops = {