libxt_string.c revision 23545c2a7a31c68c1e49c7c901b632c2f1c59968
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 string_help(void) 35{ 36 printf( 37"STRING match v%s 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", 43IPTABLES_VERSION); 44} 45 46static const struct option string_opts[] = { 47 { "from", 1, NULL, '1' }, 48 { "to", 1, NULL, '2' }, 49 { "algo", 1, NULL, '3' }, 50 { "string", 1, NULL, '4' }, 51 { "hex-string", 1, NULL, '5' }, 52 { .name = NULL } 53}; 54 55static void string_init(struct xt_entry_match *m) 56{ 57 struct xt_string_info *i = (struct xt_string_info *) m->data; 58 59 if (i->to_offset == 0) 60 i->to_offset = (u_int16_t) ~0UL; 61} 62 63static void 64parse_string(const char *s, struct xt_string_info *info) 65{ 66 if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) { 67 strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE); 68 info->patlen = strlen(s); 69 return; 70 } 71 exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s); 72} 73 74static void 75parse_algo(const char *s, struct xt_string_info *info) 76{ 77 if (strlen(s) <= XT_STRING_MAX_ALGO_NAME_SIZE) { 78 strncpy(info->algo, s, XT_STRING_MAX_ALGO_NAME_SIZE); 79 return; 80 } 81 exit_error(PARAMETER_PROBLEM, "ALGO too long `%s'", s); 82} 83 84static void 85parse_hex_string(const char *s, struct xt_string_info *info) 86{ 87 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; 107 else { 108 hex_f = 1; 109 /* get past any initial whitespace just after the '|' */ 110 while (s[i+1] == ' ') 111 i++; 112 } 113 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 } 124 info->pattern[sindex] = s[i+1]; 125 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 } 136 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]); 140 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]); 146 info->pattern[sindex] = (char) schar; 147 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 */ 152 info->pattern[sindex] = s[i]; 153 i++; 154 } 155 if (sindex > XT_STRING_MAX_PATTERN_SIZE) 156 exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s); 157 sindex++; 158 } 159 info->patlen = sindex; 160} 161 162#define STRING 0x1 163#define ALGO 0x2 164#define FROM 0x4 165#define TO 0x8 166 167/* Function which parses command options; returns true if it 168 ate an option */ 169static int 170string_parse(int c, char **argv, int invert, unsigned int *flags, 171 const void *entry, struct xt_entry_match **match) 172{ 173 struct xt_string_info *stringinfo = (struct xt_string_info *)(*match)->data; 174 175 switch (c) { 176 case '1': 177 if (*flags & FROM) 178 exit_error(PARAMETER_PROBLEM, 179 "Can't specify multiple --from"); 180 stringinfo->from_offset = atoi(optarg); 181 *flags |= FROM; 182 break; 183 case '2': 184 if (*flags & TO) 185 exit_error(PARAMETER_PROBLEM, 186 "Can't specify multiple --to"); 187 stringinfo->to_offset = atoi(optarg); 188 *flags |= TO; 189 break; 190 case '3': 191 if (*flags & ALGO) 192 exit_error(PARAMETER_PROBLEM, 193 "Can't specify multiple --algo"); 194 parse_algo(optarg, stringinfo); 195 *flags |= ALGO; 196 break; 197 case '4': 198 if (*flags & STRING) 199 exit_error(PARAMETER_PROBLEM, 200 "Can't specify multiple --string"); 201 check_inverse(optarg, &invert, &optind, 0); 202 parse_string(argv[optind-1], stringinfo); 203 if (invert) 204 stringinfo->invert = 1; 205 stringinfo->patlen=strlen((char *)&stringinfo->pattern); 206 *flags |= STRING; 207 break; 208 209 case '5': 210 if (*flags & STRING) 211 exit_error(PARAMETER_PROBLEM, 212 "Can't specify multiple --hex-string"); 213 214 check_inverse(optarg, &invert, &optind, 0); 215 parse_hex_string(argv[optind-1], stringinfo); /* sets length */ 216 if (invert) 217 stringinfo->invert = 1; 218 *flags |= STRING; 219 break; 220 221 default: 222 return 0; 223 } 224 return 1; 225} 226 227 228/* Final check; must have specified --string. */ 229static void string_check(unsigned int flags) 230{ 231 if (!(flags & STRING)) 232 exit_error(PARAMETER_PROBLEM, 233 "STRING match: You must specify `--string' or " 234 "`--hex-string'"); 235 if (!(flags & ALGO)) 236 exit_error(PARAMETER_PROBLEM, 237 "STRING match: You must specify `--algo'"); 238} 239 240/* Test to see if the string contains non-printable chars or quotes */ 241static unsigned short int 242is_hex_string(const char *str, const unsigned short int len) 243{ 244 unsigned int i; 245 for (i=0; i < len; i++) 246 if (! isprint(str[i])) 247 return 1; /* string contains at least one non-printable char */ 248 /* use hex output if the last char is a "\" */ 249 if ((unsigned char) str[len-1] == 0x5c) 250 return 1; 251 return 0; 252} 253 254/* Print string with "|" chars included as one would pass to --hex-string */ 255static void 256print_hex_string(const char *str, const unsigned short int len) 257{ 258 unsigned int i; 259 /* start hex block */ 260 printf("\"|"); 261 for (i=0; i < len; i++) { 262 /* see if we need to prepend a zero */ 263 if ((unsigned char) str[i] <= 0x0F) 264 printf("0%x", (unsigned char) str[i]); 265 else 266 printf("%x", (unsigned char) str[i]); 267 } 268 /* close hex block */ 269 printf("|\" "); 270} 271 272static void 273print_string(const char *str, const unsigned short int len) 274{ 275 unsigned int i; 276 printf("\""); 277 for (i=0; i < len; i++) { 278 if ((unsigned char) str[i] == 0x22) /* escape any embedded quotes */ 279 printf("%c", 0x5c); 280 printf("%c", (unsigned char) str[i]); 281 } 282 printf("\" "); /* closing space and quote */ 283} 284 285/* Prints out the matchinfo. */ 286static void 287string_print(const void *ip, const struct xt_entry_match *match, int numeric) 288{ 289 const struct xt_string_info *info = 290 (const struct xt_string_info*) match->data; 291 292 if (is_hex_string(info->pattern, info->patlen)) { 293 printf("STRING match %s", (info->invert) ? "!" : ""); 294 print_hex_string(info->pattern, info->patlen); 295 } else { 296 printf("STRING match %s", (info->invert) ? "!" : ""); 297 print_string(info->pattern, info->patlen); 298 } 299 printf("ALGO name %s ", info->algo); 300 if (info->from_offset != 0) 301 printf("FROM %u ", info->from_offset); 302 if (info->to_offset != 0) 303 printf("TO %u ", info->to_offset); 304} 305 306 307/* Saves the union ipt_matchinfo in parseable form to stdout. */ 308static void string_save(const void *ip, const struct xt_entry_match *match) 309{ 310 const struct xt_string_info *info = 311 (const struct xt_string_info*) match->data; 312 313 if (is_hex_string(info->pattern, info->patlen)) { 314 printf("--hex-string %s", (info->invert) ? "! ": ""); 315 print_hex_string(info->pattern, info->patlen); 316 } else { 317 printf("--string %s", (info->invert) ? "! ": ""); 318 print_string(info->pattern, info->patlen); 319 } 320 printf("--algo %s ", info->algo); 321 if (info->from_offset != 0) 322 printf("--from %u ", info->from_offset); 323 if (info->to_offset != 0) 324 printf("--to %u ", info->to_offset); 325} 326 327 328static struct xtables_match string_match = { 329 .name = "string", 330 .family = AF_UNSPEC, 331 .version = IPTABLES_VERSION, 332 .size = XT_ALIGN(sizeof(struct xt_string_info)), 333 .userspacesize = offsetof(struct xt_string_info, config), 334 .help = string_help, 335 .init = string_init, 336 .parse = string_parse, 337 .final_check = string_check, 338 .print = string_print, 339 .save = string_save, 340 .extra_opts = string_opts, 341}; 342 343void _init(void) 344{ 345 xtables_register_match(&string_match); 346} 347