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 	 .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