diff options
Diffstat (limited to 'libxtables/xtables.c')
-rw-r--r-- | libxtables/xtables.c | 288 |
1 files changed, 220 insertions, 68 deletions
diff --git a/libxtables/xtables.c b/libxtables/xtables.c index 9fff1e0d..7b370d48 100644 --- a/libxtables/xtables.c +++ b/libxtables/xtables.c @@ -28,6 +28,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <netinet/ether.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/statfs.h> @@ -45,10 +46,10 @@ #include <xtables.h> #include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */ -#include <linux/if_ether.h> /* ETH_ALEN */ #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter_ipv6/ip6_tables.h> #include <libiptc/libxtc.h> +#include <libiptc/linux_list.h> #ifndef NO_SHARED_LIBS #include <dlfcn.h> @@ -63,7 +64,6 @@ #endif #include <getopt.h> #include "iptables/internal.h" -#include "xshared.h" #define NPROTO 255 @@ -71,6 +71,10 @@ #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" #endif +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + /* we need this for ip6?tables-restore. ip6?tables-restore.c sets line to the * current line of the input file, in order to give a more precise error * message. ip6?tables itself doesn't need this, so it is initialized to the @@ -90,15 +94,25 @@ void basic_exit_err(enum xtables_exittype status, const char *msg, ...) vfprintf(stderr, msg, args); va_end(args); fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) { + if (line != -1) + fprintf(stderr, "Error occurred at line: %d\n", line); + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + xt_params->program_name, xt_params->program_name); + } else if (status == VERSION_PROBLEM) { + fprintf(stderr, + "Perhaps %s or your kernel needs to be upgraded.\n", + xt_params->program_name); + } + /* On error paths, make sure that we don't leak memory */ + xtables_free_opts(1); exit(status); } void xtables_free_opts(int unused) { - if (xt_params->opts != xt_params->orig_opts) { - free(xt_params->opts); - xt_params->opts = NULL; - } + free(xt_params->opts); + xt_params->opts = NULL; } struct option *xtables_merge_options(struct option *orig_opts, @@ -243,8 +257,83 @@ static void dlreg_free(void) } #endif +struct notarget { + struct hlist_node node; + char name[]; +}; + +#define NOTARGET_HSIZE 512 +static struct hlist_head notargets[NOTARGET_HSIZE]; + +static void notargets_hlist_init(void) +{ + int i; + + for (i = 0; i < NOTARGET_HSIZE; i++) + INIT_HLIST_HEAD(¬argets[i]); +} + +static void notargets_hlist_free(void) +{ + struct hlist_node *pos, *n; + struct notarget *cur; + int i; + + for (i = 0; i < NOTARGET_HSIZE; i++) { + hlist_for_each_entry_safe(cur, pos, n, ¬argets[i], node) { + hlist_del(&cur->node); + free(cur); + } + } +} + +static uint32_t djb_hash(const char *key) +{ + uint32_t i, hash = 5381; + + for (i = 0; i < strlen(key); i++) + hash = ((hash << 5) + hash) + key[i]; + + return hash; +} + +static struct notarget *notargets_hlist_lookup(const char *name) +{ + uint32_t key = djb_hash(name) % NOTARGET_HSIZE; + struct hlist_node *node; + struct notarget *cur; + + hlist_for_each_entry(cur, node, ¬argets[key], node) { + if (!strcmp(name, cur->name)) + return cur; + } + return NULL; +} + +static void notargets_hlist_insert(const char *name) +{ + struct notarget *cur; + + if (!name) + return; + + cur = xtables_malloc(sizeof(*cur) + strlen(name) + 1); + strcpy(cur->name, name); + hlist_add_head(&cur->node, ¬argets[djb_hash(name) % NOTARGET_HSIZE]); +} + +void xtables_announce_chain(const char *name) +{ + if (!notargets_hlist_lookup(name)) + notargets_hlist_insert(name); +} + void xtables_init(void) { + /* xtables cannot be used with setuid in a safe way. */ + if (getuid() != geteuid()) + _exit(111); + xtables_libdir = getenv("XTABLES_LIBDIR"); if (xtables_libdir != NULL) return; @@ -268,6 +357,8 @@ void xtables_init(void) return; } xtables_libdir = XTABLES_LIBDIR; + + notargets_hlist_init(); } void xtables_fini(void) @@ -275,6 +366,7 @@ void xtables_fini(void) #ifndef NO_SHARED_LIBS dlreg_free(); #endif + notargets_hlist_free(); } void xtables_set_nfproto(uint8_t nfproto) @@ -387,14 +479,9 @@ static char *get_modprobe(void) char *ret; int count; - procfile = open(PROC_SYS_MODPROBE, O_RDONLY); + procfile = open(PROC_SYS_MODPROBE, O_RDONLY | O_CLOEXEC); if (procfile < 0) return NULL; - if (fcntl(procfile, F_SETFD, FD_CLOEXEC) == -1) { - fprintf(stderr, "Could not set close on exec: %s\n", - strerror(errno)); - exit(1); - } ret = malloc(PATH_MAX); if (ret) { @@ -491,23 +578,23 @@ int xtables_load_ko(const char *modprobe, bool quiet) } /** - * xtables_strtou{i,l} - string to number conversion + * xtables_strtoul_base - string to number conversion * @s: input string * @end: like strtoul's "end" pointer * @value: pointer for result * @min: minimum accepted value * @max: maximum accepted value + * @base: assumed base of value * * If @end is NULL, we assume the caller wants a "strict strtoul", and hence * "15a" is rejected. * In either case, the value obtained is compared for min-max compliance. - * Base is always 0, i.e. autodetect depending on @s. * * Returns true/false whether number was accepted. On failure, *value has * undefined contents. */ -bool xtables_strtoul(const char *s, char **end, uintmax_t *value, - uintmax_t min, uintmax_t max) +bool xtables_strtoul_base(const char *s, char **end, uintmax_t *value, + uintmax_t min, uintmax_t max, unsigned int base) { uintmax_t v; const char *p; @@ -519,7 +606,7 @@ bool xtables_strtoul(const char *s, char **end, uintmax_t *value, ; if (*p == '-') return false; - v = strtoumax(s, &my_end, 0); + v = strtoumax(s, &my_end, base); if (my_end == s) return false; if (end != NULL) @@ -536,6 +623,12 @@ bool xtables_strtoul(const char *s, char **end, uintmax_t *value, return false; } +bool xtables_strtoul(const char *s, char **end, uintmax_t *value, + uintmax_t min, uintmax_t max) +{ + return xtables_strtoul_base(s, end, value, min, max, 0); +} + bool xtables_strtoui(const char *s, char **end, unsigned int *value, unsigned int min, unsigned int max) { @@ -681,6 +774,8 @@ xtables_find_match(const char *name, enum xtables_tryload tryload, struct xtables_match **dptr; struct xtables_match *ptr; const char *icmp6 = "icmp6"; + bool found = false; + bool seen = false; if (strlen(name) >= XT_EXTENSION_MAXNAMELEN) xtables_error(PARAMETER_PROBLEM, @@ -699,7 +794,10 @@ xtables_find_match(const char *name, enum xtables_tryload tryload, if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) { ptr = *dptr; *dptr = (*dptr)->next; - if (xtables_fully_register_pending_match(ptr, prev)) { + seen = true; + if (!found && + xtables_fully_register_pending_match(ptr, prev)) { + found = true; prev = ptr; continue; } else if (prev) { @@ -710,6 +808,11 @@ xtables_find_match(const char *name, enum xtables_tryload tryload, dptr = &((*dptr)->next); } + if (seen && !found) + fprintf(stderr, + "Warning: Extension %s is not supported, missing kernel module?\n", + name); + for (ptr = xtables_matches; ptr; ptr = ptr->next) { if (extension_cmp(name, ptr->name, ptr->family)) { struct xtables_match *clone; @@ -801,6 +904,8 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) struct xtables_target *prev = NULL; struct xtables_target **dptr; struct xtables_target *ptr; + bool found = false; + bool seen = false; /* Standard target? */ if (strcmp(name, "") == 0 @@ -809,13 +914,20 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) || strcmp(name, XTC_LABEL_QUEUE) == 0 || strcmp(name, XTC_LABEL_RETURN) == 0) name = "standard"; + /* known non-target? */ + else if (notargets_hlist_lookup(name) && + tryload != XTF_LOAD_MUST_SUCCEED) + return NULL; /* Trigger delayed initialization */ for (dptr = &xtables_pending_targets; *dptr; ) { if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) { ptr = *dptr; *dptr = (*dptr)->next; - if (xtables_fully_register_pending_target(ptr, prev)) { + seen = true; + if (!found && + xtables_fully_register_pending_target(ptr, prev)) { + found = true; prev = ptr; continue; } else if (prev) { @@ -826,6 +938,11 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) dptr = &((*dptr)->next); } + if (seen && !found) + fprintf(stderr, + "Warning: Extension %s is not supported, missing kernel module?\n", + name); + for (ptr = xtables_targets; ptr; ptr = ptr->next) { if (extension_cmp(name, ptr->name, ptr->family)) { struct xtables_target *clone; @@ -872,6 +989,8 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) if (ptr) ptr->used = 1; + else + notargets_hlist_insert(name); return ptr; } @@ -903,7 +1022,7 @@ int xtables_compatible_revision(const char *name, uint8_t revision, int opt) socklen_t s = sizeof(rev); int max_rev, sockfd; - sockfd = socket(afinfo->family, SOCK_RAW, IPPROTO_RAW); + sockfd = socket(afinfo->family, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW); if (sockfd < 0) { if (errno == EPERM) { /* revision 0 is always supported. */ @@ -919,12 +1038,6 @@ int xtables_compatible_revision(const char *name, uint8_t revision, int opt) exit(1); } - if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) { - fprintf(stderr, "Could not set close on exec: %s\n", - strerror(errno)); - exit(1); - } - xtables_load_ko(xtables_modprobe_program, true); strncpy(rev.name, name, XT_EXTENSION_MAXNAMELEN - 1); @@ -936,7 +1049,12 @@ int xtables_compatible_revision(const char *name, uint8_t revision, int opt) /* Definitely don't support this? */ if (errno == ENOENT || errno == EPROTONOSUPPORT) { close(sockfd); - return 0; + /* Pretend revision 0 support for better error messaging */ + if (revision == 0) + fprintf(stderr, + "Warning: Extension %s revision 0 not supported, missing kernel module?\n", + name); + return (revision == 0); } else if (errno == ENOPROTOOPT) { close(sockfd); /* Assume only revision 0 support (old kernel) */ @@ -1300,6 +1418,10 @@ void xtables_rule_matches_free(struct xtables_rule_match **matches) free(matchp->match->m); matchp->match->m = NULL; } + if (matchp->match->udata_size) { + free(matchp->match->udata); + matchp->match->udata = NULL; + } if (matchp->match == matchp->match->next) { free(matchp->match); matchp->match = NULL; @@ -1391,11 +1513,9 @@ void xtables_param_act(unsigned int status, const char *p1, ...) const char *xtables_ipaddr_to_numeric(const struct in_addr *addrp) { - static char buf[20]; - const unsigned char *bytep = (const void *)&addrp->s_addr; + static char buf[INET_ADDRSTRLEN]; - sprintf(buf, "%u.%u.%u.%u", bytep[0], bytep[1], bytep[2], bytep[3]); - return buf; + return inet_ntop(AF_INET, addrp, buf, sizeof(buf)); } static const char *ipaddr_to_host(const struct in_addr *addr) @@ -1455,13 +1575,14 @@ int xtables_ipmask_to_cidr(const struct in_addr *mask) const char *xtables_ipmask_to_numeric(const struct in_addr *mask) { - static char buf[20]; + static char buf[INET_ADDRSTRLEN + 1]; uint32_t cidr; cidr = xtables_ipmask_to_cidr(mask); if (cidr == (unsigned int)-1) { /* mask was not a decent combination of 1's and 0's */ - sprintf(buf, "/%s", xtables_ipaddr_to_numeric(mask)); + buf[0] = '/'; + inet_ntop(AF_INET, mask, buf + 1, sizeof(buf) - 1); return buf; } else if (cidr == 32) { /* we don't want to see "/32" */ @@ -1741,9 +1862,8 @@ void xtables_ipparse_any(const char *name, struct in_addr **addrpp, const char *xtables_ip6addr_to_numeric(const struct in6_addr *addrp) { - /* 0000:0000:0000:0000:0000:0000:000.000.000.000 - * 0000:0000:0000:0000:0000:0000:0000:0000 */ - static char buf[50+1]; + static char buf[INET6_ADDRSTRLEN]; + return inet_ntop(AF_INET6, addrp, buf, sizeof(buf)); } @@ -1801,12 +1921,12 @@ int xtables_ip6mask_to_cidr(const struct in6_addr *k) const char *xtables_ip6mask_to_numeric(const struct in6_addr *addrp) { - static char buf[50+2]; + static char buf[INET6_ADDRSTRLEN + 1]; int l = xtables_ip6mask_to_cidr(addrp); if (l == -1) { strcpy(buf, "/"); - strcat(buf, xtables_ip6addr_to_numeric(addrp)); + inet_ntop(AF_INET6, addrp, buf + 1, sizeof(buf) - 1); return buf; } /* we don't want to see "/128" */ @@ -2074,12 +2194,15 @@ const struct xtables_pprot xtables_chain_protos[] = { {"udp", IPPROTO_UDP}, {"udplite", IPPROTO_UDPLITE}, {"icmp", IPPROTO_ICMP}, - {"icmpv6", IPPROTO_ICMPV6}, {"ipv6-icmp", IPPROTO_ICMPV6}, + {"icmpv6", IPPROTO_ICMPV6}, {"esp", IPPROTO_ESP}, {"ah", IPPROTO_AH}, + {"mobility-header", IPPROTO_MH}, {"ipv6-mh", IPPROTO_MH}, {"mh", IPPROTO_MH}, + {"dccp", IPPROTO_DCCP}, + {"ipcomp", IPPROTO_COMP}, {"all", 0}, {NULL}, }; @@ -2093,23 +2216,15 @@ xtables_parse_protocol(const char *s) if (xtables_strtoui(s, NULL, &proto, 0, UINT8_MAX)) return proto; - /* first deal with the special case of 'all' to prevent - * people from being able to redefine 'all' in nsswitch - * and/or provoke expensive [not working] ldap/nis/... - * lookups */ - if (strcmp(s, "all") == 0) - return 0; + for (i = 0; xtables_chain_protos[i].name != NULL; ++i) { + if (strcmp(s, xtables_chain_protos[i].name) == 0) + return xtables_chain_protos[i].num; + } pent = getprotobyname(s); if (pent != NULL) return pent->p_proto; - for (i = 0; i < ARRAY_SIZE(xtables_chain_protos); ++i) { - if (xtables_chain_protos[i].name == NULL) - continue; - if (strcmp(s, xtables_chain_protos[i].name) == 0) - return xtables_chain_protos[i].num; - } xt_params->exit_err(PARAMETER_PROBLEM, "unknown protocol \"%s\" specified", s); return -1; @@ -2144,8 +2259,6 @@ void xtables_print_num(uint64_t number, unsigned int format) printf(FMT("%4lluT ","%lluT "), (unsigned long long)number); } -#include <netinet/ether.h> - static const unsigned char mac_type_unicast[ETH_ALEN] = {}; static const unsigned char msk_type_unicast[ETH_ALEN] = {1}; static const unsigned char mac_type_multicast[ETH_ALEN] = {1}; @@ -2349,18 +2462,11 @@ struct xt_xlate { struct xt_xlate *xt_xlate_alloc(int size) { - struct xt_xlate *xl; + struct xt_xlate *xl = xtables_malloc(sizeof(struct xt_xlate)); int i; - xl = malloc(sizeof(struct xt_xlate)); - if (xl == NULL) - xtables_error(RESOURCE_PROBLEM, "OOM"); - for (i = 0; i < __XT_XLATE_MAX; i++) { - xl->buf[i].data = malloc(size); - if (xl->buf[i].data == NULL) - xtables_error(RESOURCE_PROBLEM, "OOM"); - + xl->buf[i].data = xtables_malloc(size); xl->buf[i].data[0] = '\0'; xl->buf[i].size = size; xl->buf[i].rem = size; @@ -2381,16 +2487,39 @@ void xt_xlate_free(struct xt_xlate *xl) free(xl); } +static bool isbrace(char c) +{ + switch (c) { + case '(': + case ')': + case '{': + case '}': + case '[': + case ']': + return true; + } + return false; +} + static void __xt_xlate_add(struct xt_xlate *xl, enum xt_xlate_type type, - const char *fmt, va_list ap) + bool space, const char *fmt, va_list ap) { struct xt_xlate_buf *buf = &xl->buf[type]; + char tmpbuf[1024] = ""; int len; - len = vsnprintf(buf->data + buf->off, buf->rem, fmt, ap); - if (len < 0 || len >= buf->rem) + len = vsnprintf(tmpbuf, 1024, fmt, ap); + if (len < 0 || len >= buf->rem - 1) xtables_error(RESOURCE_PROBLEM, "OOM"); + if (space && buf->off && + !isspace(buf->data[buf->off - 1]) && + (isalnum(tmpbuf[0]) || isbrace(tmpbuf[0]))) { + buf->data[buf->off] = ' '; + buf->off++; + buf->rem--; + } + sprintf(buf->data + buf->off, "%s", tmpbuf); buf->rem -= len; buf->off += len; } @@ -2400,7 +2529,16 @@ void xt_xlate_rule_add(struct xt_xlate *xl, const char *fmt, ...) va_list ap; va_start(ap, fmt); - __xt_xlate_add(xl, XT_XLATE_RULE, fmt, ap); + __xt_xlate_add(xl, XT_XLATE_RULE, true, fmt, ap); + va_end(ap); +} + +void xt_xlate_rule_add_nospc(struct xt_xlate *xl, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __xt_xlate_add(xl, XT_XLATE_RULE, false, fmt, ap); va_end(ap); } @@ -2409,7 +2547,16 @@ void xt_xlate_set_add(struct xt_xlate *xl, const char *fmt, ...) va_list ap; va_start(ap, fmt); - __xt_xlate_add(xl, XT_XLATE_SET, fmt, ap); + __xt_xlate_add(xl, XT_XLATE_SET, true, fmt, ap); + va_end(ap); +} + +void xt_xlate_set_add_nospc(struct xt_xlate *xl, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __xt_xlate_add(xl, XT_XLATE_SET, false, fmt, ap); va_end(ap); } @@ -2436,7 +2583,12 @@ uint8_t xt_xlate_get_family(struct xt_xlate *xl) const char *xt_xlate_get(struct xt_xlate *xl) { - return xl->buf[XT_XLATE_RULE].data; + struct xt_xlate_buf *buf = &xl->buf[XT_XLATE_RULE]; + + while (buf->off && isspace(buf->data[buf->off - 1])) + buf->data[--buf->off] = '\0'; + + return buf->data; } const char *xt_xlate_set_get(struct xt_xlate *xl) |