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