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