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 XTOPT_TABLEEND,
134 };
135
owner_mt_parse_v0(struct xt_option_call * cb)136 static 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
owner_mt6_parse_v0(struct xt_option_call * cb)183 static 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
owner_parse_range(const char * s,unsigned int * from,unsigned int * to,const char * opt)225 static 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
owner_mt_parse(struct xt_option_call * cb)241 static 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
owner_mt_check(struct xt_fcheck_call * cb)280 static 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
288 static void
owner_mt_print_item_v0(const struct ipt_owner_info * info,const char * label,uint8_t flag,bool numeric)289 owner_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
337 static void
owner_mt6_print_item_v0(const struct ip6t_owner_info * info,const char * label,uint8_t flag,bool numeric)338 owner_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
382 static void
owner_mt_print_item(const struct xt_owner_match_info * info,const char * label,uint8_t flag,bool numeric)383 owner_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
427 static void
owner_mt_print_v0(const void * ip,const struct xt_entry_match * match,int numeric)428 owner_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
440 static void
owner_mt6_print_v0(const void * ip,const struct xt_entry_match * match,int numeric)441 owner_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
owner_mt_print(const void * ip,const struct xt_entry_match * match,int numeric)452 static 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
462 static void
owner_mt_save_v0(const void * ip,const struct xt_entry_match * match)463 owner_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
474 static void
owner_mt6_save_v0(const void * ip,const struct xt_entry_match * match)475 owner_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
owner_mt_save(const void * ip,const struct xt_entry_match * match)485 static 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
494 static 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
_init(void)539 void _init(void)
540 {
541 xtables_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg));
542 }
543