changing trunk/trunk to trunk
[iptables.git] / extensions / libxt_string.c
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. */
34 static 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
45 static 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
54 static 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
62 static void
63 parse_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
73 static void
74 parse_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
83 static void
84 parse_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 */
168 static int
169 string_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. */
228 static 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 */
240 static unsigned short int
241 is_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 */
254 static void
255 print_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
271 static void
272 print_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. */
285 static void
286 string_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. */
307 static 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
327 static 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
342 void _init(void)
343 {
344         xtables_register_match(&string_match);
345 }