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