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 */ 16enum { 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 29struct 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 38struct 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 52enum { 53 O_USER = 0, 54 O_GROUP, 55 O_SOCK_EXISTS, 56 O_PROCESS, 57 O_SESSION, 58 O_COMM, 59}; 60 61static 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 73static 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 84static 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 94static 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 112static 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 127static 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 136static 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 183static 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 225static 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 241static 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 280static 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 288static void 289owner_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 337static void 338owner_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 382static void 383owner_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 427static void 428owner_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 440static void 441owner_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 452static 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 462static void 463owner_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 474static void 475owner_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 485static 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 494static 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 539void _init(void) 540{ 541 xtables_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg)); 542} 543