Home | History | Annotate | Download | only in extensions
      1 /* Shared library add-on to iptables to add string matching support.
      2  *
      3  * Copyright (C) 2000 Emmanuel Roger  <winfield (at) freegates.be>
      4  *
      5  * 2005-08-05 Pablo Neira Ayuso <pablo (at) eurodev.net>
      6  * 	- reimplemented to use new string matching iptables match
      7  * 	- add functionality to match packets by using window offsets
      8  * 	- add functionality to select the string matching algorithm
      9  *
     10  * ChangeLog
     11  *     29.12.2003: Michael Rash <mbr (at) cipherdyne.org>
     12  *             Fixed iptables save/restore for ascii strings
     13  *             that contain space chars, and hex strings that
     14  *             contain embedded NULL chars.  Updated to print
     15  *             strings in hex mode if any non-printable char
     16  *             is contained within the string.
     17  *
     18  *     27.01.2001: Gianni Tedesco <gianni (at) ecsc.co.uk>
     19  *             Changed --tos to --string in save(). Also
     20  *             updated to work with slightly modified
     21  *             ipt_string_info.
     22  */
     23 #include <stdio.h>
     24 #include <string.h>
     25 #include <stdlib.h>
     26 #include <ctype.h>
     27 #include <xtables.h>
     28 #include <linux/netfilter/xt_string.h>
     29 
     30 enum {
     31 	O_FROM = 0,
     32 	O_TO,
     33 	O_ALGO,
     34 	O_ICASE,
     35 	O_STRING,
     36 	O_HEX_STRING,
     37 	F_STRING     = 1 << O_STRING,
     38 	F_HEX_STRING = 1 << O_HEX_STRING,
     39 	F_OP_ANY     = F_STRING | F_HEX_STRING,
     40 };
     41 
     42 static void string_help(void)
     43 {
     44 	printf(
     45 "string match options:\n"
     46 "--from                       Offset to start searching from\n"
     47 "--to                         Offset to stop searching\n"
     48 "--algo                       Algorithm\n"
     49 "--icase                      Ignore case (default: 0)\n"
     50 "[!] --string string          Match a string in a packet\n"
     51 "[!] --hex-string string      Match a hex string in a packet\n");
     52 }
     53 
     54 #define s struct xt_string_info
     55 static const struct xt_option_entry string_opts[] = {
     56 	{.name = "from", .id = O_FROM, .type = XTTYPE_UINT16,
     57 	 .flags = XTOPT_PUT, XTOPT_POINTER(s, from_offset)},
     58 	{.name = "to", .id = O_TO, .type = XTTYPE_UINT16,
     59 	 .flags = XTOPT_PUT, XTOPT_POINTER(s, to_offset)},
     60 	{.name = "algo", .id = O_ALGO, .type = XTTYPE_STRING,
     61 	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, algo)},
     62 	{.name = "string", .id = O_STRING, .type = XTTYPE_STRING,
     63 	 .flags = XTOPT_INVERT, .excl = F_HEX_STRING},
     64 	{.name = "hex-string", .id = O_HEX_STRING, .type = XTTYPE_STRING,
     65 	 .flags = XTOPT_INVERT, .excl = F_STRING},
     66 	{.name = "icase", .id = O_ICASE, .type = XTTYPE_NONE},
     67 	XTOPT_TABLEEND,
     68 };
     69 #undef s
     70 
     71 static void string_init(struct xt_entry_match *m)
     72 {
     73 	struct xt_string_info *i = (struct xt_string_info *) m->data;
     74 
     75 	i->to_offset = UINT16_MAX;
     76 }
     77 
     78 static void
     79 parse_string(const char *s, struct xt_string_info *info)
     80 {
     81 	/* xt_string does not need \0 at the end of the pattern */
     82 	if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) {
     83 		strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE);
     84 		info->patlen = strnlen(s, XT_STRING_MAX_PATTERN_SIZE);
     85 		return;
     86 	}
     87 	xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s);
     88 }
     89 
     90 static void
     91 parse_hex_string(const char *s, struct xt_string_info *info)
     92 {
     93 	int i=0, slen, sindex=0, schar;
     94 	short hex_f = 0, literal_f = 0;
     95 	char hextmp[3];
     96 
     97 	slen = strlen(s);
     98 
     99 	if (slen == 0) {
    100 		xtables_error(PARAMETER_PROBLEM,
    101 			"STRING must contain at least one char");
    102 	}
    103 
    104 	while (i < slen) {
    105 		if (s[i] == '\\' && !hex_f) {
    106 			literal_f = 1;
    107 		} else if (s[i] == '\\') {
    108 			xtables_error(PARAMETER_PROBLEM,
    109 				"Cannot include literals in hex data");
    110 		} else if (s[i] == '|') {
    111 			if (hex_f)
    112 				hex_f = 0;
    113 			else {
    114 				hex_f = 1;
    115 				/* get past any initial whitespace just after the '|' */
    116 				while (s[i+1] == ' ')
    117 					i++;
    118 			}
    119 			if (i+1 >= slen)
    120 				break;
    121 			else
    122 				i++;  /* advance to the next character */
    123 		}
    124 
    125 		if (literal_f) {
    126 			if (i+1 >= slen) {
    127 				xtables_error(PARAMETER_PROBLEM,
    128 					"Bad literal placement at end of string");
    129 			}
    130 			info->pattern[sindex] = s[i+1];
    131 			i += 2;  /* skip over literal char */
    132 			literal_f = 0;
    133 		} else if (hex_f) {
    134 			if (i+1 >= slen) {
    135 				xtables_error(PARAMETER_PROBLEM,
    136 					"Odd number of hex digits");
    137 			}
    138 			if (i+2 >= slen) {
    139 				/* must end with a "|" */
    140 				xtables_error(PARAMETER_PROBLEM, "Invalid hex block");
    141 			}
    142 			if (! isxdigit(s[i])) /* check for valid hex char */
    143 				xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i]);
    144 			if (! isxdigit(s[i+1])) /* check for valid hex char */
    145 				xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i+1]);
    146 			hextmp[0] = s[i];
    147 			hextmp[1] = s[i+1];
    148 			hextmp[2] = '\0';
    149 			if (! sscanf(hextmp, "%x", &schar))
    150 				xtables_error(PARAMETER_PROBLEM,
    151 					"Invalid hex char `%c'", s[i]);
    152 			info->pattern[sindex] = (char) schar;
    153 			if (s[i+2] == ' ')
    154 				i += 3;  /* spaces included in the hex block */
    155 			else
    156 				i += 2;
    157 		} else {  /* the char is not part of hex data, so just copy */
    158 			info->pattern[sindex] = s[i];
    159 			i++;
    160 		}
    161 		if (sindex > XT_STRING_MAX_PATTERN_SIZE)
    162 			xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s);
    163 		sindex++;
    164 	}
    165 	info->patlen = sindex;
    166 }
    167 
    168 static void string_parse(struct xt_option_call *cb)
    169 {
    170 	struct xt_string_info *stringinfo = cb->data;
    171 	const unsigned int revision = (*cb->match)->u.user.revision;
    172 
    173 	xtables_option_parse(cb);
    174 	switch (cb->entry->id) {
    175 	case O_STRING:
    176 		parse_string(cb->arg, stringinfo);
    177 		if (cb->invert) {
    178 			if (revision == 0)
    179 				stringinfo->u.v0.invert = 1;
    180 			else
    181 				stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT;
    182 		}
    183 		break;
    184 	case O_HEX_STRING:
    185 		parse_hex_string(cb->arg, stringinfo);  /* sets length */
    186 		if (cb->invert) {
    187 			if (revision == 0)
    188 				stringinfo->u.v0.invert = 1;
    189 			else
    190 				stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT;
    191 		}
    192 		break;
    193 	case O_ICASE:
    194 		if (revision == 0)
    195 			xtables_error(VERSION_PROBLEM,
    196 				   "Kernel doesn't support --icase");
    197 
    198 		stringinfo->u.v1.flags |= XT_STRING_FLAG_IGNORECASE;
    199 		break;
    200 	}
    201 }
    202 
    203 static void string_check(struct xt_fcheck_call *cb)
    204 {
    205 	if (!(cb->xflags & F_OP_ANY))
    206 		xtables_error(PARAMETER_PROBLEM,
    207 			   "STRING match: You must specify `--string' or "
    208 			   "`--hex-string'");
    209 }
    210 
    211 /* Test to see if the string contains non-printable chars or quotes */
    212 static unsigned short int
    213 is_hex_string(const char *str, const unsigned short int len)
    214 {
    215 	unsigned int i;
    216 	for (i=0; i < len; i++)
    217 		if (! isprint(str[i]))
    218 			return 1;  /* string contains at least one non-printable char */
    219 	/* use hex output if the last char is a "\" */
    220 	if ((unsigned char) str[len-1] == 0x5c)
    221 		return 1;
    222 	return 0;
    223 }
    224 
    225 /* Print string with "|" chars included as one would pass to --hex-string */
    226 static void
    227 print_hex_string(const char *str, const unsigned short int len)
    228 {
    229 	unsigned int i;
    230 	/* start hex block */
    231 	printf("\"|");
    232 	for (i=0; i < len; i++) {
    233 		/* see if we need to prepend a zero */
    234 		if ((unsigned char) str[i] <= 0x0F)
    235 			printf("0%x", (unsigned char) str[i]);
    236 		else
    237 			printf("%x", (unsigned char) str[i]);
    238 	}
    239 	/* close hex block */
    240 	printf("|\" ");
    241 }
    242 
    243 static void
    244 print_string(const char *str, const unsigned short int len)
    245 {
    246 	unsigned int i;
    247 	printf(" \"");
    248 	for (i=0; i < len; i++) {
    249 		if ((unsigned char) str[i] == 0x22)  /* escape any embedded quotes */
    250 			printf("%c", 0x5c);
    251 		printf("%c", (unsigned char) str[i]);
    252 	}
    253 	printf("\"");  /* closing quote */
    254 }
    255 
    256 static void
    257 string_print(const void *ip, const struct xt_entry_match *match, int numeric)
    258 {
    259 	const struct xt_string_info *info =
    260 	    (const struct xt_string_info*) match->data;
    261 	const int revision = match->u.user.revision;
    262 	int invert = (revision == 0 ? info->u.v0.invert :
    263 				    info->u.v1.flags & XT_STRING_FLAG_INVERT);
    264 
    265 	if (is_hex_string(info->pattern, info->patlen)) {
    266 		printf(" STRING match %s", invert ? "!" : "");
    267 		print_hex_string(info->pattern, info->patlen);
    268 	} else {
    269 		printf(" STRING match %s", invert ? "!" : "");
    270 		print_string(info->pattern, info->patlen);
    271 	}
    272 	printf(" ALGO name %s", info->algo);
    273 	if (info->from_offset != 0)
    274 		printf(" FROM %u", info->from_offset);
    275 	if (info->to_offset != 0)
    276 		printf(" TO %u", info->to_offset);
    277 	if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE)
    278 		printf(" ICASE");
    279 }
    280 
    281 static void string_save(const void *ip, const struct xt_entry_match *match)
    282 {
    283 	const struct xt_string_info *info =
    284 	    (const struct xt_string_info*) match->data;
    285 	const int revision = match->u.user.revision;
    286 	int invert = (revision == 0 ? info->u.v0.invert :
    287 				    info->u.v1.flags & XT_STRING_FLAG_INVERT);
    288 
    289 	if (is_hex_string(info->pattern, info->patlen)) {
    290 		printf("%s --hex-string", (invert) ? " !" : "");
    291 		print_hex_string(info->pattern, info->patlen);
    292 	} else {
    293 		printf("%s --string", (invert) ? " !": "");
    294 		print_string(info->pattern, info->patlen);
    295 	}
    296 	printf(" --algo %s", info->algo);
    297 	if (info->from_offset != 0)
    298 		printf(" --from %u", info->from_offset);
    299 	if (info->to_offset != 0)
    300 		printf(" --to %u", info->to_offset);
    301 	if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE)
    302 		printf(" --icase");
    303 }
    304 
    305 
    306 static struct xtables_match string_mt_reg[] = {
    307 	{
    308 		.name          = "string",
    309 		.revision      = 0,
    310 		.family        = NFPROTO_UNSPEC,
    311 		.version       = XTABLES_VERSION,
    312 		.size          = XT_ALIGN(sizeof(struct xt_string_info)),
    313 		.userspacesize = offsetof(struct xt_string_info, config),
    314 		.help          = string_help,
    315 		.init          = string_init,
    316 		.print         = string_print,
    317 		.save          = string_save,
    318 		.x6_parse      = string_parse,
    319 		.x6_fcheck     = string_check,
    320 		.x6_options    = string_opts,
    321 	},
    322 	{
    323 		.name          = "string",
    324 		.revision      = 1,
    325 		.family        = NFPROTO_UNSPEC,
    326 		.version       = XTABLES_VERSION,
    327 		.size          = XT_ALIGN(sizeof(struct xt_string_info)),
    328 		.userspacesize = offsetof(struct xt_string_info, config),
    329 		.help          = string_help,
    330 		.init          = string_init,
    331 		.print         = string_print,
    332 		.save          = string_save,
    333 		.x6_parse      = string_parse,
    334 		.x6_fcheck     = string_check,
    335 		.x6_options    = string_opts,
    336 	},
    337 };
    338 
    339 void _init(void)
    340 {
    341 	xtables_register_matches(string_mt_reg, ARRAY_SIZE(string_mt_reg));
    342 }
    343