Home | History | Annotate | Download | only in extensions
      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 	XTOPT_TABLEEND,
    134 };
    135 
    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 
    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 
    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 
    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 
    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
    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
    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
    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
    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
    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 
    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
    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
    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 
    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 
    539 void _init(void)
    540 {
    541 	xtables_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg));
    542 }
    543