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 */
16 enum {
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 
29 struct 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 
38 struct 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 
52 enum {
53 	O_USER = 0,
54 	O_GROUP,
55 	O_SOCK_EXISTS,
56 	O_PROCESS,
57 	O_SESSION,
58 	O_COMM,
59 };
60 
owner_mt_help_v0(void)61 static 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 
owner_mt6_help_v0(void)73 static 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 
owner_mt_help(void)84 static 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
94 static 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
112 static 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 
127 static 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 	 .flags = XTOPT_INVERT},
134 	XTOPT_TABLEEND,
135 };
136 
owner_mt_parse_v0(struct xt_option_call * cb)137 static void owner_mt_parse_v0(struct xt_option_call *cb)
138 {
139 	struct ipt_owner_info *info = cb->data;
140 	struct passwd *pwd;
141 	struct group *grp;
142 	unsigned int id;
143 
144 	xtables_option_parse(cb);
145 	switch (cb->entry->id) {
146 	case O_USER:
147 		if ((pwd = getpwnam(cb->arg)) != NULL)
148 			id = pwd->pw_uid;
149 		else if (!xtables_strtoui(cb->arg, NULL, &id, 0, UINT32_MAX - 1))
150 			xtables_param_act(XTF_BAD_VALUE, "owner", "--uid-owner", cb->arg);
151 		if (cb->invert)
152 			info->invert |= IPT_OWNER_UID;
153 		info->match |= IPT_OWNER_UID;
154 		info->uid    = id;
155 		break;
156 	case O_GROUP:
157 		if ((grp = getgrnam(cb->arg)) != NULL)
158 			id = grp->gr_gid;
159 		else if (!xtables_strtoui(cb->arg, NULL, &id, 0, UINT32_MAX - 1))
160 			xtables_param_act(XTF_BAD_VALUE, "owner", "--gid-owner", cb->arg);
161 		if (cb->invert)
162 			info->invert |= IPT_OWNER_GID;
163 		info->match |= IPT_OWNER_GID;
164 		info->gid    = id;
165 		break;
166 	case O_PROCESS:
167 		if (cb->invert)
168 			info->invert |= IPT_OWNER_PID;
169 		info->match |= IPT_OWNER_PID;
170 		break;
171 	case O_SESSION:
172 		if (cb->invert)
173 			info->invert |= IPT_OWNER_SID;
174 		info->match |= IPT_OWNER_SID;
175 		break;
176 	case O_COMM:
177 		if (cb->invert)
178 			info->invert |= IPT_OWNER_COMM;
179 		info->match |= IPT_OWNER_COMM;
180 		break;
181 	}
182 }
183 
owner_mt6_parse_v0(struct xt_option_call * cb)184 static void owner_mt6_parse_v0(struct xt_option_call *cb)
185 {
186 	struct ip6t_owner_info *info = cb->data;
187 	struct passwd *pwd;
188 	struct group *grp;
189 	unsigned int id;
190 
191 	xtables_option_parse(cb);
192 	switch (cb->entry->id) {
193 	case O_USER:
194 		if ((pwd = getpwnam(cb->arg)) != NULL)
195 			id = pwd->pw_uid;
196 		else if (!xtables_strtoui(cb->arg, NULL, &id, 0, UINT32_MAX - 1))
197 			xtables_param_act(XTF_BAD_VALUE, "owner", "--uid-owner", cb->arg);
198 		if (cb->invert)
199 			info->invert |= IP6T_OWNER_UID;
200 		info->match |= IP6T_OWNER_UID;
201 		info->uid    = id;
202 		break;
203 	case O_GROUP:
204 		if ((grp = getgrnam(cb->arg)) != NULL)
205 			id = grp->gr_gid;
206 		else if (!xtables_strtoui(cb->arg, NULL, &id, 0, UINT32_MAX - 1))
207 			xtables_param_act(XTF_BAD_VALUE, "owner", "--gid-owner", cb->arg);
208 		if (cb->invert)
209 			info->invert |= IP6T_OWNER_GID;
210 		info->match |= IP6T_OWNER_GID;
211 		info->gid    = id;
212 		break;
213 	case O_PROCESS:
214 		if (cb->invert)
215 			info->invert |= IP6T_OWNER_PID;
216 		info->match |= IP6T_OWNER_PID;
217 		break;
218 	case O_SESSION:
219 		if (cb->invert)
220 			info->invert |= IP6T_OWNER_SID;
221 		info->match |= IP6T_OWNER_SID;
222 		break;
223 	}
224 }
225 
owner_parse_range(const char * s,unsigned int * from,unsigned int * to,const char * opt)226 static void owner_parse_range(const char *s, unsigned int *from,
227                               unsigned int *to, const char *opt)
228 {
229 	char *end;
230 
231 	/* -1 is reversed, so the max is one less than that. */
232 	if (!xtables_strtoui(s, &end, from, 0, UINT32_MAX - 1))
233 		xtables_param_act(XTF_BAD_VALUE, "owner", opt, s);
234 	*to = *from;
235 	if (*end == '-' || *end == ':')
236 		if (!xtables_strtoui(end + 1, &end, to, 0, UINT32_MAX - 1))
237 			xtables_param_act(XTF_BAD_VALUE, "owner", opt, s);
238 	if (*end != '\0')
239 		xtables_param_act(XTF_BAD_VALUE, "owner", opt, s);
240 }
241 
owner_mt_parse(struct xt_option_call * cb)242 static void owner_mt_parse(struct xt_option_call *cb)
243 {
244 	struct xt_owner_match_info *info = cb->data;
245 	struct passwd *pwd;
246 	struct group *grp;
247 	unsigned int from, to;
248 
249 	xtables_option_parse(cb);
250 	switch (cb->entry->id) {
251 	case O_USER:
252 		if ((pwd = getpwnam(cb->arg)) != NULL)
253 			from = to = pwd->pw_uid;
254 		else
255 			owner_parse_range(cb->arg, &from, &to, "--uid-owner");
256 		if (cb->invert)
257 			info->invert |= XT_OWNER_UID;
258 		info->match  |= XT_OWNER_UID;
259 		info->uid_min = from;
260 		info->uid_max = to;
261 		break;
262 	case O_GROUP:
263 		if ((grp = getgrnam(cb->arg)) != NULL)
264 			from = to = grp->gr_gid;
265 		else
266 			owner_parse_range(cb->arg, &from, &to, "--gid-owner");
267 		if (cb->invert)
268 			info->invert |= XT_OWNER_GID;
269 		info->match  |= XT_OWNER_GID;
270 		info->gid_min = from;
271 		info->gid_max = to;
272 		break;
273 	case O_SOCK_EXISTS:
274 		if (cb->invert)
275 			info->invert |= XT_OWNER_SOCKET;
276 		info->match |= XT_OWNER_SOCKET;
277 		break;
278 	}
279 }
280 
owner_mt_check(struct xt_fcheck_call * cb)281 static void owner_mt_check(struct xt_fcheck_call *cb)
282 {
283 	if (cb->xflags == 0)
284 		xtables_error(PARAMETER_PROBLEM, "owner: At least one of "
285 		           "--uid-owner, --gid-owner or --socket-exists "
286 		           "is required");
287 }
288 
289 static void
owner_mt_print_item_v0(const struct ipt_owner_info * info,const char * label,uint8_t flag,bool numeric)290 owner_mt_print_item_v0(const struct ipt_owner_info *info, const char *label,
291                        uint8_t flag, bool numeric)
292 {
293 	if (!(info->match & flag))
294 		return;
295 	if (info->invert & flag)
296 		printf(" !");
297 	printf(" %s", label);
298 
299 	switch (info->match & flag) {
300 	case IPT_OWNER_UID:
301 		if (!numeric) {
302 			struct passwd *pwd = getpwuid(info->uid);
303 
304 			if (pwd != NULL && pwd->pw_name != NULL) {
305 				printf(" %s", pwd->pw_name);
306 				break;
307 			}
308 		}
309 		printf(" %u", (unsigned int)info->uid);
310 		break;
311 
312 	case IPT_OWNER_GID:
313 		if (!numeric) {
314 			struct group *grp = getgrgid(info->gid);
315 
316 			if (grp != NULL && grp->gr_name != NULL) {
317 				printf(" %s", grp->gr_name);
318 				break;
319 			}
320 		}
321 		printf(" %u", (unsigned int)info->gid);
322 		break;
323 
324 	case IPT_OWNER_PID:
325 		printf(" %u", (unsigned int)info->pid);
326 		break;
327 
328 	case IPT_OWNER_SID:
329 		printf(" %u", (unsigned int)info->sid);
330 		break;
331 
332 	case IPT_OWNER_COMM:
333 		printf(" %.*s", (int)sizeof(info->comm), info->comm);
334 		break;
335 	}
336 }
337 
338 static void
owner_mt6_print_item_v0(const struct ip6t_owner_info * info,const char * label,uint8_t flag,bool numeric)339 owner_mt6_print_item_v0(const struct ip6t_owner_info *info, const char *label,
340                         uint8_t flag, bool numeric)
341 {
342 	if (!(info->match & flag))
343 		return;
344 	if (info->invert & flag)
345 		printf(" !");
346 	printf(" %s", label);
347 
348 	switch (info->match & flag) {
349 	case IP6T_OWNER_UID:
350 		if (!numeric) {
351 			struct passwd *pwd = getpwuid(info->uid);
352 
353 			if (pwd != NULL && pwd->pw_name != NULL) {
354 				printf(" %s", pwd->pw_name);
355 				break;
356 			}
357 		}
358 		printf(" %u", (unsigned int)info->uid);
359 		break;
360 
361 	case IP6T_OWNER_GID:
362 		if (!numeric) {
363 			struct group *grp = getgrgid(info->gid);
364 
365 			if (grp != NULL && grp->gr_name != NULL) {
366 				printf(" %s", grp->gr_name);
367 				break;
368 			}
369 		}
370 		printf(" %u", (unsigned int)info->gid);
371 		break;
372 
373 	case IP6T_OWNER_PID:
374 		printf(" %u", (unsigned int)info->pid);
375 		break;
376 
377 	case IP6T_OWNER_SID:
378 		printf(" %u", (unsigned int)info->sid);
379 		break;
380 	}
381 }
382 
383 static void
owner_mt_print_item(const struct xt_owner_match_info * info,const char * label,uint8_t flag,bool numeric)384 owner_mt_print_item(const struct xt_owner_match_info *info, const char *label,
385                     uint8_t flag, bool numeric)
386 {
387 	if (!(info->match & flag))
388 		return;
389 	if (info->invert & flag)
390 		printf(" !");
391 	printf(" %s", label);
392 
393 	switch (info->match & flag) {
394 	case XT_OWNER_UID:
395 		if (info->uid_min != info->uid_max) {
396 			printf(" %u-%u", (unsigned int)info->uid_min,
397 			       (unsigned int)info->uid_max);
398 			break;
399 		} else if (!numeric) {
400 			const struct passwd *pwd = getpwuid(info->uid_min);
401 
402 			if (pwd != NULL && pwd->pw_name != NULL) {
403 				printf(" %s", pwd->pw_name);
404 				break;
405 			}
406 		}
407 		printf(" %u", (unsigned int)info->uid_min);
408 		break;
409 
410 	case XT_OWNER_GID:
411 		if (info->gid_min != info->gid_max) {
412 			printf(" %u-%u", (unsigned int)info->gid_min,
413 			       (unsigned int)info->gid_max);
414 			break;
415 		} else if (!numeric) {
416 			const struct group *grp = getgrgid(info->gid_min);
417 
418 			if (grp != NULL && grp->gr_name != NULL) {
419 				printf(" %s", grp->gr_name);
420 				break;
421 			}
422 		}
423 		printf(" %u", (unsigned int)info->gid_min);
424 		break;
425 	}
426 }
427 
428 static void
owner_mt_print_v0(const void * ip,const struct xt_entry_match * match,int numeric)429 owner_mt_print_v0(const void *ip, const struct xt_entry_match *match,
430                   int numeric)
431 {
432 	const struct ipt_owner_info *info = (void *)match->data;
433 
434 	owner_mt_print_item_v0(info, "owner UID match", IPT_OWNER_UID, numeric);
435 	owner_mt_print_item_v0(info, "owner GID match", IPT_OWNER_GID, numeric);
436 	owner_mt_print_item_v0(info, "owner PID match", IPT_OWNER_PID, numeric);
437 	owner_mt_print_item_v0(info, "owner SID match", IPT_OWNER_SID, numeric);
438 	owner_mt_print_item_v0(info, "owner CMD match", IPT_OWNER_COMM, numeric);
439 }
440 
441 static void
owner_mt6_print_v0(const void * ip,const struct xt_entry_match * match,int numeric)442 owner_mt6_print_v0(const void *ip, const struct xt_entry_match *match,
443                    int numeric)
444 {
445 	const struct ip6t_owner_info *info = (void *)match->data;
446 
447 	owner_mt6_print_item_v0(info, "owner UID match", IPT_OWNER_UID, numeric);
448 	owner_mt6_print_item_v0(info, "owner GID match", IPT_OWNER_GID, numeric);
449 	owner_mt6_print_item_v0(info, "owner PID match", IPT_OWNER_PID, numeric);
450 	owner_mt6_print_item_v0(info, "owner SID match", IPT_OWNER_SID, numeric);
451 }
452 
owner_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)453 static void owner_mt_print(const void *ip, const struct xt_entry_match *match,
454                            int numeric)
455 {
456 	const struct xt_owner_match_info *info = (void *)match->data;
457 
458 	owner_mt_print_item(info, "owner socket exists", XT_OWNER_SOCKET, numeric);
459 	owner_mt_print_item(info, "owner UID match",     XT_OWNER_UID,    numeric);
460 	owner_mt_print_item(info, "owner GID match",     XT_OWNER_GID,    numeric);
461 }
462 
463 static void
owner_mt_save_v0(const void * ip,const struct xt_entry_match * match)464 owner_mt_save_v0(const void *ip, const struct xt_entry_match *match)
465 {
466 	const struct ipt_owner_info *info = (void *)match->data;
467 
468 	owner_mt_print_item_v0(info, "--uid-owner", IPT_OWNER_UID, true);
469 	owner_mt_print_item_v0(info, "--gid-owner", IPT_OWNER_GID, true);
470 	owner_mt_print_item_v0(info, "--pid-owner", IPT_OWNER_PID, true);
471 	owner_mt_print_item_v0(info, "--sid-owner", IPT_OWNER_SID, true);
472 	owner_mt_print_item_v0(info, "--cmd-owner", IPT_OWNER_COMM, true);
473 }
474 
475 static void
owner_mt6_save_v0(const void * ip,const struct xt_entry_match * match)476 owner_mt6_save_v0(const void *ip, const struct xt_entry_match *match)
477 {
478 	const struct ip6t_owner_info *info = (void *)match->data;
479 
480 	owner_mt6_print_item_v0(info, "--uid-owner", IPT_OWNER_UID, true);
481 	owner_mt6_print_item_v0(info, "--gid-owner", IPT_OWNER_GID, true);
482 	owner_mt6_print_item_v0(info, "--pid-owner", IPT_OWNER_PID, true);
483 	owner_mt6_print_item_v0(info, "--sid-owner", IPT_OWNER_SID, true);
484 }
485 
owner_mt_save(const void * ip,const struct xt_entry_match * match)486 static void owner_mt_save(const void *ip, const struct xt_entry_match *match)
487 {
488 	const struct xt_owner_match_info *info = (void *)match->data;
489 
490 	owner_mt_print_item(info, "--socket-exists",  XT_OWNER_SOCKET, true);
491 	owner_mt_print_item(info, "--uid-owner",      XT_OWNER_UID,    true);
492 	owner_mt_print_item(info, "--gid-owner",      XT_OWNER_GID,    true);
493 }
494 
495 static struct xtables_match owner_mt_reg[] = {
496 	{
497 		.version       = XTABLES_VERSION,
498 		.name          = "owner",
499 		.revision      = 0,
500 		.family        = NFPROTO_IPV4,
501 		.size          = XT_ALIGN(sizeof(struct ipt_owner_info)),
502 		.userspacesize = XT_ALIGN(sizeof(struct ipt_owner_info)),
503 		.help          = owner_mt_help_v0,
504 		.x6_parse      = owner_mt_parse_v0,
505 		.x6_fcheck     = owner_mt_check,
506 		.print         = owner_mt_print_v0,
507 		.save          = owner_mt_save_v0,
508 		.x6_options    = owner_mt_opts_v0,
509 	},
510 	{
511 		.version       = XTABLES_VERSION,
512 		.name          = "owner",
513 		.revision      = 0,
514 		.family        = NFPROTO_IPV6,
515 		.size          = XT_ALIGN(sizeof(struct ip6t_owner_info)),
516 		.userspacesize = XT_ALIGN(sizeof(struct ip6t_owner_info)),
517 		.help          = owner_mt6_help_v0,
518 		.x6_parse      = owner_mt6_parse_v0,
519 		.x6_fcheck     = owner_mt_check,
520 		.print         = owner_mt6_print_v0,
521 		.save          = owner_mt6_save_v0,
522 		.x6_options    = owner_mt6_opts_v0,
523 	},
524 	{
525 		.version       = XTABLES_VERSION,
526 		.name          = "owner",
527 		.revision      = 1,
528 		.family        = NFPROTO_UNSPEC,
529 		.size          = XT_ALIGN(sizeof(struct xt_owner_match_info)),
530 		.userspacesize = XT_ALIGN(sizeof(struct xt_owner_match_info)),
531 		.help          = owner_mt_help,
532 		.x6_parse      = owner_mt_parse,
533 		.x6_fcheck     = owner_mt_check,
534 		.print         = owner_mt_print,
535 		.save          = owner_mt_save,
536 		.x6_options    = owner_mt_opts,
537 	},
538 };
539 
_init(void)540 void _init(void)
541 {
542 	xtables_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg));
543 }
544