blob: 508eb90b1eae53836e9b92ba7d5a50a466f48ee6 [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 *
5 * ChangeLog
Michael Rashb807fb32004-01-05 09:50:12 +00006 * 29.12.2003: Michael Rash <mbr@cipherdyne.org>
7 * Fixed iptables save/restore for ascii strings
8 * that contain space chars, and hex strings that
9 * contain embedded NULL chars. Updated to print
10 * strings in hex mode if any non-printable char
11 * is contained within the string.
12 *
András Kis-Szabó764316a2001-02-26 17:31:20 +000013 * 27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
14 * Changed --tos to --string in save(). Also
15 * updated to work with slightly modified
16 * ipt_string_info.
Emmanuel Roger30719132000-10-04 15:19:31 +000017 */
18#include <stdio.h>
19#include <netdb.h>
20#include <string.h>
21#include <stdlib.h>
22#include <getopt.h>
Michael Rashf8ac3292003-02-26 17:34:13 +000023#include <ctype.h>
Emmanuel Roger30719132000-10-04 15:19:31 +000024
25#include <iptables.h>
26#include <linux/netfilter_ipv4/ipt_string.h>
27
Stephane Ouellette451f3ea2003-04-27 12:59:00 +000028
Emmanuel Roger30719132000-10-04 15:19:31 +000029/* Function which prints out usage message. */
30static void
31help(void)
32{
33 printf(
34"STRING match v%s options:\n"
Michael Rash96d85932003-04-21 07:27:03 +000035"--string [!] string Match a string in a packet\n"
36"--hex-string [!] string Match a hex string in a packet\n",
Harald Welte80fe35d2002-05-29 13:08:15 +000037IPTABLES_VERSION);
Emmanuel Roger30719132000-10-04 15:19:31 +000038}
39
Stephane Ouellette451f3ea2003-04-27 12:59:00 +000040
Emmanuel Roger30719132000-10-04 15:19:31 +000041static struct option opts[] = {
Stephane Ouellette451f3ea2003-04-27 12:59:00 +000042 { .name = "string", .has_arg = 1, .flag = 0, .val = '1' },
43 { .name = "hex-string", .has_arg = 1, .flag = 0, .val = '2' },
44 { .name = 0 }
Emmanuel Roger30719132000-10-04 15:19:31 +000045};
46
Emmanuel Roger30719132000-10-04 15:19:31 +000047static void
48parse_string(const unsigned char *s, struct ipt_string_info *info)
49{
Michael Rash96d85932003-04-21 07:27:03 +000050 if (strlen(s) <= BM_MAX_NLEN) strcpy(info->string, s);
51 else exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
52}
53
Stephane Ouellette451f3ea2003-04-27 12:59:00 +000054
Michael Rash96d85932003-04-21 07:27:03 +000055static void
56parse_hex_string(const unsigned char *s, struct ipt_string_info *info)
57{
Michael Rashf8ac3292003-02-26 17:34:13 +000058 int i=0, slen, sindex=0, schar;
59 short hex_f = 0, literal_f = 0;
60 char hextmp[3];
61
62 slen = strlen(s);
63
64 if (slen == 0) {
65 exit_error(PARAMETER_PROBLEM,
66 "STRING must contain at least one char");
67 }
68
69 while (i < slen) {
70 if (s[i] == '\\' && !hex_f) {
71 literal_f = 1;
72 } else if (s[i] == '\\') {
73 exit_error(PARAMETER_PROBLEM,
74 "Cannot include literals in hex data");
75 } else if (s[i] == '|') {
76 if (hex_f)
77 hex_f = 0;
Michael Rashb807fb32004-01-05 09:50:12 +000078 else {
Michael Rashf8ac3292003-02-26 17:34:13 +000079 hex_f = 1;
Michael Rashb807fb32004-01-05 09:50:12 +000080 /* get past any initial whitespace just after the '|' */
81 while (s[i+1] == ' ')
82 i++;
83 }
Michael Rashf8ac3292003-02-26 17:34:13 +000084 if (i+1 >= slen)
85 break;
86 else
87 i++; /* advance to the next character */
88 }
89
90 if (literal_f) {
91 if (i+1 >= slen) {
92 exit_error(PARAMETER_PROBLEM,
93 "Bad literal placement at end of string");
94 }
95 info->string[sindex] = s[i+1];
96 i += 2; /* skip over literal char */
97 literal_f = 0;
98 } else if (hex_f) {
99 if (i+1 >= slen) {
100 exit_error(PARAMETER_PROBLEM,
101 "Odd number of hex digits");
102 }
103 if (i+2 >= slen) {
104 /* must end with a "|" */
105 exit_error(PARAMETER_PROBLEM, "Invalid hex block");
106 }
Michael Rash96d85932003-04-21 07:27:03 +0000107 if (! isxdigit(s[i])) /* check for valid hex char */
108 exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i]);
109 if (! isxdigit(s[i+1])) /* check for valid hex char */
110 exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i+1]);
Michael Rashf8ac3292003-02-26 17:34:13 +0000111 hextmp[0] = s[i];
112 hextmp[1] = s[i+1];
113 hextmp[2] = '\0';
114 if (! sscanf(hextmp, "%x", &schar))
115 exit_error(PARAMETER_PROBLEM,
116 "Invalid hex char `%c'", s[i]);
117 info->string[sindex] = (char) schar;
118 if (s[i+2] == ' ')
119 i += 3; /* spaces included in the hex block */
120 else
121 i += 2;
122 } else { /* the char is not part of hex data, so just copy */
123 info->string[sindex] = s[i];
124 i++;
125 }
126 if (sindex > BM_MAX_NLEN)
127 exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
128 sindex++;
129 }
Michael Rash96d85932003-04-21 07:27:03 +0000130 info->len = sindex;
Emmanuel Roger30719132000-10-04 15:19:31 +0000131}
132
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000133
Emmanuel Roger30719132000-10-04 15:19:31 +0000134/* Function which parses command options; returns true if it
135 ate an option */
136static int
137parse(int c, char **argv, int invert, unsigned int *flags,
138 const struct ipt_entry *entry,
139 unsigned int *nfcache,
140 struct ipt_entry_match **match)
141{
142 struct ipt_string_info *stringinfo = (struct ipt_string_info *)(*match)->data;
143
144 switch (c) {
145 case '1':
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000146 if (*flags)
147 exit_error(PARAMETER_PROBLEM,
148 "Can't specify multiple strings");
149
Harald Welteb77f1da2002-03-14 11:35:58 +0000150 check_inverse(optarg, &invert, &optind, 0);
Emmanuel Roger30719132000-10-04 15:19:31 +0000151 parse_string(argv[optind-1], stringinfo);
152 if (invert)
153 stringinfo->invert = 1;
Michael Rash96d85932003-04-21 07:27:03 +0000154 stringinfo->len=strlen((char *)&stringinfo->string);
155 *flags = 1;
156 break;
157
158 case '2':
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000159 if (*flags)
160 exit_error(PARAMETER_PROBLEM,
161 "Can't specify multiple strings");
162
Michael Rash96d85932003-04-21 07:27:03 +0000163 check_inverse(optarg, &invert, &optind, 0);
164 parse_hex_string(argv[optind-1], stringinfo); /* sets length */
165 if (invert)
166 stringinfo->invert = 1;
Emmanuel Roger30719132000-10-04 15:19:31 +0000167 *flags = 1;
168 break;
169
170 default:
171 return 0;
172 }
173 return 1;
174}
175
Emmanuel Roger30719132000-10-04 15:19:31 +0000176
177/* Final check; must have specified --string. */
178static void
179final_check(unsigned int flags)
180{
181 if (!flags)
182 exit_error(PARAMETER_PROBLEM,
Michael Rashb807fb32004-01-05 09:50:12 +0000183 "STRING match: You must specify `--string' or `--hex-string'");
Emmanuel Roger30719132000-10-04 15:19:31 +0000184}
185
Michael Rashb807fb32004-01-05 09:50:12 +0000186/* Test to see if the string contains non-printable chars or quotes */
187static unsigned short int
188is_hex_string(const char *str, const unsigned short int len)
189{
190 unsigned int i;
191 for (i=0; i < len; i++)
192 if (! isprint(str[i]))
193 return 1; /* string contains at least one non-printable char */
194 /* use hex output if the last char is a "\" */
195 if ((unsigned char) str[len-1] == 0x5c)
196 return 1;
197 return 0;
198}
199
200/* Print string with "|" chars included as one would pass to --hex-string */
201static void
202print_hex_string(const char *str, const unsigned short int len)
203{
204 unsigned int i;
205 /* start hex block */
206 printf("\"|");
207 for (i=0; i < len; i++) {
208 /* see if we need to prepend a zero */
209 if ((unsigned char) str[i] <= 0x0F)
210 printf("0%x", (unsigned char) str[i]);
211 else
212 printf("%x", (unsigned char) str[i]);
213 }
214 /* close hex block */
215 printf("|\" ");
216}
217
218static void
219print_string(const char *str, const unsigned short int len)
220{
221 unsigned int i;
222 printf("\"");
223 for (i=0; i < len; i++) {
224 if ((unsigned char) str[i] == 0x22) /* escape any embedded quotes */
225 printf("%c", 0x5c);
226 printf("%c", (unsigned char) str[i]);
227 }
228 printf("\" "); /* closing space and quote */
229}
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000230
Emmanuel Roger30719132000-10-04 15:19:31 +0000231/* Prints out the matchinfo. */
232static void
233print(const struct ipt_ip *ip,
234 const struct ipt_entry_match *match,
235 int numeric)
236{
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000237 const struct ipt_string_info *info =
238 (const struct ipt_string_info*) match->data;
239
Michael Rashb807fb32004-01-05 09:50:12 +0000240 if (is_hex_string(info->string, info->len)) {
241 printf("STRING match %s", (info->invert) ? "!" : "");
242 print_hex_string(info->string, info->len);
243 } else {
244 printf("STRING match %s", (info->invert) ? "!" : "");
245 print_string(info->string, info->len);
246 }
Emmanuel Roger30719132000-10-04 15:19:31 +0000247}
248
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000249
250/* Saves the union ipt_matchinfo in parseable form to stdout. */
Emmanuel Roger30719132000-10-04 15:19:31 +0000251static void
252save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
253{
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000254 const struct ipt_string_info *info =
255 (const struct ipt_string_info*) match->data;
256
Michael Rashb807fb32004-01-05 09:50:12 +0000257 if (is_hex_string(info->string, info->len)) {
258 printf("--hex-string %s", (info->invert) ? "! ": "");
259 print_hex_string(info->string, info->len);
260 } else {
261 printf("--string %s", (info->invert) ? "! ": "");
262 print_string(info->string, info->len);
263 }
Emmanuel Roger30719132000-10-04 15:19:31 +0000264}
265
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000266
267static struct iptables_match string = {
268 .name = "string",
269 .version = IPTABLES_VERSION,
270 .size = IPT_ALIGN(sizeof(struct ipt_string_info)),
271 .userspacesize = IPT_ALIGN(sizeof(struct ipt_string_info)),
272 .help = &help,
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000273 .parse = &parse,
274 .final_check = &final_check,
275 .print = &print,
276 .save = &save,
277 .extra_opts = opts
Emmanuel Roger30719132000-10-04 15:19:31 +0000278};
279
Stephane Ouellette451f3ea2003-04-27 12:59:00 +0000280
Emmanuel Roger30719132000-10-04 15:19:31 +0000281void _init(void)
282{
283 register_match(&string);
284}