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