libxt_string.c revision 9b488b992872d4d2b7ebf7897d74d52f4fb59e1c
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 <netdb.h>
25#include <string.h>
26#include <stdlib.h>
27#include <getopt.h>
28#include <ctype.h>
29#include <xtables.h>
30#include <stddef.h>
31#include <linux/netfilter/xt_string.h>
32
33/* Function which prints out usage message. */
34static void string_help(void)
35{
36	printf(
37"string match options:\n"
38"--from                       Offset to start searching from\n"
39"--to                         Offset to stop searching\n"
40"--algo	                      Algorithm\n"
41"[!] --string string          Match a string in a packet\n"
42"[!] --hex-string string      Match a hex string in a packet\n");
43}
44
45static const struct option string_opts[] = {
46	{ "from", 1, NULL, '1' },
47	{ "to", 1, NULL, '2' },
48	{ "algo", 1, NULL, '3' },
49	{ "string", 1, NULL, '4' },
50	{ "hex-string", 1, NULL, '5' },
51	{ .name = NULL }
52};
53
54static void string_init(struct xt_entry_match *m)
55{
56	struct xt_string_info *i = (struct xt_string_info *) m->data;
57
58	if (i->to_offset == 0)
59		i->to_offset = (u_int16_t) ~0UL;
60}
61
62static void
63parse_string(const char *s, struct xt_string_info *info)
64{
65	if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) {
66		strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE);
67		info->patlen = strlen(s);
68		return;
69	}
70	exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
71}
72
73static void
74parse_algo(const char *s, struct xt_string_info *info)
75{
76	if (strlen(s) <= XT_STRING_MAX_ALGO_NAME_SIZE) {
77		strncpy(info->algo, s, XT_STRING_MAX_ALGO_NAME_SIZE);
78		return;
79	}
80	exit_error(PARAMETER_PROBLEM, "ALGO too long `%s'", s);
81}
82
83static void
84parse_hex_string(const char *s, struct xt_string_info *info)
85{
86	int i=0, slen, sindex=0, schar;
87	short hex_f = 0, literal_f = 0;
88	char hextmp[3];
89
90	slen = strlen(s);
91
92	if (slen == 0) {
93		exit_error(PARAMETER_PROBLEM,
94			"STRING must contain at least one char");
95	}
96
97	while (i < slen) {
98		if (s[i] == '\\' && !hex_f) {
99			literal_f = 1;
100		} else if (s[i] == '\\') {
101			exit_error(PARAMETER_PROBLEM,
102				"Cannot include literals in hex data");
103		} else if (s[i] == '|') {
104			if (hex_f)
105				hex_f = 0;
106			else {
107				hex_f = 1;
108				/* get past any initial whitespace just after the '|' */
109				while (s[i+1] == ' ')
110					i++;
111			}
112			if (i+1 >= slen)
113				break;
114			else
115				i++;  /* advance to the next character */
116		}
117
118		if (literal_f) {
119			if (i+1 >= slen) {
120				exit_error(PARAMETER_PROBLEM,
121					"Bad literal placement at end of string");
122			}
123			info->pattern[sindex] = s[i+1];
124			i += 2;  /* skip over literal char */
125			literal_f = 0;
126		} else if (hex_f) {
127			if (i+1 >= slen) {
128				exit_error(PARAMETER_PROBLEM,
129					"Odd number of hex digits");
130			}
131			if (i+2 >= slen) {
132				/* must end with a "|" */
133				exit_error(PARAMETER_PROBLEM, "Invalid hex block");
134			}
135			if (! isxdigit(s[i])) /* check for valid hex char */
136				exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i]);
137			if (! isxdigit(s[i+1])) /* check for valid hex char */
138				exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i+1]);
139			hextmp[0] = s[i];
140			hextmp[1] = s[i+1];
141			hextmp[2] = '\0';
142			if (! sscanf(hextmp, "%x", &schar))
143				exit_error(PARAMETER_PROBLEM,
144					"Invalid hex char `%c'", s[i]);
145			info->pattern[sindex] = (char) schar;
146			if (s[i+2] == ' ')
147				i += 3;  /* spaces included in the hex block */
148			else
149				i += 2;
150		} else {  /* the char is not part of hex data, so just copy */
151			info->pattern[sindex] = s[i];
152			i++;
153		}
154		if (sindex > XT_STRING_MAX_PATTERN_SIZE)
155			exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
156		sindex++;
157	}
158	info->patlen = sindex;
159}
160
161#define STRING 0x1
162#define ALGO   0x2
163#define FROM   0x4
164#define TO     0x8
165
166/* Function which parses command options; returns true if it
167   ate an option */
168static int
169string_parse(int c, char **argv, int invert, unsigned int *flags,
170             const void *entry, struct xt_entry_match **match)
171{
172	struct xt_string_info *stringinfo = (struct xt_string_info *)(*match)->data;
173
174	switch (c) {
175	case '1':
176		if (*flags & FROM)
177			exit_error(PARAMETER_PROBLEM,
178				   "Can't specify multiple --from");
179		stringinfo->from_offset = atoi(optarg);
180		*flags |= FROM;
181		break;
182	case '2':
183		if (*flags & TO)
184			exit_error(PARAMETER_PROBLEM,
185				   "Can't specify multiple --to");
186		stringinfo->to_offset = atoi(optarg);
187		*flags |= TO;
188		break;
189	case '3':
190		if (*flags & ALGO)
191			exit_error(PARAMETER_PROBLEM,
192				   "Can't specify multiple --algo");
193		parse_algo(optarg, stringinfo);
194		*flags |= ALGO;
195		break;
196	case '4':
197		if (*flags & STRING)
198			exit_error(PARAMETER_PROBLEM,
199				   "Can't specify multiple --string");
200		check_inverse(optarg, &invert, &optind, 0);
201		parse_string(argv[optind-1], stringinfo);
202		if (invert)
203			stringinfo->invert = 1;
204		stringinfo->patlen=strlen((char *)&stringinfo->pattern);
205		*flags |= STRING;
206		break;
207
208	case '5':
209		if (*flags & STRING)
210			exit_error(PARAMETER_PROBLEM,
211				   "Can't specify multiple --hex-string");
212
213		check_inverse(optarg, &invert, &optind, 0);
214		parse_hex_string(argv[optind-1], stringinfo);  /* sets length */
215		if (invert)
216			stringinfo->invert = 1;
217		*flags |= STRING;
218		break;
219
220	default:
221		return 0;
222	}
223	return 1;
224}
225
226
227/* Final check; must have specified --string. */
228static void string_check(unsigned int flags)
229{
230	if (!(flags & STRING))
231		exit_error(PARAMETER_PROBLEM,
232			   "STRING match: You must specify `--string' or "
233			   "`--hex-string'");
234	if (!(flags & ALGO))
235		exit_error(PARAMETER_PROBLEM,
236			   "STRING match: You must specify `--algo'");
237}
238
239/* Test to see if the string contains non-printable chars or quotes */
240static unsigned short int
241is_hex_string(const char *str, const unsigned short int len)
242{
243	unsigned int i;
244	for (i=0; i < len; i++)
245		if (! isprint(str[i]))
246			return 1;  /* string contains at least one non-printable char */
247	/* use hex output if the last char is a "\" */
248	if ((unsigned char) str[len-1] == 0x5c)
249		return 1;
250	return 0;
251}
252
253/* Print string with "|" chars included as one would pass to --hex-string */
254static void
255print_hex_string(const char *str, const unsigned short int len)
256{
257	unsigned int i;
258	/* start hex block */
259	printf("\"|");
260	for (i=0; i < len; i++) {
261		/* see if we need to prepend a zero */
262		if ((unsigned char) str[i] <= 0x0F)
263			printf("0%x", (unsigned char) str[i]);
264		else
265			printf("%x", (unsigned char) str[i]);
266	}
267	/* close hex block */
268	printf("|\" ");
269}
270
271static void
272print_string(const char *str, const unsigned short int len)
273{
274	unsigned int i;
275	printf("\"");
276	for (i=0; i < len; i++) {
277		if ((unsigned char) str[i] == 0x22)  /* escape any embedded quotes */
278			printf("%c", 0x5c);
279		printf("%c", (unsigned char) str[i]);
280	}
281	printf("\" ");  /* closing space and quote */
282}
283
284/* Prints out the matchinfo. */
285static void
286string_print(const void *ip, const struct xt_entry_match *match, int numeric)
287{
288	const struct xt_string_info *info =
289	    (const struct xt_string_info*) match->data;
290
291	if (is_hex_string(info->pattern, info->patlen)) {
292		printf("STRING match %s", (info->invert) ? "!" : "");
293		print_hex_string(info->pattern, info->patlen);
294	} else {
295		printf("STRING match %s", (info->invert) ? "!" : "");
296		print_string(info->pattern, info->patlen);
297	}
298	printf("ALGO name %s ", info->algo);
299	if (info->from_offset != 0)
300		printf("FROM %u ", info->from_offset);
301	if (info->to_offset != 0)
302		printf("TO %u ", info->to_offset);
303}
304
305
306/* Saves the union ipt_matchinfo in parseable form to stdout. */
307static void string_save(const void *ip, const struct xt_entry_match *match)
308{
309	const struct xt_string_info *info =
310	    (const struct xt_string_info*) match->data;
311
312	if (is_hex_string(info->pattern, info->patlen)) {
313		printf("--hex-string %s", (info->invert) ? "! ": "");
314		print_hex_string(info->pattern, info->patlen);
315	} else {
316		printf("--string %s", (info->invert) ? "! ": "");
317		print_string(info->pattern, info->patlen);
318	}
319	printf("--algo %s ", info->algo);
320	if (info->from_offset != 0)
321		printf("--from %u ", info->from_offset);
322	if (info->to_offset != 0)
323		printf("--to %u ", info->to_offset);
324}
325
326
327static struct xtables_match string_match = {
328    .name		= "string",
329    .family		= AF_UNSPEC,
330    .version		= XTABLES_VERSION,
331    .size		= XT_ALIGN(sizeof(struct xt_string_info)),
332    .userspacesize	= offsetof(struct xt_string_info, config),
333    .help		= string_help,
334    .init		= string_init,
335    .parse		= string_parse,
336    .final_check	= string_check,
337    .print		= string_print,
338    .save		= string_save,
339    .extra_opts		= string_opts,
340};
341
342void _init(void)
343{
344	xtables_register_match(&string_match);
345}
346