libxt_string.c revision d09b6d591ca7d7d7575cb6aa20384c9830f777ab
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#define _GNU_SOURCE 1 24#include <stdbool.h> 25#include <stdio.h> 26#include <netdb.h> 27#include <string.h> 28#include <stdlib.h> 29#include <getopt.h> 30#include <ctype.h> 31#include <xtables.h> 32#include <stddef.h> 33#include <linux/netfilter/xt_string.h> 34 35static void string_help(void) 36{ 37 printf( 38"string match options:\n" 39"--from Offset to start searching from\n" 40"--to Offset to stop searching\n" 41"--algo Algorithm\n" 42"--icase Ignore case (default: 0)\n" 43"[!] --string string Match a string in a packet\n" 44"[!] --hex-string string Match a hex string in a packet\n"); 45} 46 47static const struct option string_opts[] = { 48 {.name = "from", .has_arg = true, .val = '1'}, 49 {.name = "to", .has_arg = true, .val = '2'}, 50 {.name = "algo", .has_arg = true, .val = '3'}, 51 {.name = "string", .has_arg = true, .val = '4'}, 52 {.name = "hex-string", .has_arg = true, .val = '5'}, 53 {.name = "icase", .has_arg = false, .val = '6'}, 54 XT_GETOPT_TABLEEND, 55}; 56 57static void string_init(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 = UINT16_MAX; 63} 64 65static void 66parse_string(const char *s, struct xt_string_info *info) 67{ 68 /* xt_string does not need \0 at the end of the pattern */ 69 if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) { 70 strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE); 71 info->patlen = strnlen(s, XT_STRING_MAX_PATTERN_SIZE); 72 return; 73 } 74 xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s); 75} 76 77static void 78parse_algo(const char *s, struct xt_string_info *info) 79{ 80 /* xt_string needs \0 for algo name */ 81 if (strlen(s) < XT_STRING_MAX_ALGO_NAME_SIZE) { 82 strncpy(info->algo, s, XT_STRING_MAX_ALGO_NAME_SIZE); 83 return; 84 } 85 xtables_error(PARAMETER_PROBLEM, "ALGO too long \"%s\"", s); 86} 87 88static void 89parse_hex_string(const char *s, struct xt_string_info *info) 90{ 91 int i=0, slen, sindex=0, schar; 92 short hex_f = 0, literal_f = 0; 93 char hextmp[3]; 94 95 slen = strlen(s); 96 97 if (slen == 0) { 98 xtables_error(PARAMETER_PROBLEM, 99 "STRING must contain at least one char"); 100 } 101 102 while (i < slen) { 103 if (s[i] == '\\' && !hex_f) { 104 literal_f = 1; 105 } else if (s[i] == '\\') { 106 xtables_error(PARAMETER_PROBLEM, 107 "Cannot include literals in hex data"); 108 } else if (s[i] == '|') { 109 if (hex_f) 110 hex_f = 0; 111 else { 112 hex_f = 1; 113 /* get past any initial whitespace just after the '|' */ 114 while (s[i+1] == ' ') 115 i++; 116 } 117 if (i+1 >= slen) 118 break; 119 else 120 i++; /* advance to the next character */ 121 } 122 123 if (literal_f) { 124 if (i+1 >= slen) { 125 xtables_error(PARAMETER_PROBLEM, 126 "Bad literal placement at end of string"); 127 } 128 info->pattern[sindex] = s[i+1]; 129 i += 2; /* skip over literal char */ 130 literal_f = 0; 131 } else if (hex_f) { 132 if (i+1 >= slen) { 133 xtables_error(PARAMETER_PROBLEM, 134 "Odd number of hex digits"); 135 } 136 if (i+2 >= slen) { 137 /* must end with a "|" */ 138 xtables_error(PARAMETER_PROBLEM, "Invalid hex block"); 139 } 140 if (! isxdigit(s[i])) /* check for valid hex char */ 141 xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i]); 142 if (! isxdigit(s[i+1])) /* check for valid hex char */ 143 xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i+1]); 144 hextmp[0] = s[i]; 145 hextmp[1] = s[i+1]; 146 hextmp[2] = '\0'; 147 if (! sscanf(hextmp, "%x", &schar)) 148 xtables_error(PARAMETER_PROBLEM, 149 "Invalid hex char `%c'", s[i]); 150 info->pattern[sindex] = (char) schar; 151 if (s[i+2] == ' ') 152 i += 3; /* spaces included in the hex block */ 153 else 154 i += 2; 155 } else { /* the char is not part of hex data, so just copy */ 156 info->pattern[sindex] = s[i]; 157 i++; 158 } 159 if (sindex > XT_STRING_MAX_PATTERN_SIZE) 160 xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s); 161 sindex++; 162 } 163 info->patlen = sindex; 164} 165 166#define STRING 0x1 167#define ALGO 0x2 168#define FROM 0x4 169#define TO 0x8 170#define ICASE 0x10 171 172static int 173string_parse(int c, char **argv, int invert, unsigned int *flags, 174 const void *entry, struct xt_entry_match **match) 175{ 176 struct xt_string_info *stringinfo = 177 (struct xt_string_info *)(*match)->data; 178 const int revision = (*match)->u.user.revision; 179 180 switch (c) { 181 case '1': 182 if (*flags & FROM) 183 xtables_error(PARAMETER_PROBLEM, 184 "Can't specify multiple --from"); 185 stringinfo->from_offset = atoi(optarg); 186 *flags |= FROM; 187 break; 188 case '2': 189 if (*flags & TO) 190 xtables_error(PARAMETER_PROBLEM, 191 "Can't specify multiple --to"); 192 stringinfo->to_offset = atoi(optarg); 193 *flags |= TO; 194 break; 195 case '3': 196 if (*flags & ALGO) 197 xtables_error(PARAMETER_PROBLEM, 198 "Can't specify multiple --algo"); 199 parse_algo(optarg, stringinfo); 200 *flags |= ALGO; 201 break; 202 case '4': 203 if (*flags & STRING) 204 xtables_error(PARAMETER_PROBLEM, 205 "Can't specify multiple --string"); 206 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 207 parse_string(optarg, stringinfo); 208 if (invert) { 209 if (revision == 0) 210 stringinfo->u.v0.invert = 1; 211 else 212 stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT; 213 } 214 *flags |= STRING; 215 break; 216 217 case '5': 218 if (*flags & STRING) 219 xtables_error(PARAMETER_PROBLEM, 220 "Can't specify multiple --hex-string"); 221 222 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 223 parse_hex_string(optarg, stringinfo); /* sets length */ 224 if (invert) { 225 if (revision == 0) 226 stringinfo->u.v0.invert = 1; 227 else 228 stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT; 229 } 230 *flags |= STRING; 231 break; 232 233 case '6': 234 if (revision == 0) 235 xtables_error(VERSION_PROBLEM, 236 "Kernel doesn't support --icase"); 237 238 stringinfo->u.v1.flags |= XT_STRING_FLAG_IGNORECASE; 239 *flags |= ICASE; 240 break; 241 } 242 return 1; 243} 244 245static void string_check(unsigned int flags) 246{ 247 if (!(flags & STRING)) 248 xtables_error(PARAMETER_PROBLEM, 249 "STRING match: You must specify `--string' or " 250 "`--hex-string'"); 251 if (!(flags & ALGO)) 252 xtables_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("%s--hex-string ", (invert) ? "! ": ""); 336 print_hex_string(info->pattern, info->patlen); 337 } else { 338 printf("%s--string ", (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_mt_reg[] = { 352 { 353 .name = "string", 354 .revision = 0, 355 .family = NFPROTO_UNSPEC, 356 .version = XTABLES_VERSION, 357 .size = XT_ALIGN(sizeof(struct xt_string_info)), 358 .userspacesize = offsetof(struct xt_string_info, config), 359 .help = string_help, 360 .init = string_init, 361 .parse = string_parse, 362 .final_check = string_check, 363 .print = string_print, 364 .save = string_save, 365 .extra_opts = string_opts, 366 }, 367 { 368 .name = "string", 369 .revision = 1, 370 .family = NFPROTO_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}; 383 384void _init(void) 385{ 386 xtables_register_matches(string_mt_reg, ARRAY_SIZE(string_mt_reg)); 387} 388