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