blob: 50b7f61e45e4236f2044587e94148d6454dade63 [file] [log] [blame]
Emmanuel Roger30719132000-10-04 15:19:31 +00001/* Shared library add-on to iptables to add string matching support.
2 *
3 * Copyright (C) 2000 Emmanuel Roger <winfield@freegates.be>
András Kis-Szabó764316a2001-02-26 17:31:20 +00004 *
Pablo Neirac6fbf412005-08-28 08:09:44 +00005 * 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 *
András Kis-Szabó764316a2001-02-26 17:31:20 +000010 * ChangeLog
Michael Rashb807fb32004-01-05 09:50:12 +000011 * 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 *
András Kis-Szabó764316a2001-02-26 17:31:20 +000018 * 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.
Emmanuel Roger30719132000-10-04 15:19:31 +000022 */
23#include <stdio.h>
24#include <netdb.h>
25#include <string.h>
26#include <stdlib.h>
27#include <getopt.h>
Michael Rashf8ac3292003-02-26 17:34:13 +000028#include <ctype.h>
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +000029#include <xtables.h>
Pablo Neirac6fbf412005-08-28 08:09:44 +000030#include <stddef.h>
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +000031#include <linux/netfilter/xt_string.h>
Emmanuel Roger30719132000-10-04 15:19:31 +000032
33/* Function which prints out usage message. */
Jan Engelhardt181dead2007-10-04 16:27:07 +000034static void string_help(void)
Emmanuel Roger30719132000-10-04 15:19:31 +000035{
36 printf(
37"STRING match v%s options:\n"
Pablo Neirac6fbf412005-08-28 08:09:44 +000038"--from Offset to start searching from\n"
39"--to Offset to stop searching\n"
40"--algo Algorithm\n"
Michael Rash96d85932003-04-21 07:27:03 +000041"--string [!] string Match a string in a packet\n"
42"--hex-string [!] string Match a hex string in a packet\n",
Harald Welte80fe35d2002-05-29 13:08:15 +000043IPTABLES_VERSION);
Emmanuel Roger30719132000-10-04 15:19:31 +000044}
45
Jan Engelhardt181dead2007-10-04 16:27:07 +000046static const struct option string_opts[] = {
Patrick McHardy500f4832007-09-08 15:59:04 +000047 { "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 { }
Emmanuel Roger30719132000-10-04 15:19:31 +000053};
54
Jan Engelhardt181dead2007-10-04 16:27:07 +000055static void string_init(struct xt_entry_match *m)
Pablo Neirac6fbf412005-08-28 08:09:44 +000056{
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +000057 struct xt_string_info *i = (struct xt_string_info *) m->data;
Pablo Neirac6fbf412005-08-28 08:09:44 +000058
59 if (i->to_offset == 0)
60 i->to_offset = (u_int16_t) ~0UL;
61}
62
63static void
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +000064parse_string(const char *s, struct xt_string_info *info)
Emmanuel Roger30719132000-10-04 15:19:31 +000065{
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +000066 if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) {
67 strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE);
Pablo Neirac6fbf412005-08-28 08:09:44 +000068 info->patlen = strlen(s);
69 return;
70 }
71 exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
Michael Rash96d85932003-04-21 07:27:03 +000072}
73
Pablo Neirac6fbf412005-08-28 08:09:44 +000074static void
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +000075parse_algo(const char *s, struct xt_string_info *info)
Pablo Neirac6fbf412005-08-28 08:09:44 +000076{
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +000077 if (strlen(s) <= XT_STRING_MAX_ALGO_NAME_SIZE) {
78 strncpy(info->algo, s, XT_STRING_MAX_ALGO_NAME_SIZE);
Pablo Neirac6fbf412005-08-28 08:09:44 +000079 return;
80 }
81 exit_error(PARAMETER_PROBLEM, "ALGO too long `%s'", s);
82}
Stephane Ouellette451f3ea2003-04-27 12:59:00 +000083
Michael Rash96d85932003-04-21 07:27:03 +000084static void
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +000085parse_hex_string(const char *s, struct xt_string_info *info)
Michael Rash96d85932003-04-21 07:27:03 +000086{
Michael Rashf8ac3292003-02-26 17:34:13 +000087 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;
Michael Rashb807fb32004-01-05 09:50:12 +0000107 else {
Michael Rashf8ac3292003-02-26 17:34:13 +0000108 hex_f = 1;
Michael Rashb807fb32004-01-05 09:50:12 +0000109 /* get past any initial whitespace just after the '|' */
110 while (s[i+1] == ' ')
111 i++;
112 }
Michael Rashf8ac3292003-02-26 17:34:13 +0000113 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 }
Pablo Neirac6fbf412005-08-28 08:09:44 +0000124 info->pattern[sindex] = s[i+1];
Michael Rashf8ac3292003-02-26 17:34:13 +0000125 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 }
Michael Rash96d85932003-04-21 07:27:03 +0000136 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]);
Michael Rashf8ac3292003-02-26 17:34:13 +0000140 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]);
Pablo Neirac6fbf412005-08-28 08:09:44 +0000146 info->pattern[sindex] = (char) schar;
Michael Rashf8ac3292003-02-26 17:34:13 +0000147 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 */
Pablo Neirac6fbf412005-08-28 08:09:44 +0000152 info->pattern[sindex] = s[i];
Michael Rashf8ac3292003-02-26 17:34:13 +0000153 i++;
154 }
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +0000155 if (sindex > XT_STRING_MAX_PATTERN_SIZE)
Michael Rashf8ac3292003-02-26 17:34:13 +0000156 exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
157 sindex++;
158 }
Pablo Neirac6fbf412005-08-28 08:09:44 +0000159 info->patlen = sindex;
Emmanuel Roger30719132000-10-04 15:19:31 +0000160}
161
Pablo Neirac6fbf412005-08-28 08:09:44 +0000162#define STRING 0x1
163#define ALGO 0x2
164#define FROM 0x4
165#define TO 0x8
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000166
Emmanuel Roger30719132000-10-04 15:19:31 +0000167/* Function which parses command options; returns true if it
168 ate an option */
169static int
Jan Engelhardt181dead2007-10-04 16:27:07 +0000170string_parse(int c, char **argv, int invert, unsigned int *flags,
171 const void *entry, struct xt_entry_match **match)
Emmanuel Roger30719132000-10-04 15:19:31 +0000172{
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +0000173 struct xt_string_info *stringinfo = (struct xt_string_info *)(*match)->data;
Emmanuel Roger30719132000-10-04 15:19:31 +0000174
175 switch (c) {
176 case '1':
Pablo Neirac6fbf412005-08-28 08:09:44 +0000177 if (*flags & FROM)
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000178 exit_error(PARAMETER_PROBLEM,
Pablo Neirac6fbf412005-08-28 08:09:44 +0000179 "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");
Harald Welteb77f1da2002-03-14 11:35:58 +0000201 check_inverse(optarg, &invert, &optind, 0);
Emmanuel Roger30719132000-10-04 15:19:31 +0000202 parse_string(argv[optind-1], stringinfo);
203 if (invert)
204 stringinfo->invert = 1;
Pablo Neirac6fbf412005-08-28 08:09:44 +0000205 stringinfo->patlen=strlen((char *)&stringinfo->pattern);
206 *flags |= STRING;
Michael Rash96d85932003-04-21 07:27:03 +0000207 break;
208
Pablo Neirac6fbf412005-08-28 08:09:44 +0000209 case '5':
210 if (*flags & STRING)
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000211 exit_error(PARAMETER_PROBLEM,
Pablo Neirac6fbf412005-08-28 08:09:44 +0000212 "Can't specify multiple --hex-string");
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000213
Michael Rash96d85932003-04-21 07:27:03 +0000214 check_inverse(optarg, &invert, &optind, 0);
215 parse_hex_string(argv[optind-1], stringinfo); /* sets length */
216 if (invert)
217 stringinfo->invert = 1;
Pablo Neirac6fbf412005-08-28 08:09:44 +0000218 *flags |= STRING;
Emmanuel Roger30719132000-10-04 15:19:31 +0000219 break;
220
221 default:
222 return 0;
223 }
224 return 1;
225}
226
Emmanuel Roger30719132000-10-04 15:19:31 +0000227
228/* Final check; must have specified --string. */
Jan Engelhardt181dead2007-10-04 16:27:07 +0000229static void string_check(unsigned int flags)
Emmanuel Roger30719132000-10-04 15:19:31 +0000230{
Pablo Neirac6fbf412005-08-28 08:09:44 +0000231 if (!(flags & STRING))
Emmanuel Roger30719132000-10-04 15:19:31 +0000232 exit_error(PARAMETER_PROBLEM,
Pablo Neirac6fbf412005-08-28 08:09:44 +0000233 "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'");
Emmanuel Roger30719132000-10-04 15:19:31 +0000238}
239
Michael Rashb807fb32004-01-05 09:50:12 +0000240/* 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}
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000284
Emmanuel Roger30719132000-10-04 15:19:31 +0000285/* Prints out the matchinfo. */
286static void
Jan Engelhardt181dead2007-10-04 16:27:07 +0000287string_print(const void *ip, const struct xt_entry_match *match, int numeric)
Emmanuel Roger30719132000-10-04 15:19:31 +0000288{
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +0000289 const struct xt_string_info *info =
290 (const struct xt_string_info*) match->data;
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000291
Pablo Neirac6fbf412005-08-28 08:09:44 +0000292 if (is_hex_string(info->pattern, info->patlen)) {
Michael Rashb807fb32004-01-05 09:50:12 +0000293 printf("STRING match %s", (info->invert) ? "!" : "");
Pablo Neirac6fbf412005-08-28 08:09:44 +0000294 print_hex_string(info->pattern, info->patlen);
Michael Rashb807fb32004-01-05 09:50:12 +0000295 } else {
296 printf("STRING match %s", (info->invert) ? "!" : "");
Pablo Neirac6fbf412005-08-28 08:09:44 +0000297 print_string(info->pattern, info->patlen);
Michael Rashb807fb32004-01-05 09:50:12 +0000298 }
Pablo Neirac6fbf412005-08-28 08:09:44 +0000299 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)
Patrick McHardy8a0b6ea2007-01-11 08:23:17 +0000303 printf("TO %u ", info->to_offset);
Emmanuel Roger30719132000-10-04 15:19:31 +0000304}
305
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000306
307/* Saves the union ipt_matchinfo in parseable form to stdout. */
Jan Engelhardt181dead2007-10-04 16:27:07 +0000308static void string_save(const void *ip, const struct xt_entry_match *match)
Emmanuel Roger30719132000-10-04 15:19:31 +0000309{
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +0000310 const struct xt_string_info *info =
311 (const struct xt_string_info*) match->data;
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000312
Pablo Neirac6fbf412005-08-28 08:09:44 +0000313 if (is_hex_string(info->pattern, info->patlen)) {
Michael Rashb807fb32004-01-05 09:50:12 +0000314 printf("--hex-string %s", (info->invert) ? "! ": "");
Pablo Neirac6fbf412005-08-28 08:09:44 +0000315 print_hex_string(info->pattern, info->patlen);
Michael Rashb807fb32004-01-05 09:50:12 +0000316 } else {
317 printf("--string %s", (info->invert) ? "! ": "");
Pablo Neirac6fbf412005-08-28 08:09:44 +0000318 print_string(info->pattern, info->patlen);
Michael Rashb807fb32004-01-05 09:50:12 +0000319 }
Pablo Neirac6fbf412005-08-28 08:09:44 +0000320 printf("--algo %s ", info->algo);
321 if (info->from_offset != 0)
Michael Rash0829a2b2006-01-30 09:02:45 +0000322 printf("--from %u ", info->from_offset);
Pablo Neirac6fbf412005-08-28 08:09:44 +0000323 if (info->to_offset != 0)
Michael Rash0829a2b2006-01-30 09:02:45 +0000324 printf("--to %u ", info->to_offset);
Emmanuel Roger30719132000-10-04 15:19:31 +0000325}
326
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000327
Jan Engelhardt181dead2007-10-04 16:27:07 +0000328static struct xtables_match string_match = {
Pablo Neirac6fbf412005-08-28 08:09:44 +0000329 .name = "string",
Jan Engelhardt181dead2007-10-04 16:27:07 +0000330 .family = AF_INET,
Pablo Neirac6fbf412005-08-28 08:09:44 +0000331 .version = IPTABLES_VERSION,
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +0000332 .size = XT_ALIGN(sizeof(struct xt_string_info)),
333 .userspacesize = offsetof(struct xt_string_info, config),
Jan Engelhardt181dead2007-10-04 16:27:07 +0000334 .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,
Emmanuel Roger30719132000-10-04 15:19:31 +0000341};
342
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000343
Jan Engelhardt181dead2007-10-04 16:27:07 +0000344static struct xtables_match string_match6 = {
Yasuyuki KOZAKAI4ccb6f52007-07-24 06:52:16 +0000345 .name = "string",
Jan Engelhardt181dead2007-10-04 16:27:07 +0000346 .family = AF_INET6,
Yasuyuki KOZAKAI4ccb6f52007-07-24 06:52:16 +0000347 .version = IPTABLES_VERSION,
348 .size = XT_ALIGN(sizeof(struct xt_string_info)),
349 .userspacesize = offsetof(struct xt_string_info, config),
Jan Engelhardt181dead2007-10-04 16:27:07 +0000350 .help = string_help,
351 .init = string_init,
352 .parse = string_parse,
353 .final_check = string_check,
354 .print = string_print,
355 .save = string_save,
356 .extra_opts = string_opts,
Yasuyuki KOZAKAI4ccb6f52007-07-24 06:52:16 +0000357};
358
Emmanuel Roger30719132000-10-04 15:19:31 +0000359void _init(void)
360{
Jan Engelhardt181dead2007-10-04 16:27:07 +0000361 xtables_register_match(&string_match);
362 xtables_register_match(&string_match6);
Emmanuel Roger30719132000-10-04 15:19:31 +0000363}