libxt_owner.c revision f2a77520693f0a6dd1df1f87be4b81913961c1f5
1/*
2 *	libxt_owner - iptables addon for xt_owner
3 *
4 *	Copyright © CC Computer Consultants GmbH, 2007 - 2008
5 *	Jan Engelhardt <jengelh@computergmbh.de>
6 */
7#include <getopt.h>
8#include <grp.h>
9#include <netdb.h>
10#include <pwd.h>
11#include <stdbool.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <limits.h>
16
17#include <xtables.h>
18#include <linux/netfilter/xt_owner.h>
19#include <linux/netfilter_ipv4/ipt_owner.h>
20#include <linux/netfilter_ipv6/ip6t_owner.h>
21
22/*
23 *	Note: "UINT32_MAX - 1" is used in the code because -1 is a reserved
24 *	UID/GID value anyway.
25 */
26
27enum {
28	FLAG_UID_OWNER     = 1 << 0,
29	FLAG_GID_OWNER     = 1 << 1,
30	FLAG_SOCKET_EXISTS = 1 << 2,
31	FLAG_PID_OWNER     = 1 << 3,
32	FLAG_SID_OWNER     = 1 << 4,
33	FLAG_COMM          = 1 << 5,
34};
35
36static void owner_mt_help_v0(void)
37{
38#ifdef IPT_OWNER_COMM
39	printf(
40"owner match options:\n"
41"[!] --uid-owner userid       Match local UID\n"
42"[!] --gid-owner groupid      Match local GID\n"
43"[!] --pid-owner processid    Match local PID\n"
44"[!] --sid-owner sessionid    Match local SID\n"
45"[!] --cmd-owner name         Match local command name\n"
46"NOTE: PID, SID and command matching are broken on SMP\n");
47#else
48	printf(
49"owner match options:\n"
50"[!] --uid-owner userid       Match local UID\n"
51"[!] --gid-owner groupid      Match local GID\n"
52"[!] --pid-owner processid    Match local PID\n"
53"[!] --sid-owner sessionid    Match local SID\n"
54"NOTE: PID and SID matching are broken on SMP\n");
55#endif /* IPT_OWNER_COMM */
56}
57
58static void owner_mt6_help_v0(void)
59{
60	printf(
61"owner match options:\n"
62"[!] --uid-owner userid       Match local UID\n"
63"[!] --gid-owner groupid      Match local GID\n"
64"[!] --pid-owner processid    Match local PID\n"
65"[!] --sid-owner sessionid    Match local SID\n"
66"NOTE: PID and SID matching are broken on SMP\n");
67}
68
69static void owner_mt_help(void)
70{
71	printf(
72"owner match options:\n"
73"[!] --uid-owner userid[-userid]      Match local UID\n"
74"[!] --gid-owner groupid[-groupid]    Match local GID\n"
75"[!] --socket-exists                  Match if socket exists\n");
76}
77
78static const struct option owner_mt_opts_v0[] = {
79	{.name = "uid-owner", .has_arg = true, .val = 'u'},
80	{.name = "gid-owner", .has_arg = true, .val = 'g'},
81	{.name = "pid-owner", .has_arg = true, .val = 'p'},
82	{.name = "sid-owner", .has_arg = true, .val = 's'},
83#ifdef IPT_OWNER_COMM
84	{.name = "cmd-owner", .has_arg = true, .val = 'c'},
85#endif
86	{ .name = NULL }
87};
88
89static const struct option owner_mt6_opts_v0[] = {
90	{.name = "uid-owner", .has_arg = true, .val = 'u'},
91	{.name = "gid-owner", .has_arg = true, .val = 'g'},
92	{.name = "pid-owner", .has_arg = true, .val = 'p'},
93	{.name = "sid-owner", .has_arg = true, .val = 's'},
94	{ .name = NULL }
95};
96
97static const struct option owner_mt_opts[] = {
98	{.name = "uid-owner",     .has_arg = true,  .val = 'u'},
99	{.name = "gid-owner",     .has_arg = true,  .val = 'g'},
100	{.name = "socket-exists", .has_arg = false, .val = 'k'},
101	{ .name = NULL }
102};
103
104static int
105owner_mt_parse_v0(int c, char **argv, int invert, unsigned int *flags,
106                  const void *entry, struct xt_entry_match **match)
107{
108	struct ipt_owner_info *info = (void *)(*match)->data;
109	struct passwd *pwd;
110	struct group *grp;
111	unsigned int id;
112
113	switch (c) {
114	case 'u':
115		xtables_param_act(XTF_ONLY_ONCE, "owner", "--uid-owner", *flags & FLAG_UID_OWNER);
116		if ((pwd = getpwnam(optarg)) != NULL)
117			id = pwd->pw_uid;
118		else if (!xtables_strtoui(optarg, NULL, &id, 0, UINT32_MAX - 1))
119			xtables_param_act(XTF_BAD_VALUE, "owner", "--uid-owner", optarg);
120		if (invert)
121			info->invert |= IPT_OWNER_UID;
122		info->match |= IPT_OWNER_UID;
123		info->uid    = id;
124		*flags      |= FLAG_UID_OWNER;
125		return true;
126
127	case 'g':
128		xtables_param_act(XTF_ONLY_ONCE, "owner", "--gid-owner", *flags & FLAG_GID_OWNER);
129		if ((grp = getgrnam(optarg)) != NULL)
130			id = grp->gr_gid;
131		else if (!xtables_strtoui(optarg, NULL, &id, 0, UINT32_MAX - 1))
132			xtables_param_act(XTF_BAD_VALUE, "owner", "--gid-owner", optarg);
133		if (invert)
134			info->invert |= IPT_OWNER_GID;
135		info->match |= IPT_OWNER_GID;
136		info->gid    = id;
137		*flags      |= FLAG_GID_OWNER;
138		return true;
139
140	case 'p':
141		xtables_param_act(XTF_ONLY_ONCE, "owner", "--pid-owner", *flags & FLAG_PID_OWNER);
142		if (!xtables_strtoui(optarg, NULL, &id, 0, INT_MAX))
143			xtables_param_act(XTF_BAD_VALUE, "owner", "--pid-owner", optarg);
144		if (invert)
145			info->invert |= IPT_OWNER_PID;
146		info->match |= IPT_OWNER_PID;
147		info->pid    = id;
148		*flags      |= FLAG_PID_OWNER;
149		return true;
150
151	case 's':
152		xtables_param_act(XTF_ONLY_ONCE, "owner", "--sid-owner", *flags & FLAG_SID_OWNER);
153		if (!xtables_strtoui(optarg, NULL, &id, 0, INT_MAX))
154			xtables_param_act(XTF_BAD_VALUE, "owner", "--sid-value", optarg);
155		if (invert)
156			info->invert |= IPT_OWNER_SID;
157		info->match |= IPT_OWNER_SID;
158		info->sid    = id;
159		*flags      |= FLAG_SID_OWNER;
160		return true;
161
162#ifdef IPT_OWNER_COMM
163	case 'c':
164		xtables_param_act(XTF_ONLY_ONCE, "owner", "--cmd-owner", *flags & FLAG_COMM);
165		if (strlen(optarg) > sizeof(info->comm))
166			xtables_error(PARAMETER_PROBLEM, "owner match: command "
167			           "\"%s\" too long, max. %zu characters",
168			           optarg, sizeof(info->comm));
169
170		info->comm[sizeof(info->comm)-1] = '\0';
171		strncpy(info->comm, optarg, sizeof(info->comm));
172
173		if (invert)
174			info->invert |= IPT_OWNER_COMM;
175		info->match |= IPT_OWNER_COMM;
176		*flags      |= FLAG_COMM;
177		return true;
178#endif
179	}
180	return false;
181}
182
183static int
184owner_mt6_parse_v0(int c, char **argv, int invert, unsigned int *flags,
185                   const void *entry, struct xt_entry_match **match)
186{
187	struct ip6t_owner_info *info = (void *)(*match)->data;
188	struct passwd *pwd;
189	struct group *grp;
190	unsigned int id;
191
192	switch (c) {
193	case 'u':
194		xtables_param_act(XTF_ONLY_ONCE, "owner", "--uid-owner",
195		          *flags & FLAG_UID_OWNER);
196		if ((pwd = getpwnam(optarg)) != NULL)
197			id = pwd->pw_uid;
198		else if (!xtables_strtoui(optarg, NULL, &id, 0, UINT32_MAX - 1))
199			xtables_param_act(XTF_BAD_VALUE, "owner", "--uid-owner", optarg);
200		if (invert)
201			info->invert |= IP6T_OWNER_UID;
202		info->match |= IP6T_OWNER_UID;
203		info->uid    = id;
204		*flags      |= FLAG_UID_OWNER;
205		return true;
206
207	case 'g':
208		xtables_param_act(XTF_ONLY_ONCE, "owner", "--gid-owner",
209		          *flags & FLAG_GID_OWNER);
210		if ((grp = getgrnam(optarg)) != NULL)
211			id = grp->gr_gid;
212		else if (!xtables_strtoui(optarg, NULL, &id, 0, UINT32_MAX - 1))
213			xtables_param_act(XTF_BAD_VALUE, "owner", "--gid-owner", optarg);
214		if (invert)
215			info->invert |= IP6T_OWNER_GID;
216		info->match |= IP6T_OWNER_GID;
217		info->gid    = id;
218		*flags      |= FLAG_GID_OWNER;
219		return true;
220
221	case 'p':
222		xtables_param_act(XTF_ONLY_ONCE, "owner", "--pid-owner",
223		          *flags & FLAG_PID_OWNER);
224		if (!xtables_strtoui(optarg, NULL, &id, 0, INT_MAX))
225			xtables_param_act(XTF_BAD_VALUE, "owner", "--pid-owner", optarg);
226		if (invert)
227			info->invert |= IP6T_OWNER_PID;
228		info->match |= IP6T_OWNER_PID;
229		info->pid    = id;
230		*flags      |= FLAG_PID_OWNER;
231		return true;
232
233	case 's':
234		xtables_param_act(XTF_ONLY_ONCE, "owner", "--sid-owner",
235		          *flags & FLAG_SID_OWNER);
236		if (!xtables_strtoui(optarg, NULL, &id, 0, INT_MAX))
237			xtables_param_act(XTF_BAD_VALUE, "owner", "--sid-owner", optarg);
238		if (invert)
239			info->invert |= IP6T_OWNER_SID;
240		info->match |= IP6T_OWNER_SID;
241		info->sid    = id;
242		*flags      |= FLAG_SID_OWNER;
243		return true;
244	}
245	return false;
246}
247
248static void owner_parse_range(const char *s, unsigned int *from,
249                              unsigned int *to, const char *opt)
250{
251	char *end;
252
253	/* -1 is reversed, so the max is one less than that. */
254	if (!xtables_strtoui(s, &end, from, 0, UINT32_MAX - 1))
255		xtables_param_act(XTF_BAD_VALUE, "owner", opt, s);
256	*to = *from;
257	if (*end == '-' || *end == ':')
258		if (!xtables_strtoui(end + 1, &end, to, 0, UINT32_MAX - 1))
259			xtables_param_act(XTF_BAD_VALUE, "owner", opt, s);
260	if (*end != '\0')
261		xtables_param_act(XTF_BAD_VALUE, "owner", opt, s);
262}
263
264static int owner_mt_parse(int c, char **argv, int invert, unsigned int *flags,
265                          const void *entry, struct xt_entry_match **match)
266{
267	struct xt_owner_match_info *info = (void *)(*match)->data;
268	struct passwd *pwd;
269	struct group *grp;
270	unsigned int from, to;
271
272	switch (c) {
273	case 'u':
274		xtables_param_act(XTF_ONLY_ONCE, "owner", "--uid-owner",
275		          *flags & FLAG_UID_OWNER);
276		if ((pwd = getpwnam(optarg)) != NULL)
277			from = to = pwd->pw_uid;
278		else
279			owner_parse_range(optarg, &from, &to, "--uid-owner");
280		if (invert)
281			info->invert |= XT_OWNER_UID;
282		info->match  |= XT_OWNER_UID;
283		info->uid_min = from;
284		info->uid_max = to;
285		*flags       |= FLAG_UID_OWNER;
286		return true;
287
288	case 'g':
289		xtables_param_act(XTF_ONLY_ONCE, "owner", "--gid-owner",
290		          *flags & FLAG_GID_OWNER);
291		if ((grp = getgrnam(optarg)) != NULL)
292			from = to = grp->gr_gid;
293		else
294			owner_parse_range(optarg, &from, &to, "--gid-owner");
295		if (invert)
296			info->invert |= XT_OWNER_GID;
297		info->match  |= XT_OWNER_GID;
298		info->gid_min = from;
299		info->gid_max = to;
300		*flags      |= FLAG_GID_OWNER;
301		return true;
302
303	case 'k':
304		xtables_param_act(XTF_ONLY_ONCE, "owner", "--socket-exists",
305		          *flags & FLAG_SOCKET_EXISTS);
306		if (invert)
307			info->invert |= XT_OWNER_SOCKET;
308		info->match |= XT_OWNER_SOCKET;
309		*flags |= FLAG_SOCKET_EXISTS;
310		return true;
311
312	}
313	return false;
314}
315
316static void owner_mt_check(unsigned int flags)
317{
318	if (flags == 0)
319		xtables_error(PARAMETER_PROBLEM, "owner: At least one of "
320		           "--uid-owner, --gid-owner or --socket-exists "
321		           "is required");
322}
323
324static void
325owner_mt_print_item_v0(const struct ipt_owner_info *info, const char *label,
326                       u_int8_t flag, bool numeric)
327{
328	if (!(info->match & flag))
329		return;
330	if (info->invert & flag)
331		printf("! ");
332	printf("%s ", label);
333
334	switch (info->match & flag) {
335	case IPT_OWNER_UID:
336		if (!numeric) {
337			struct passwd *pwd = getpwuid(info->uid);
338
339			if (pwd != NULL && pwd->pw_name != NULL) {
340				printf("%s ", pwd->pw_name);
341				break;
342			}
343		}
344		printf("%u ", (unsigned int)info->uid);
345		break;
346
347	case IPT_OWNER_GID:
348		if (!numeric) {
349			struct group *grp = getgrgid(info->gid);
350
351			if (grp != NULL && grp->gr_name != NULL) {
352				printf("%s ", grp->gr_name);
353				break;
354			}
355		}
356		printf("%u ", (unsigned int)info->gid);
357		break;
358
359	case IPT_OWNER_PID:
360		printf("%u ", (unsigned int)info->pid);
361		break;
362
363	case IPT_OWNER_SID:
364		printf("%u ", (unsigned int)info->sid);
365		break;
366
367#ifdef IPT_OWNER_COMM
368	case IPT_OWNER_COMM:
369		printf("%.*s ", (int)sizeof(info->comm), info->comm);
370		break;
371#endif
372	}
373}
374
375static void
376owner_mt6_print_item_v0(const struct ip6t_owner_info *info, const char *label,
377                        u_int8_t flag, bool numeric)
378{
379	if (!(info->match & flag))
380		return;
381	if (info->invert & flag)
382		printf("! ");
383	printf("%s ", label);
384
385	switch (info->match & flag) {
386	case IP6T_OWNER_UID:
387		if (!numeric) {
388			struct passwd *pwd = getpwuid(info->uid);
389
390			if (pwd != NULL && pwd->pw_name != NULL) {
391				printf("%s ", pwd->pw_name);
392				break;
393			}
394		}
395		printf("%u ", (unsigned int)info->uid);
396		break;
397
398	case IP6T_OWNER_GID:
399		if (!numeric) {
400			struct group *grp = getgrgid(info->gid);
401
402			if (grp != NULL && grp->gr_name != NULL) {
403				printf("%s ", grp->gr_name);
404				break;
405			}
406		}
407		printf("%u ", (unsigned int)info->gid);
408		break;
409
410	case IP6T_OWNER_PID:
411		printf("%u ", (unsigned int)info->pid);
412		break;
413
414	case IP6T_OWNER_SID:
415		printf("%u ", (unsigned int)info->sid);
416		break;
417	}
418}
419
420static void
421owner_mt_print_item(const struct xt_owner_match_info *info, const char *label,
422                    u_int8_t flag, bool numeric)
423{
424	if (!(info->match & flag))
425		return;
426	if (info->invert & flag)
427		printf("! ");
428	printf("%s ", label);
429
430	switch (info->match & flag) {
431	case XT_OWNER_UID:
432		if (info->uid_min != info->uid_max) {
433			printf("%u-%u ", (unsigned int)info->uid_min,
434			       (unsigned int)info->uid_max);
435			break;
436		} else if (!numeric) {
437			const struct passwd *pwd = getpwuid(info->uid_min);
438
439			if (pwd != NULL && pwd->pw_name != NULL) {
440				printf("%s ", pwd->pw_name);
441				break;
442			}
443		}
444		printf("%u ", (unsigned int)info->uid_min);
445		break;
446
447	case XT_OWNER_GID:
448		if (info->gid_min != info->gid_max) {
449			printf("%u-%u ", (unsigned int)info->gid_min,
450			       (unsigned int)info->gid_max);
451			break;
452		} else if (!numeric) {
453			const struct group *grp = getgrgid(info->gid_min);
454
455			if (grp != NULL && grp->gr_name != NULL) {
456				printf("%s ", grp->gr_name);
457				break;
458			}
459		}
460		printf("%u ", (unsigned int)info->gid_min);
461		break;
462	}
463}
464
465static void
466owner_mt_print_v0(const void *ip, const struct xt_entry_match *match,
467                  int numeric)
468{
469	const struct ipt_owner_info *info = (void *)match->data;
470
471	owner_mt_print_item_v0(info, "owner UID match", IPT_OWNER_UID, numeric);
472	owner_mt_print_item_v0(info, "owner GID match", IPT_OWNER_GID, numeric);
473	owner_mt_print_item_v0(info, "owner PID match", IPT_OWNER_PID, numeric);
474	owner_mt_print_item_v0(info, "owner SID match", IPT_OWNER_SID, numeric);
475#ifdef IPT_OWNER_COMM
476	owner_mt_print_item_v0(info, "owner CMD match", IPT_OWNER_COMM, numeric);
477#endif
478}
479
480static void
481owner_mt6_print_v0(const void *ip, const struct xt_entry_match *match,
482                   int numeric)
483{
484	const struct ip6t_owner_info *info = (void *)match->data;
485
486	owner_mt6_print_item_v0(info, "owner UID match", IPT_OWNER_UID, numeric);
487	owner_mt6_print_item_v0(info, "owner GID match", IPT_OWNER_GID, numeric);
488	owner_mt6_print_item_v0(info, "owner PID match", IPT_OWNER_PID, numeric);
489	owner_mt6_print_item_v0(info, "owner SID match", IPT_OWNER_SID, numeric);
490}
491
492static void owner_mt_print(const void *ip, const struct xt_entry_match *match,
493                           int numeric)
494{
495	const struct xt_owner_match_info *info = (void *)match->data;
496
497	owner_mt_print_item(info, "owner socket exists", XT_OWNER_SOCKET, numeric);
498	owner_mt_print_item(info, "owner UID match",     XT_OWNER_UID,    numeric);
499	owner_mt_print_item(info, "owner GID match",     XT_OWNER_GID,    numeric);
500}
501
502static void
503owner_mt_save_v0(const void *ip, const struct xt_entry_match *match)
504{
505	const struct ipt_owner_info *info = (void *)match->data;
506
507	owner_mt_print_item_v0(info, "--uid-owner", IPT_OWNER_UID, true);
508	owner_mt_print_item_v0(info, "--gid-owner", IPT_OWNER_GID, true);
509	owner_mt_print_item_v0(info, "--pid-owner", IPT_OWNER_PID, true);
510	owner_mt_print_item_v0(info, "--sid-owner", IPT_OWNER_SID, true);
511#ifdef IPT_OWNER_COMM
512	owner_mt_print_item_v0(info, "--cmd-owner", IPT_OWNER_COMM, true);
513#endif
514}
515
516static void
517owner_mt6_save_v0(const void *ip, const struct xt_entry_match *match)
518{
519	const struct ip6t_owner_info *info = (void *)match->data;
520
521	owner_mt6_print_item_v0(info, "--uid-owner", IPT_OWNER_UID, true);
522	owner_mt6_print_item_v0(info, "--gid-owner", IPT_OWNER_GID, true);
523	owner_mt6_print_item_v0(info, "--pid-owner", IPT_OWNER_PID, true);
524	owner_mt6_print_item_v0(info, "--sid-owner", IPT_OWNER_SID, true);
525}
526
527static void owner_mt_save(const void *ip, const struct xt_entry_match *match)
528{
529	const struct xt_owner_match_info *info = (void *)match->data;
530
531	owner_mt_print_item(info, "--socket-exists",  XT_OWNER_SOCKET, false);
532	owner_mt_print_item(info, "--uid-owner",      XT_OWNER_UID,    false);
533	owner_mt_print_item(info, "--gid-owner",      XT_OWNER_GID,    false);
534}
535
536static struct xtables_match owner_mt_reg[] = {
537	{
538		.version       = XTABLES_VERSION,
539		.name          = "owner",
540		.revision      = 0,
541		.family        = NFPROTO_IPV4,
542		.size          = XT_ALIGN(sizeof(struct ipt_owner_info)),
543		.userspacesize = XT_ALIGN(sizeof(struct ipt_owner_info)),
544		.help          = owner_mt_help_v0,
545		.parse         = owner_mt_parse_v0,
546		.final_check   = owner_mt_check,
547		.print         = owner_mt_print_v0,
548		.save          = owner_mt_save_v0,
549		.extra_opts    = owner_mt_opts_v0,
550	},
551	{
552		.version       = XTABLES_VERSION,
553		.name          = "owner",
554		.revision      = 0,
555		.family        = NFPROTO_IPV6,
556		.size          = XT_ALIGN(sizeof(struct ip6t_owner_info)),
557		.userspacesize = XT_ALIGN(sizeof(struct ip6t_owner_info)),
558		.help          = owner_mt6_help_v0,
559		.parse         = owner_mt6_parse_v0,
560		.final_check   = owner_mt_check,
561		.print         = owner_mt6_print_v0,
562		.save          = owner_mt6_save_v0,
563		.extra_opts    = owner_mt6_opts_v0,
564	},
565	{
566		.version       = XTABLES_VERSION,
567		.name          = "owner",
568		.revision      = 1,
569		.family        = NFPROTO_UNSPEC,
570		.size          = XT_ALIGN(sizeof(struct xt_owner_match_info)),
571		.userspacesize = XT_ALIGN(sizeof(struct xt_owner_match_info)),
572		.help          = owner_mt_help,
573		.parse         = owner_mt_parse,
574		.final_check   = owner_mt_check,
575		.print         = owner_mt_print,
576		.save          = owner_mt_save,
577		.extra_opts    = owner_mt_opts,
578	},
579};
580
581void _init(void)
582{
583	xtables_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg));
584}
585