Skip to content

Commit fc04365

Browse files
authored
Validate for iptables bad iptables modules & libxtables update (#38)
* catch ABI errors * fixing with new libxtables * Update xshared.c * Update xshared.c * Update xshared.c * Update xshared.c * Update xshared.c * Revert "Update xshared.c" This reverts commit 38b779b. * ... * ... * Update ip6tables.c
1 parent 423a86e commit fc04365

5 files changed

Lines changed: 242 additions & 25 deletions

File tree

ipthelper/ip6tables.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,6 @@ static const unsigned int inverse_for_options[NUMBER_OF_OPT] =
216216
0,
217217
};
218218

219-
#define opts iptables_globals.opts
220219
#define prog_name iptables_globals.program_name
221220
#define prog_vers iptables_globals.program_version
222221
/* A few hardcoded protocols for 'all' and in case the user has no
@@ -1439,10 +1438,27 @@ int do_command6(int argc, char *argv[], char **table, void **handle)
14391438
demand-load a protocol. */
14401439
opterr = 0;
14411440

1442-
opts = xt_params->orig_opts;
1441+
/* Create a malloc'd copy of orig_opts */
1442+
if (iptables_globals.opts == NULL) {
1443+
size_t num_opts = 0;
1444+
struct option *orig_opts = iptables_globals.orig_opts;
1445+
1446+
/* Count the number of options (including the NULL terminator) */
1447+
while (orig_opts[num_opts].name != NULL) {
1448+
num_opts++;
1449+
}
1450+
num_opts++; /* Include the NULL terminator */
1451+
1452+
/* Allocate memory and copy the options */
1453+
iptables_globals.opts = malloc(num_opts * sizeof(struct option));
1454+
if (iptables_globals.opts == NULL) {
1455+
xtables_error(OTHER_PROBLEM, "malloc failed for options array");
1456+
}
1457+
memcpy(iptables_globals.opts, iptables_globals.orig_opts, num_opts * sizeof(struct option));
1458+
}
14431459
while ((cs.c = getopt_long(argc, argv,
14441460
"-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:g:46",
1445-
opts, NULL)) != -1) {
1461+
iptables_globals.opts?: iptables_globals.orig_opts, NULL)) != -1) {
14461462
switch (cs.c) {
14471463
/*
14481464
* Command selection
@@ -1774,7 +1790,7 @@ int do_command6(int argc, char *argv[], char **table, void **handle)
17741790
continue;
17751791
break;
17761792
}
1777-
cs.invert = 2;
1793+
cs.invert = FALSE;
17781794
}
17791795

17801796
for (matchp = cs.matches; matchp; matchp = matchp->next)
@@ -2017,5 +2033,11 @@ int do_command6(int argc, char *argv[], char **table, void **handle)
20172033
free(dmasks);
20182034
xtables_free_opts(1);
20192035

2036+
/* Free the malloc'd copy of opts if it was allocated */
2037+
if (iptables_globals.opts != iptables_globals.orig_opts) {
2038+
free(iptables_globals.opts);
2039+
iptables_globals.opts = NULL;
2040+
}
2041+
20202042
return ret;
20212043
}

ipthelper/iptables.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,10 +1017,28 @@ int do_command4(int argc, char *argv[], char **table, void **handle)
10171017
demand-load a protocol. */
10181018
opterr = 0;
10191019

1020-
iptables_globals.opts = iptables_globals.orig_opts;
1020+
/* Create a malloc'd copy of orig_opts */
1021+
if (iptables_globals.opts == NULL) {
1022+
size_t num_opts = 0;
1023+
struct option *orig_opts = iptables_globals.orig_opts;
1024+
1025+
/* Count the number of options (including the NULL terminator) */
1026+
while (orig_opts[num_opts].name != NULL) {
1027+
num_opts++;
1028+
}
1029+
num_opts++; /* Include the NULL terminator */
1030+
1031+
/* Allocate memory and copy the options */
1032+
iptables_globals.opts = malloc(num_opts * sizeof(struct option));
1033+
if (iptables_globals.opts == NULL) {
1034+
xtables_error(OTHER_PROBLEM, "malloc failed for options array");
1035+
}
1036+
memcpy(iptables_globals.opts, iptables_globals.orig_opts, num_opts * sizeof(struct option));
1037+
}
10211038
while ((cs.c = getopt_long(argc, argv,
10221039
"-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:46",
1023-
iptables_globals.opts, NULL)) != -1) {
1040+
iptables_globals.opts?: iptables_globals.orig_opts,
1041+
NULL)) != -1) {
10241042
switch (cs.c) {
10251043
/*
10261044
* Command selection
@@ -1523,5 +1541,11 @@ int do_command4(int argc, char *argv[], char **table, void **handle)
15231541
free(dmasks);
15241542
//xtables_free_opts(1);
15251543

1544+
/* Free the malloc'd copy of opts if it was allocated */
1545+
if (iptables_globals.opts != iptables_globals.orig_opts) {
1546+
free(iptables_globals.opts);
1547+
iptables_globals.opts = NULL;
1548+
}
1549+
15261550
return ret;
15271551
}

ipthelper/xshared.c

Lines changed: 184 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
#ifndef _GNU_SOURCE
2+
#define _GNU_SOURCE 1
3+
#endif
4+
15
#include <ctype.h>
26
#include <getopt.h>
37
#include <errno.h>
@@ -14,10 +18,89 @@
1418
#include <sys/time.h>
1519
#include <unistd.h>
1620
#include <fcntl.h>
21+
#if defined(__unix__) || defined(__APPLE__)
22+
#include <dlfcn.h>
23+
#endif
1724
#include "xtables.h"
1825
#include <math.h>
1926
#include "xshared.h"
2027

28+
#define XS_LONGOPTS_SCAN_LIMIT 4096U
29+
30+
#if defined(__unix__) || defined(__APPLE__)
31+
#define XS_HAVE_DLADDR 1
32+
#else
33+
#define XS_HAVE_DLADDR 0
34+
#endif
35+
36+
static size_t xs_longopts_count(const struct option *opts, const char *ext_name)
37+
{
38+
size_t i;
39+
40+
if (opts == NULL)
41+
return 0;
42+
43+
for (i = 0; i < XS_LONGOPTS_SCAN_LIMIT; ++i) {
44+
if (opts[i].name == NULL)
45+
return i;
46+
}
47+
48+
if (ext_name != NULL)
49+
xtables_error(OTHER_PROBLEM,
50+
"Extension \"%s\" returned an unterminated option table.",
51+
ext_name);
52+
53+
xtables_error(OTHER_PROBLEM,
54+
"xtables option table is missing its terminator.");
55+
return 0;
56+
}
57+
58+
#if XS_HAVE_DLADDR
59+
static bool xs_option_name_pointer_is_valid(const char *name)
60+
{
61+
Dl_info info;
62+
63+
if (name == NULL)
64+
return true;
65+
66+
return dladdr((const void *)name, &info) != 0;
67+
}
68+
#else
69+
static bool xs_option_name_pointer_is_valid(const char *name)
70+
{
71+
if (name == NULL)
72+
return true;
73+
74+
#if INTPTR_MAX > 0xffffffff
75+
if ((uintptr_t)name < 0x100000000ULL)
76+
return false;
77+
#endif
78+
return true;
79+
}
80+
#endif
81+
82+
static void xs_validate_new_longopts(struct option *opts, size_t start,
83+
const char *ext_name)
84+
{
85+
size_t total;
86+
size_t i;
87+
88+
if (opts == NULL || ext_name == NULL)
89+
return;
90+
91+
total = xs_longopts_count(opts, ext_name);
92+
if (start >= total)
93+
return;
94+
95+
for (i = start; i < total; ++i) {
96+
if (!xs_option_name_pointer_is_valid(opts[i].name)) {
97+
xtables_error(OTHER_PROBLEM,
98+
"Extension \"%s\" was built against an incompatible libxtables release (detected corrupt option metadata). Please rebuild the module.",
99+
ext_name);
100+
}
101+
}
102+
}
103+
21104
/*
22105
* Print out any special helps. A user might like to be able to add a --help
23106
* to the commandline, and see expected results. So we call help for all
@@ -146,6 +229,7 @@ int command_default(struct iptables_command_state *cs,
146229
m = load_proto(cs);
147230
if (m != NULL) {
148231
size_t size;
232+
size_t merge_start;
149233

150234
cs->proto_used = 1;
151235

@@ -157,18 +241,22 @@ int command_default(struct iptables_command_state *cs,
157241
m->m->u.user.revision = m->revision;
158242
xs_init_match(m);
159243

244+
merge_start = xs_longopts_count(gl->opts, NULL);
160245
if (m->x6_options != NULL)
161246
gl->opts = xtables_options_xfrm(gl->orig_opts,
162247
gl->opts,
163248
m->x6_options,
164249
&m->option_offset);
165250
else
166-
gl->opts = xtables_merge_options(gl->orig_opts,
167-
gl->opts,
168-
m->extra_opts,
169-
&m->option_offset);
251+
gl->opts = xs_merge_options(gl->orig_opts,
252+
gl->opts,
253+
m->extra_opts,
254+
&m->option_offset);
170255
if (gl->opts == NULL)
171256
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
257+
xs_validate_new_longopts(gl->opts, merge_start,
258+
m->real_name != NULL ?
259+
m->real_name : m->name);
172260
optind--;
173261
/* Indicate to rerun getopt *immediately* */
174262
return 1;
@@ -564,18 +652,26 @@ void command_match(struct iptables_command_state *cs)
564652
if (m == m->next)
565653
return;
566654
/* Merge options for non-cloned matches */
567-
if (m->x6_options != NULL){
568-
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
655+
{
656+
size_t merge_start = xs_longopts_count(opts, NULL);
657+
658+
if (m->x6_options != NULL) {
659+
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
569660
m->x6_options, &m->option_offset);
570-
int num_orig;
571-
for (num_orig = 0; opts[num_orig].name != NULL; ++num_orig) {}
661+
} else if (m->extra_opts != NULL) {
662+
opts = xs_merge_options(xt_params->orig_opts, opts,
663+
m->extra_opts, &m->option_offset);
664+
665+
} else
666+
return;
572667

668+
if (opts == NULL)
669+
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
670+
671+
xs_validate_new_longopts(opts, merge_start,
672+
m->real_name != NULL ?
673+
m->real_name : m->name);
573674
}
574-
else if (m->extra_opts != NULL)
575-
opts = xtables_merge_options(xt_params->orig_opts, opts,
576-
m->extra_opts, &m->option_offset);
577-
if (opts == NULL)
578-
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
579675
xt_params->opts = opts;
580676
}
581677

@@ -628,15 +724,85 @@ void command_jump(struct iptables_command_state *cs)
628724
cs->target->t->u.user.revision = cs->target->revision;
629725
xs_init_target(cs->target);
630726

631-
if (cs->target->x6_options != NULL)
727+
size_t merge_start = xs_longopts_count(opts, NULL);
728+
729+
if (cs->target->x6_options != NULL) {
632730
opts = xtables_options_xfrm(xt_params->orig_opts, opts,
633-
cs->target->x6_options,
634-
&cs->target->option_offset);
635-
else
636-
opts = xtables_merge_options(xt_params->orig_opts, opts,
731+
cs->target->x6_options,
732+
&cs->target->option_offset);
733+
} else if (cs->target->extra_opts != NULL)
734+
opts = xs_merge_options(xt_params->orig_opts, opts,
637735
cs->target->extra_opts,
638736
&cs->target->option_offset);
737+
else
738+
return;
639739
if (opts == NULL)
640740
xtables_error(OTHER_PROBLEM, "can't alloc memory!");
741+
xs_validate_new_longopts(opts, merge_start,
742+
cs->target->real_name != NULL ?
743+
cs->target->real_name : cs->jumpto);
641744
xt_params->opts = opts;
642745
}
746+
747+
748+
struct option *
749+
xs_merge_options(struct option *orig_opts, struct option *oldopts,
750+
const struct option *newopts, unsigned int *option_offset)
751+
{
752+
unsigned int num_orig = 0, num_old = 0, num_new = 0, i;
753+
struct option *merge;
754+
struct option *mp;
755+
struct option *old_base = oldopts;
756+
757+
if (newopts == NULL || newopts->name == NULL)
758+
return oldopts;
759+
760+
if (orig_opts != NULL)
761+
for (; orig_opts[num_orig].name != NULL; ++num_orig)
762+
;
763+
764+
if (oldopts != NULL)
765+
for (; oldopts[num_old].name != NULL; ++num_old)
766+
;
767+
768+
for (; newopts[num_new].name != NULL; ++num_new)
769+
;
770+
771+
if (oldopts != NULL && num_old >= num_orig) {
772+
oldopts += num_orig;
773+
num_old -= num_orig;
774+
} else {
775+
oldopts = NULL;
776+
num_old = 0;
777+
}
778+
779+
merge = malloc(sizeof(*merge) * (num_orig + num_new + num_old + 1));
780+
if (merge == NULL)
781+
return NULL;
782+
783+
if (num_orig != 0)
784+
memcpy(merge, orig_opts, sizeof(*merge) * num_orig);
785+
mp = merge + num_orig;
786+
787+
xt_params->option_offset += XT_OPTION_OFFSET_SCALE;
788+
*option_offset = xt_params->option_offset;
789+
790+
for (i = 0; i < num_new; ++i, ++mp) {
791+
mp->name = newopts[i].name;
792+
mp->has_arg = newopts[i].has_arg;
793+
mp->flag = newopts[i].flag;
794+
mp->val = newopts[i].val + *option_offset;
795+
}
796+
797+
if (oldopts != NULL && num_old != 0) {
798+
memcpy(mp, oldopts, sizeof(*mp) * num_old);
799+
mp += num_old;
800+
}
801+
802+
memset(mp, 0, sizeof(*mp));
803+
804+
if (old_base != NULL && old_base != orig_opts && old_base != xt_params->orig_opts)
805+
free(old_base);
806+
807+
return merge;
808+
}

ipthelper/xshared.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,5 +179,9 @@ void print_ifaces(const char *iniface, const char *outiface, uint8_t invflags,
179179
void command_match(struct iptables_command_state *cs);
180180
const char *xt_parse_target(const char *targetname);
181181
void command_jump(struct iptables_command_state *cs);
182+
struct option *xs_merge_options(struct option *orig_opts,
183+
struct option *oldopts,
184+
const struct option *newopts,
185+
unsigned int *option_offset);
182186

183187
#endif /* IPTABLES_XSHARED_H */

ipthelper/xtoptions.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ xtables_options_xfrm(struct option *orig_opts, struct option *oldopts,
120120
memcpy(mp, oldopts, sizeof(*mp) * num_old);
121121
mp += num_old;
122122
}
123-
xtables_free_opts(0);
123+
if (xt_params->opts != NULL && xt_params->opts != xt_params->orig_opts)
124+
xtables_free_opts(0);
124125

125126
/* Clear trailing entry */
126127
memset(mp, 0, sizeof(*mp));

0 commit comments

Comments
 (0)