libxt_string.c revision e37d45ce390c2f5a7f1e64742b9100ecef0def54
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#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
30enum {
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
42static 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
55static 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
71static 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
78static void
79parse_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
90static void
91parse_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
168static 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
203static 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 */
212static unsigned short int
213is_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 */
226static void
227print_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
243static void
244print_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
256static void
257string_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
281static 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
306static 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
339void _init(void)
340{
341	xtables_register_matches(string_mt_reg, ARRAY_SIZE(string_mt_reg));
342}
343