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