blob: 0408c230ed9081c320158199a0128ee3be4cf3d5 [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
Jan Engelhardt181dead2007-10-04 16:27:07 +000033static void string_help(void)
Emmanuel Roger30719132000-10-04 15:19:31 +000034{
35 printf(
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020036"string match options:\n"
Pablo Neirac6fbf412005-08-28 08:09:44 +000037"--from Offset to start searching from\n"
38"--to Offset to stop searching\n"
Joonwoo Park78d2d142008-07-07 13:32:25 +020039"--algo Algorithm\n"
40"--icase Ignore case (default: 0)\n"
Jan Engelhardt9b488b92008-06-08 19:11:51 +020041"[!] --string string Match a string in a packet\n"
42"[!] --hex-string string Match a hex string in a packet\n");
Emmanuel Roger30719132000-10-04 15:19:31 +000043}
44
Jan Engelhardt181dead2007-10-04 16:27:07 +000045static const struct option string_opts[] = {
Patrick McHardy500f4832007-09-08 15:59:04 +000046 { "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' },
Joonwoo Park78d2d142008-07-07 13:32:25 +020051 { "icase", 0, NULL, '6' },
Max Kellermann9ee386a2008-01-29 13:48:05 +000052 { .name = NULL }
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)
Jan Engelhardta8097542009-01-27 17:39:01 +010060 i->to_offset = UINT16_MAX;
Pablo Neirac6fbf412005-08-28 08:09:44 +000061}
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
Joonwoo Park78d2d142008-07-07 13:32:25 +0200166#define ICASE 0x10
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000167
Emmanuel Roger30719132000-10-04 15:19:31 +0000168static int
Jan Engelhardt181dead2007-10-04 16:27:07 +0000169string_parse(int c, char **argv, int invert, unsigned int *flags,
170 const void *entry, struct xt_entry_match **match)
Emmanuel Roger30719132000-10-04 15:19:31 +0000171{
Joonwoo Park78d2d142008-07-07 13:32:25 +0200172 struct xt_string_info *stringinfo =
173 (struct xt_string_info *)(*match)->data;
174 const int revision = (*match)->u.user.revision;
Emmanuel Roger30719132000-10-04 15:19:31 +0000175
176 switch (c) {
177 case '1':
Pablo Neirac6fbf412005-08-28 08:09:44 +0000178 if (*flags & FROM)
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000179 exit_error(PARAMETER_PROBLEM,
Pablo Neirac6fbf412005-08-28 08:09:44 +0000180 "Can't specify multiple --from");
181 stringinfo->from_offset = atoi(optarg);
182 *flags |= FROM;
183 break;
184 case '2':
185 if (*flags & TO)
186 exit_error(PARAMETER_PROBLEM,
187 "Can't specify multiple --to");
188 stringinfo->to_offset = atoi(optarg);
189 *flags |= TO;
190 break;
191 case '3':
192 if (*flags & ALGO)
193 exit_error(PARAMETER_PROBLEM,
194 "Can't specify multiple --algo");
195 parse_algo(optarg, stringinfo);
196 *flags |= ALGO;
197 break;
198 case '4':
199 if (*flags & STRING)
200 exit_error(PARAMETER_PROBLEM,
201 "Can't specify multiple --string");
Harald Welteb77f1da2002-03-14 11:35:58 +0000202 check_inverse(optarg, &invert, &optind, 0);
Emmanuel Roger30719132000-10-04 15:19:31 +0000203 parse_string(argv[optind-1], stringinfo);
Joonwoo Park78d2d142008-07-07 13:32:25 +0200204 if (invert) {
205 if (revision == 0)
206 stringinfo->u.v0.invert = 1;
207 else
208 stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT;
209 }
Pablo Neirac6fbf412005-08-28 08:09:44 +0000210 stringinfo->patlen=strlen((char *)&stringinfo->pattern);
211 *flags |= STRING;
Michael Rash96d85932003-04-21 07:27:03 +0000212 break;
213
Pablo Neirac6fbf412005-08-28 08:09:44 +0000214 case '5':
215 if (*flags & STRING)
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000216 exit_error(PARAMETER_PROBLEM,
Pablo Neirac6fbf412005-08-28 08:09:44 +0000217 "Can't specify multiple --hex-string");
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000218
Michael Rash96d85932003-04-21 07:27:03 +0000219 check_inverse(optarg, &invert, &optind, 0);
220 parse_hex_string(argv[optind-1], stringinfo); /* sets length */
Joonwoo Park78d2d142008-07-07 13:32:25 +0200221 if (invert) {
222 if (revision == 0)
223 stringinfo->u.v0.invert = 1;
224 else
225 stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT;
226 }
Pablo Neirac6fbf412005-08-28 08:09:44 +0000227 *flags |= STRING;
Emmanuel Roger30719132000-10-04 15:19:31 +0000228 break;
229
Joonwoo Park78d2d142008-07-07 13:32:25 +0200230 case '6':
231 if (revision == 0)
232 exit_error(VERSION_PROBLEM,
233 "Kernel doesn't support --icase");
234
235 stringinfo->u.v1.flags |= XT_STRING_FLAG_IGNORECASE;
236 *flags |= ICASE;
237 break;
238
Emmanuel Roger30719132000-10-04 15:19:31 +0000239 default:
240 return 0;
241 }
242 return 1;
243}
244
Jan Engelhardt181dead2007-10-04 16:27:07 +0000245static void string_check(unsigned int flags)
Emmanuel Roger30719132000-10-04 15:19:31 +0000246{
Pablo Neirac6fbf412005-08-28 08:09:44 +0000247 if (!(flags & STRING))
Emmanuel Roger30719132000-10-04 15:19:31 +0000248 exit_error(PARAMETER_PROBLEM,
Pablo Neirac6fbf412005-08-28 08:09:44 +0000249 "STRING match: You must specify `--string' or "
250 "`--hex-string'");
251 if (!(flags & ALGO))
252 exit_error(PARAMETER_PROBLEM,
253 "STRING match: You must specify `--algo'");
Emmanuel Roger30719132000-10-04 15:19:31 +0000254}
255
Michael Rashb807fb32004-01-05 09:50:12 +0000256/* Test to see if the string contains non-printable chars or quotes */
257static unsigned short int
258is_hex_string(const char *str, const unsigned short int len)
259{
260 unsigned int i;
261 for (i=0; i < len; i++)
262 if (! isprint(str[i]))
263 return 1; /* string contains at least one non-printable char */
264 /* use hex output if the last char is a "\" */
265 if ((unsigned char) str[len-1] == 0x5c)
266 return 1;
267 return 0;
268}
269
270/* Print string with "|" chars included as one would pass to --hex-string */
271static void
272print_hex_string(const char *str, const unsigned short int len)
273{
274 unsigned int i;
275 /* start hex block */
276 printf("\"|");
277 for (i=0; i < len; i++) {
278 /* see if we need to prepend a zero */
279 if ((unsigned char) str[i] <= 0x0F)
280 printf("0%x", (unsigned char) str[i]);
281 else
282 printf("%x", (unsigned char) str[i]);
283 }
284 /* close hex block */
285 printf("|\" ");
286}
287
288static void
289print_string(const char *str, const unsigned short int len)
290{
291 unsigned int i;
292 printf("\"");
293 for (i=0; i < len; i++) {
294 if ((unsigned char) str[i] == 0x22) /* escape any embedded quotes */
295 printf("%c", 0x5c);
296 printf("%c", (unsigned char) str[i]);
297 }
298 printf("\" "); /* closing space and quote */
299}
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000300
Emmanuel Roger30719132000-10-04 15:19:31 +0000301static void
Jan Engelhardt181dead2007-10-04 16:27:07 +0000302string_print(const void *ip, const struct xt_entry_match *match, int numeric)
Emmanuel Roger30719132000-10-04 15:19:31 +0000303{
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +0000304 const struct xt_string_info *info =
305 (const struct xt_string_info*) match->data;
Joonwoo Park78d2d142008-07-07 13:32:25 +0200306 const int revision = match->u.user.revision;
307 int invert = (revision == 0 ? info->u.v0.invert :
308 info->u.v1.flags & XT_STRING_FLAG_INVERT);
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000309
Pablo Neirac6fbf412005-08-28 08:09:44 +0000310 if (is_hex_string(info->pattern, info->patlen)) {
Joonwoo Park78d2d142008-07-07 13:32:25 +0200311 printf("STRING match %s", invert ? "!" : "");
Pablo Neirac6fbf412005-08-28 08:09:44 +0000312 print_hex_string(info->pattern, info->patlen);
Michael Rashb807fb32004-01-05 09:50:12 +0000313 } else {
Joonwoo Park78d2d142008-07-07 13:32:25 +0200314 printf("STRING match %s", invert ? "!" : "");
Pablo Neirac6fbf412005-08-28 08:09:44 +0000315 print_string(info->pattern, info->patlen);
Michael Rashb807fb32004-01-05 09:50:12 +0000316 }
Pablo Neirac6fbf412005-08-28 08:09:44 +0000317 printf("ALGO name %s ", info->algo);
318 if (info->from_offset != 0)
319 printf("FROM %u ", info->from_offset);
320 if (info->to_offset != 0)
Patrick McHardy8a0b6ea2007-01-11 08:23:17 +0000321 printf("TO %u ", info->to_offset);
Joonwoo Park78d2d142008-07-07 13:32:25 +0200322 if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE)
323 printf("ICASE ");
Emmanuel Roger30719132000-10-04 15:19:31 +0000324}
325
Jan Engelhardt181dead2007-10-04 16:27:07 +0000326static void string_save(const void *ip, const struct xt_entry_match *match)
Emmanuel Roger30719132000-10-04 15:19:31 +0000327{
Yasuyuki KOZAKAI6ac58e32007-07-24 06:50:03 +0000328 const struct xt_string_info *info =
329 (const struct xt_string_info*) match->data;
Joonwoo Park78d2d142008-07-07 13:32:25 +0200330 const int revision = match->u.user.revision;
331 int invert = (revision == 0 ? info->u.v0.invert :
332 info->u.v1.flags & XT_STRING_FLAG_INVERT);
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000333
Pablo Neirac6fbf412005-08-28 08:09:44 +0000334 if (is_hex_string(info->pattern, info->patlen)) {
Jan Engelhardtcea9f712008-12-09 15:06:20 +0100335 printf("%s--hex-string ", (invert) ? "! ": "");
Pablo Neirac6fbf412005-08-28 08:09:44 +0000336 print_hex_string(info->pattern, info->patlen);
Michael Rashb807fb32004-01-05 09:50:12 +0000337 } else {
Jan Engelhardtcea9f712008-12-09 15:06:20 +0100338 printf("%s--string ", (invert) ? "! ": "");
Pablo Neirac6fbf412005-08-28 08:09:44 +0000339 print_string(info->pattern, info->patlen);
Michael Rashb807fb32004-01-05 09:50:12 +0000340 }
Pablo Neirac6fbf412005-08-28 08:09:44 +0000341 printf("--algo %s ", info->algo);
342 if (info->from_offset != 0)
Michael Rash0829a2b2006-01-30 09:02:45 +0000343 printf("--from %u ", info->from_offset);
Pablo Neirac6fbf412005-08-28 08:09:44 +0000344 if (info->to_offset != 0)
Michael Rash0829a2b2006-01-30 09:02:45 +0000345 printf("--to %u ", info->to_offset);
Joonwoo Park78d2d142008-07-07 13:32:25 +0200346 if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE)
347 printf("--icase ");
Emmanuel Roger30719132000-10-04 15:19:31 +0000348}
349
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000350
Jan Engelhardt181dead2007-10-04 16:27:07 +0000351static struct xtables_match string_match = {
Pablo Neirac6fbf412005-08-28 08:09:44 +0000352 .name = "string",
Joonwoo Park78d2d142008-07-07 13:32:25 +0200353 .revision = 0,
354 .family = AF_UNSPEC,
355 .version = XTABLES_VERSION,
356 .size = XT_ALIGN(sizeof(struct xt_string_info)),
357 .userspacesize = offsetof(struct xt_string_info, config),
358 .help = string_help,
359 .init = string_init,
360 .parse = string_parse,
361 .final_check = string_check,
362 .print = string_print,
363 .save = string_save,
364 .extra_opts = string_opts,
365};
366
367static struct xtables_match string_match_v1 = {
368 .name = "string",
369 .revision = 1,
Jan Engelhardt23545c22008-02-14 04:23:04 +0100370 .family = AF_UNSPEC,
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200371 .version = XTABLES_VERSION,
Yasuyuki KOZAKAI4ccb6f52007-07-24 06:52:16 +0000372 .size = XT_ALIGN(sizeof(struct xt_string_info)),
373 .userspacesize = offsetof(struct xt_string_info, config),
Jan Engelhardt181dead2007-10-04 16:27:07 +0000374 .help = string_help,
375 .init = string_init,
376 .parse = string_parse,
377 .final_check = string_check,
378 .print = string_print,
379 .save = string_save,
380 .extra_opts = string_opts,
Yasuyuki KOZAKAI4ccb6f52007-07-24 06:52:16 +0000381};
382
Emmanuel Roger30719132000-10-04 15:19:31 +0000383void _init(void)
384{
Jan Engelhardt181dead2007-10-04 16:27:07 +0000385 xtables_register_match(&string_match);
Joonwoo Park78d2d142008-07-07 13:32:25 +0200386 xtables_register_match(&string_match_v1);
Emmanuel Roger30719132000-10-04 15:19:31 +0000387}