libxt_string.c revision ea146a982e26c42f9954f140276f8deeb2edbe98
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) 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 struct xt_entry_match **match) 175{ 176 struct xt_string_info *stringinfo = (struct xt_string_info *)(*match)->data; 177 178 switch (c) { 179 case '1': 180 if (*flags & FROM) 181 exit_error(PARAMETER_PROBLEM, 182 "Can't specify multiple --from"); 183 stringinfo->from_offset = atoi(optarg); 184 *flags |= FROM; 185 break; 186 case '2': 187 if (*flags & TO) 188 exit_error(PARAMETER_PROBLEM, 189 "Can't specify multiple --to"); 190 stringinfo->to_offset = atoi(optarg); 191 *flags |= TO; 192 break; 193 case '3': 194 if (*flags & ALGO) 195 exit_error(PARAMETER_PROBLEM, 196 "Can't specify multiple --algo"); 197 parse_algo(optarg, stringinfo); 198 *flags |= ALGO; 199 break; 200 case '4': 201 if (*flags & STRING) 202 exit_error(PARAMETER_PROBLEM, 203 "Can't specify multiple --string"); 204 check_inverse(optarg, &invert, &optind, 0); 205 parse_string(argv[optind-1], stringinfo); 206 if (invert) 207 stringinfo->invert = 1; 208 stringinfo->patlen=strlen((char *)&stringinfo->pattern); 209 *flags |= STRING; 210 break; 211 212 case '5': 213 if (*flags & STRING) 214 exit_error(PARAMETER_PROBLEM, 215 "Can't specify multiple --hex-string"); 216 217 check_inverse(optarg, &invert, &optind, 0); 218 parse_hex_string(argv[optind-1], stringinfo); /* sets length */ 219 if (invert) 220 stringinfo->invert = 1; 221 *flags |= STRING; 222 break; 223 224 default: 225 return 0; 226 } 227 return 1; 228} 229 230 231/* Final check; must have specified --string. */ 232static void 233final_check(unsigned int flags) 234{ 235 if (!(flags & STRING)) 236 exit_error(PARAMETER_PROBLEM, 237 "STRING match: You must specify `--string' or " 238 "`--hex-string'"); 239 if (!(flags & ALGO)) 240 exit_error(PARAMETER_PROBLEM, 241 "STRING match: You must specify `--algo'"); 242} 243 244/* Test to see if the string contains non-printable chars or quotes */ 245static unsigned short int 246is_hex_string(const char *str, const unsigned short int len) 247{ 248 unsigned int i; 249 for (i=0; i < len; i++) 250 if (! isprint(str[i])) 251 return 1; /* string contains at least one non-printable char */ 252 /* use hex output if the last char is a "\" */ 253 if ((unsigned char) str[len-1] == 0x5c) 254 return 1; 255 return 0; 256} 257 258/* Print string with "|" chars included as one would pass to --hex-string */ 259static void 260print_hex_string(const char *str, const unsigned short int len) 261{ 262 unsigned int i; 263 /* start hex block */ 264 printf("\"|"); 265 for (i=0; i < len; i++) { 266 /* see if we need to prepend a zero */ 267 if ((unsigned char) str[i] <= 0x0F) 268 printf("0%x", (unsigned char) str[i]); 269 else 270 printf("%x", (unsigned char) str[i]); 271 } 272 /* close hex block */ 273 printf("|\" "); 274} 275 276static void 277print_string(const char *str, const unsigned short int len) 278{ 279 unsigned int i; 280 printf("\""); 281 for (i=0; i < len; i++) { 282 if ((unsigned char) str[i] == 0x22) /* escape any embedded quotes */ 283 printf("%c", 0x5c); 284 printf("%c", (unsigned char) str[i]); 285 } 286 printf("\" "); /* closing space and quote */ 287} 288 289/* Prints out the matchinfo. */ 290static void 291print(const void *ip, 292 const struct xt_entry_match *match, 293 int numeric) 294{ 295 const struct xt_string_info *info = 296 (const struct xt_string_info*) match->data; 297 298 if (is_hex_string(info->pattern, info->patlen)) { 299 printf("STRING match %s", (info->invert) ? "!" : ""); 300 print_hex_string(info->pattern, info->patlen); 301 } else { 302 printf("STRING match %s", (info->invert) ? "!" : ""); 303 print_string(info->pattern, info->patlen); 304 } 305 printf("ALGO name %s ", info->algo); 306 if (info->from_offset != 0) 307 printf("FROM %u ", info->from_offset); 308 if (info->to_offset != 0) 309 printf("TO %u ", info->to_offset); 310} 311 312 313/* Saves the union ipt_matchinfo in parseable form to stdout. */ 314static void 315save(const void *ip, const struct xt_entry_match *match) 316{ 317 const struct xt_string_info *info = 318 (const struct xt_string_info*) match->data; 319 320 if (is_hex_string(info->pattern, info->patlen)) { 321 printf("--hex-string %s", (info->invert) ? "! ": ""); 322 print_hex_string(info->pattern, info->patlen); 323 } else { 324 printf("--string %s", (info->invert) ? "! ": ""); 325 print_string(info->pattern, info->patlen); 326 } 327 printf("--algo %s ", info->algo); 328 if (info->from_offset != 0) 329 printf("--from %u ", info->from_offset); 330 if (info->to_offset != 0) 331 printf("--to %u ", info->to_offset); 332} 333 334 335static struct xtables_match string = { 336 .name = "string", 337 .family = AF_INET, 338 .version = IPTABLES_VERSION, 339 .size = XT_ALIGN(sizeof(struct xt_string_info)), 340 .userspacesize = offsetof(struct xt_string_info, config), 341 .help = help, 342 .init = init, 343 .parse = parse, 344 .final_check = final_check, 345 .print = print, 346 .save = save, 347 .extra_opts = opts 348}; 349 350 351static struct xtables_match string6 = { 352 .name = "string", 353 .family = AF_INET6, 354 .version = IPTABLES_VERSION, 355 .size = XT_ALIGN(sizeof(struct xt_string_info)), 356 .userspacesize = offsetof(struct xt_string_info, config), 357 .help = help, 358 .init = init, 359 .parse = parse, 360 .final_check = final_check, 361 .print = print, 362 .save = save, 363 .extra_opts = opts 364}; 365 366void _init(void) 367{ 368 xtables_register_match(&string); 369 xtables_register_match(&string6); 370} 371