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