1 /* 2 * libxt_owner - iptables addon for xt_owner 3 * 4 * Copyright CC Computer Consultants GmbH, 2007 - 2008 5 * Jan Engelhardt <jengelh (at) 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 540 void _init(void) 541 { 542 xtables_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg)); 543 } 544