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