1 /* Shared library add-on to iptables to add string matching support. 2 * 3 * Copyright (C) 2000 Emmanuel Roger <winfield (at) freegates.be> 4 * 5 * 2005-08-05 Pablo Neira Ayuso <pablo (at) 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 (at) 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 (at) 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 <string.h> 25 #include <stdlib.h> 26 #include <ctype.h> 27 #include <xtables.h> 28 #include <linux/netfilter/xt_string.h> 29 30 enum { 31 O_FROM = 0, 32 O_TO, 33 O_ALGO, 34 O_ICASE, 35 O_STRING, 36 O_HEX_STRING, 37 F_STRING = 1 << O_STRING, 38 F_HEX_STRING = 1 << O_HEX_STRING, 39 F_OP_ANY = F_STRING | F_HEX_STRING, 40 }; 41 42 static void string_help(void) 43 { 44 printf( 45 "string match options:\n" 46 "--from Offset to start searching from\n" 47 "--to Offset to stop searching\n" 48 "--algo Algorithm\n" 49 "--icase Ignore case (default: 0)\n" 50 "[!] --string string Match a string in a packet\n" 51 "[!] --hex-string string Match a hex string in a packet\n"); 52 } 53 54 #define s struct xt_string_info 55 static const struct xt_option_entry string_opts[] = { 56 {.name = "from", .id = O_FROM, .type = XTTYPE_UINT16, 57 .flags = XTOPT_PUT, XTOPT_POINTER(s, from_offset)}, 58 {.name = "to", .id = O_TO, .type = XTTYPE_UINT16, 59 .flags = XTOPT_PUT, XTOPT_POINTER(s, to_offset)}, 60 {.name = "algo", .id = O_ALGO, .type = XTTYPE_STRING, 61 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, algo)}, 62 {.name = "string", .id = O_STRING, .type = XTTYPE_STRING, 63 .flags = XTOPT_INVERT, .excl = F_HEX_STRING}, 64 {.name = "hex-string", .id = O_HEX_STRING, .type = XTTYPE_STRING, 65 .flags = XTOPT_INVERT, .excl = F_STRING}, 66 {.name = "icase", .id = O_ICASE, .type = XTTYPE_NONE}, 67 XTOPT_TABLEEND, 68 }; 69 #undef s 70 71 static void string_init(struct xt_entry_match *m) 72 { 73 struct xt_string_info *i = (struct xt_string_info *) m->data; 74 75 i->to_offset = UINT16_MAX; 76 } 77 78 static void 79 parse_string(const char *s, struct xt_string_info *info) 80 { 81 /* xt_string does not need \0 at the end of the pattern */ 82 if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) { 83 strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE); 84 info->patlen = strnlen(s, XT_STRING_MAX_PATTERN_SIZE); 85 return; 86 } 87 xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s); 88 } 89 90 static void 91 parse_hex_string(const char *s, struct xt_string_info *info) 92 { 93 int i=0, slen, sindex=0, schar; 94 short hex_f = 0, literal_f = 0; 95 char hextmp[3]; 96 97 slen = strlen(s); 98 99 if (slen == 0) { 100 xtables_error(PARAMETER_PROBLEM, 101 "STRING must contain at least one char"); 102 } 103 104 while (i < slen) { 105 if (s[i] == '\\' && !hex_f) { 106 literal_f = 1; 107 } else if (s[i] == '\\') { 108 xtables_error(PARAMETER_PROBLEM, 109 "Cannot include literals in hex data"); 110 } else if (s[i] == '|') { 111 if (hex_f) 112 hex_f = 0; 113 else { 114 hex_f = 1; 115 /* get past any initial whitespace just after the '|' */ 116 while (s[i+1] == ' ') 117 i++; 118 } 119 if (i+1 >= slen) 120 break; 121 else 122 i++; /* advance to the next character */ 123 } 124 125 if (literal_f) { 126 if (i+1 >= slen) { 127 xtables_error(PARAMETER_PROBLEM, 128 "Bad literal placement at end of string"); 129 } 130 info->pattern[sindex] = s[i+1]; 131 i += 2; /* skip over literal char */ 132 literal_f = 0; 133 } else if (hex_f) { 134 if (i+1 >= slen) { 135 xtables_error(PARAMETER_PROBLEM, 136 "Odd number of hex digits"); 137 } 138 if (i+2 >= slen) { 139 /* must end with a "|" */ 140 xtables_error(PARAMETER_PROBLEM, "Invalid hex block"); 141 } 142 if (! isxdigit(s[i])) /* check for valid hex char */ 143 xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i]); 144 if (! isxdigit(s[i+1])) /* check for valid hex char */ 145 xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i+1]); 146 hextmp[0] = s[i]; 147 hextmp[1] = s[i+1]; 148 hextmp[2] = '\0'; 149 if (! sscanf(hextmp, "%x", &schar)) 150 xtables_error(PARAMETER_PROBLEM, 151 "Invalid hex char `%c'", s[i]); 152 info->pattern[sindex] = (char) schar; 153 if (s[i+2] == ' ') 154 i += 3; /* spaces included in the hex block */ 155 else 156 i += 2; 157 } else { /* the char is not part of hex data, so just copy */ 158 info->pattern[sindex] = s[i]; 159 i++; 160 } 161 if (sindex > XT_STRING_MAX_PATTERN_SIZE) 162 xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s); 163 sindex++; 164 } 165 info->patlen = sindex; 166 } 167 168 static void string_parse(struct xt_option_call *cb) 169 { 170 struct xt_string_info *stringinfo = cb->data; 171 const unsigned int revision = (*cb->match)->u.user.revision; 172 173 xtables_option_parse(cb); 174 switch (cb->entry->id) { 175 case O_STRING: 176 parse_string(cb->arg, stringinfo); 177 if (cb->invert) { 178 if (revision == 0) 179 stringinfo->u.v0.invert = 1; 180 else 181 stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT; 182 } 183 break; 184 case O_HEX_STRING: 185 parse_hex_string(cb->arg, stringinfo); /* sets length */ 186 if (cb->invert) { 187 if (revision == 0) 188 stringinfo->u.v0.invert = 1; 189 else 190 stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT; 191 } 192 break; 193 case O_ICASE: 194 if (revision == 0) 195 xtables_error(VERSION_PROBLEM, 196 "Kernel doesn't support --icase"); 197 198 stringinfo->u.v1.flags |= XT_STRING_FLAG_IGNORECASE; 199 break; 200 } 201 } 202 203 static void string_check(struct xt_fcheck_call *cb) 204 { 205 if (!(cb->xflags & F_OP_ANY)) 206 xtables_error(PARAMETER_PROBLEM, 207 "STRING match: You must specify `--string' or " 208 "`--hex-string'"); 209 } 210 211 /* Test to see if the string contains non-printable chars or quotes */ 212 static unsigned short int 213 is_hex_string(const char *str, const unsigned short int len) 214 { 215 unsigned int i; 216 for (i=0; i < len; i++) 217 if (! isprint(str[i])) 218 return 1; /* string contains at least one non-printable char */ 219 /* use hex output if the last char is a "\" */ 220 if ((unsigned char) str[len-1] == 0x5c) 221 return 1; 222 return 0; 223 } 224 225 /* Print string with "|" chars included as one would pass to --hex-string */ 226 static void 227 print_hex_string(const char *str, const unsigned short int len) 228 { 229 unsigned int i; 230 /* start hex block */ 231 printf("\"|"); 232 for (i=0; i < len; i++) { 233 /* see if we need to prepend a zero */ 234 if ((unsigned char) str[i] <= 0x0F) 235 printf("0%x", (unsigned char) str[i]); 236 else 237 printf("%x", (unsigned char) str[i]); 238 } 239 /* close hex block */ 240 printf("|\" "); 241 } 242 243 static void 244 print_string(const char *str, const unsigned short int len) 245 { 246 unsigned int i; 247 printf(" \""); 248 for (i=0; i < len; i++) { 249 if ((unsigned char) str[i] == 0x22) /* escape any embedded quotes */ 250 printf("%c", 0x5c); 251 printf("%c", (unsigned char) str[i]); 252 } 253 printf("\""); /* closing quote */ 254 } 255 256 static void 257 string_print(const void *ip, const struct xt_entry_match *match, int numeric) 258 { 259 const struct xt_string_info *info = 260 (const struct xt_string_info*) match->data; 261 const int revision = match->u.user.revision; 262 int invert = (revision == 0 ? info->u.v0.invert : 263 info->u.v1.flags & XT_STRING_FLAG_INVERT); 264 265 if (is_hex_string(info->pattern, info->patlen)) { 266 printf(" STRING match %s", invert ? "!" : ""); 267 print_hex_string(info->pattern, info->patlen); 268 } else { 269 printf(" STRING match %s", invert ? "!" : ""); 270 print_string(info->pattern, info->patlen); 271 } 272 printf(" ALGO name %s", info->algo); 273 if (info->from_offset != 0) 274 printf(" FROM %u", info->from_offset); 275 if (info->to_offset != 0) 276 printf(" TO %u", info->to_offset); 277 if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE) 278 printf(" ICASE"); 279 } 280 281 static void string_save(const void *ip, const struct xt_entry_match *match) 282 { 283 const struct xt_string_info *info = 284 (const struct xt_string_info*) match->data; 285 const int revision = match->u.user.revision; 286 int invert = (revision == 0 ? info->u.v0.invert : 287 info->u.v1.flags & XT_STRING_FLAG_INVERT); 288 289 if (is_hex_string(info->pattern, info->patlen)) { 290 printf("%s --hex-string", (invert) ? " !" : ""); 291 print_hex_string(info->pattern, info->patlen); 292 } else { 293 printf("%s --string", (invert) ? " !": ""); 294 print_string(info->pattern, info->patlen); 295 } 296 printf(" --algo %s", info->algo); 297 if (info->from_offset != 0) 298 printf(" --from %u", info->from_offset); 299 if (info->to_offset != 0) 300 printf(" --to %u", info->to_offset); 301 if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE) 302 printf(" --icase"); 303 } 304 305 306 static struct xtables_match string_mt_reg[] = { 307 { 308 .name = "string", 309 .revision = 0, 310 .family = NFPROTO_UNSPEC, 311 .version = XTABLES_VERSION, 312 .size = XT_ALIGN(sizeof(struct xt_string_info)), 313 .userspacesize = offsetof(struct xt_string_info, config), 314 .help = string_help, 315 .init = string_init, 316 .print = string_print, 317 .save = string_save, 318 .x6_parse = string_parse, 319 .x6_fcheck = string_check, 320 .x6_options = string_opts, 321 }, 322 { 323 .name = "string", 324 .revision = 1, 325 .family = NFPROTO_UNSPEC, 326 .version = XTABLES_VERSION, 327 .size = XT_ALIGN(sizeof(struct xt_string_info)), 328 .userspacesize = offsetof(struct xt_string_info, config), 329 .help = string_help, 330 .init = string_init, 331 .print = string_print, 332 .save = string_save, 333 .x6_parse = string_parse, 334 .x6_fcheck = string_check, 335 .x6_options = string_opts, 336 }, 337 }; 338 339 void _init(void) 340 { 341 xtables_register_matches(string_mt_reg, ARRAY_SIZE(string_mt_reg)); 342 } 343