xref: /linux/net/netfilter/xt_cgroup.c (revision 00afb1811fa638dacf125dd1c343b7a181624dfd)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Xtables module to match the process control group.
4  *
5  * Might be used to implement individual "per-application" firewall
6  * policies in contrast to global policies based on control groups.
7  * Matching is based upon processes tagged to net_cls' classid marker.
8  *
9  * (C) 2013 Daniel Borkmann <dborkman@redhat.com>
10  */
11 
12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13 
14 #include <linux/skbuff.h>
15 #include <linux/module.h>
16 #include <linux/netfilter/x_tables.h>
17 #include <linux/netfilter/xt_cgroup.h>
18 #include <net/sock.h>
19 
20 MODULE_LICENSE("GPL");
21 MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>");
22 MODULE_DESCRIPTION("Xtables: process control group matching");
23 MODULE_ALIAS("ipt_cgroup");
24 MODULE_ALIAS("ip6t_cgroup");
25 
26 #define NET_CLS_CLASSID_INVALID_MSG "xt_cgroup: classid invalid without net_cls cgroups\n"
27 
cgroup_mt_check_v0(const struct xt_mtchk_param * par)28 static int cgroup_mt_check_v0(const struct xt_mtchk_param *par)
29 {
30 	struct xt_cgroup_info_v0 *info = par->matchinfo;
31 
32 	if (info->invert & ~1)
33 		return -EINVAL;
34 
35 	if (!IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)) {
36 		pr_info(NET_CLS_CLASSID_INVALID_MSG);
37 		return -EINVAL;
38 	}
39 
40 	return 0;
41 }
42 
cgroup_mt_check_v1(const struct xt_mtchk_param * par)43 static int cgroup_mt_check_v1(const struct xt_mtchk_param *par)
44 {
45 	struct xt_cgroup_info_v1 *info = par->matchinfo;
46 	struct cgroup *cgrp;
47 
48 	if ((info->invert_path & ~1) || (info->invert_classid & ~1))
49 		return -EINVAL;
50 
51 	if (!info->has_path && !info->has_classid) {
52 		pr_info("xt_cgroup: no path or classid specified\n");
53 		return -EINVAL;
54 	}
55 
56 	if (info->has_path && info->has_classid) {
57 		pr_info_ratelimited("path and classid specified\n");
58 		return -EINVAL;
59 	}
60 
61 	if (info->has_classid && !IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)) {
62 		pr_info(NET_CLS_CLASSID_INVALID_MSG);
63 		return -EINVAL;
64 	}
65 
66 	info->priv = NULL;
67 	if (info->has_path) {
68 		if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
69 			return -ENAMETOOLONG;
70 
71 		cgrp = cgroup_get_from_path(info->path);
72 		if (IS_ERR(cgrp)) {
73 			pr_info_ratelimited("invalid path, errno=%ld\n",
74 					    PTR_ERR(cgrp));
75 			return -EINVAL;
76 		}
77 		info->priv = cgrp;
78 	}
79 
80 	return 0;
81 }
82 
cgroup_mt_check_v2(const struct xt_mtchk_param * par)83 static int cgroup_mt_check_v2(const struct xt_mtchk_param *par)
84 {
85 	struct xt_cgroup_info_v2 *info = par->matchinfo;
86 	struct cgroup *cgrp;
87 
88 	if ((info->invert_path & ~1) || (info->invert_classid & ~1))
89 		return -EINVAL;
90 
91 	if (!info->has_path && !info->has_classid) {
92 		pr_info("xt_cgroup: no path or classid specified\n");
93 		return -EINVAL;
94 	}
95 
96 	if (info->has_path && info->has_classid) {
97 		pr_info_ratelimited("path and classid specified\n");
98 		return -EINVAL;
99 	}
100 
101 	if (info->has_classid && !IS_ENABLED(CONFIG_CGROUP_NET_CLASSID)) {
102 		pr_info(NET_CLS_CLASSID_INVALID_MSG);
103 		return -EINVAL;
104 	}
105 
106 	info->priv = NULL;
107 	if (info->has_path) {
108 		if (strnlen(info->path, sizeof(info->path)) >= sizeof(info->path))
109 			return -ENAMETOOLONG;
110 
111 		cgrp = cgroup_get_from_path(info->path);
112 		if (IS_ERR(cgrp)) {
113 			pr_info_ratelimited("invalid path, errno=%ld\n",
114 					    PTR_ERR(cgrp));
115 			return -EINVAL;
116 		}
117 		info->priv = cgrp;
118 	}
119 
120 	return 0;
121 }
122 
123 static bool
cgroup_mt_v0(const struct sk_buff * skb,struct xt_action_param * par)124 cgroup_mt_v0(const struct sk_buff *skb, struct xt_action_param *par)
125 {
126 #ifdef CONFIG_CGROUP_NET_CLASSID
127 	const struct xt_cgroup_info_v0 *info = par->matchinfo;
128 	struct sock *sk = skb->sk;
129 
130 	if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
131 		return false;
132 
133 	return (info->id == sock_cgroup_classid(&skb->sk->sk_cgrp_data)) ^
134 		info->invert;
135 #endif
136 	return false;
137 }
138 
cgroup_mt_v1(const struct sk_buff * skb,struct xt_action_param * par)139 static bool cgroup_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
140 {
141 	const struct xt_cgroup_info_v1 *info = par->matchinfo;
142 	struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data;
143 	struct cgroup *ancestor = info->priv;
144 	struct sock *sk = skb->sk;
145 
146 	if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
147 		return false;
148 
149 	if (ancestor)
150 		return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^
151 			info->invert_path;
152 #ifdef CONFIG_CGROUP_NET_CLASSID
153 	else
154 		return (info->classid == sock_cgroup_classid(skcd)) ^
155 			info->invert_classid;
156 #endif
157 	return false;
158 }
159 
cgroup_mt_v2(const struct sk_buff * skb,struct xt_action_param * par)160 static bool cgroup_mt_v2(const struct sk_buff *skb, struct xt_action_param *par)
161 {
162 	const struct xt_cgroup_info_v2 *info = par->matchinfo;
163 	struct sock_cgroup_data *skcd = &skb->sk->sk_cgrp_data;
164 	struct cgroup *ancestor = info->priv;
165 	struct sock *sk = skb->sk;
166 
167 	if (!sk || !sk_fullsock(sk) || !net_eq(xt_net(par), sock_net(sk)))
168 		return false;
169 
170 	if (ancestor)
171 		return cgroup_is_descendant(sock_cgroup_ptr(skcd), ancestor) ^
172 			info->invert_path;
173 #ifdef CONFIG_CGROUP_NET_CLASSID
174 	else
175 		return (info->classid == sock_cgroup_classid(skcd)) ^
176 			info->invert_classid;
177 #endif
178 	return false;
179 }
180 
cgroup_mt_destroy_v1(const struct xt_mtdtor_param * par)181 static void cgroup_mt_destroy_v1(const struct xt_mtdtor_param *par)
182 {
183 	struct xt_cgroup_info_v1 *info = par->matchinfo;
184 
185 	if (info->priv)
186 		cgroup_put(info->priv);
187 }
188 
cgroup_mt_destroy_v2(const struct xt_mtdtor_param * par)189 static void cgroup_mt_destroy_v2(const struct xt_mtdtor_param *par)
190 {
191 	struct xt_cgroup_info_v2 *info = par->matchinfo;
192 
193 	if (info->priv)
194 		cgroup_put(info->priv);
195 }
196 
197 static struct xt_match cgroup_mt_reg[] __read_mostly = {
198 	{
199 		.name		= "cgroup",
200 		.revision	= 0,
201 		.family		= NFPROTO_UNSPEC,
202 		.checkentry	= cgroup_mt_check_v0,
203 		.match		= cgroup_mt_v0,
204 		.matchsize	= sizeof(struct xt_cgroup_info_v0),
205 		.me		= THIS_MODULE,
206 		.hooks		= (1 << NF_INET_LOCAL_OUT) |
207 				  (1 << NF_INET_POST_ROUTING) |
208 				  (1 << NF_INET_LOCAL_IN),
209 	},
210 	{
211 		.name		= "cgroup",
212 		.revision	= 1,
213 		.family		= NFPROTO_UNSPEC,
214 		.checkentry	= cgroup_mt_check_v1,
215 		.match		= cgroup_mt_v1,
216 		.matchsize	= sizeof(struct xt_cgroup_info_v1),
217 		.usersize	= offsetof(struct xt_cgroup_info_v1, priv),
218 		.destroy	= cgroup_mt_destroy_v1,
219 		.me		= THIS_MODULE,
220 		.hooks		= (1 << NF_INET_LOCAL_OUT) |
221 				  (1 << NF_INET_POST_ROUTING) |
222 				  (1 << NF_INET_LOCAL_IN),
223 	},
224 	{
225 		.name		= "cgroup",
226 		.revision	= 2,
227 		.family		= NFPROTO_UNSPEC,
228 		.checkentry	= cgroup_mt_check_v2,
229 		.match		= cgroup_mt_v2,
230 		.matchsize	= sizeof(struct xt_cgroup_info_v2),
231 		.usersize	= offsetof(struct xt_cgroup_info_v2, priv),
232 		.destroy	= cgroup_mt_destroy_v2,
233 		.me		= THIS_MODULE,
234 		.hooks		= (1 << NF_INET_LOCAL_OUT) |
235 				  (1 << NF_INET_POST_ROUTING) |
236 				  (1 << NF_INET_LOCAL_IN),
237 	},
238 };
239 
cgroup_mt_init(void)240 static int __init cgroup_mt_init(void)
241 {
242 	return xt_register_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
243 }
244 
cgroup_mt_exit(void)245 static void __exit cgroup_mt_exit(void)
246 {
247 	xt_unregister_matches(cgroup_mt_reg, ARRAY_SIZE(cgroup_mt_reg));
248 }
249 
250 module_init(cgroup_mt_init);
251 module_exit(cgroup_mt_exit);
252