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