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