/* Shared library add-on to iptables to add string matching support. * * Copyright (C) 2000 Emmanuel Roger * * 2005-08-05 Pablo Neira Ayuso * - reimplemented to use new string matching iptables match * - add functionality to match packets by using window offsets * - add functionality to select the string matching algorithm * * ChangeLog * 29.12.2003: Michael Rash * Fixed iptables save/restore for ascii strings * that contain space chars, and hex strings that * contain embedded NULL chars. Updated to print * strings in hex mode if any non-printable char * is contained within the string. * * 27.01.2001: Gianni Tedesco * Changed --tos to --string in save(). Also * updated to work with slightly modified * ipt_string_info. */ #include #include #include #include #include #include #include #include #include /* Function which prints out usage message. */ static void string_help(void) { printf( "string match options:\n" "--from Offset to start searching from\n" "--to Offset to stop searching\n" "--algo Algorithm\n" "--string [!] string Match a string in a packet\n" "--hex-string [!] string Match a hex string in a packet\n"); } static const struct option string_opts[] = { { "from", 1, NULL, '1' }, { "to", 1, NULL, '2' }, { "algo", 1, NULL, '3' }, { "string", 1, NULL, '4' }, { "hex-string", 1, NULL, '5' }, { .name = NULL } }; static void string_init(struct xt_entry_match *m) { struct xt_string_info *i = (struct xt_string_info *) m->data; if (i->to_offset == 0) i->to_offset = (u_int16_t) ~0UL; } static void parse_string(const char *s, struct xt_string_info *info) { if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) { strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE); info->patlen = strlen(s); return; } exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s); } static void parse_algo(const char *s, struct xt_string_info *info) { if (strlen(s) <= XT_STRING_MAX_ALGO_NAME_SIZE) { strncpy(info->algo, s, XT_STRING_MAX_ALGO_NAME_SIZE); return; } exit_error(PARAMETER_PROBLEM, "ALGO too long `%s'", s); } static void parse_hex_string(const char *s, struct xt_string_info *info) { int i=0, slen, sindex=0, schar; short hex_f = 0, literal_f = 0; char hextmp[3]; slen = strlen(s); if (slen == 0) { exit_error(PARAMETER_PROBLEM, "STRING must contain at least one char"); } while (i < slen) { if (s[i] == '\\' && !hex_f) { literal_f = 1; } else if (s[i] == '\\') { exit_error(PARAMETER_PROBLEM, "Cannot include literals in hex data"); } else if (s[i] == '|') { if (hex_f) hex_f = 0; else { hex_f = 1; /* get past any initial whitespace just after the '|' */ while (s[i+1] == ' ') i++; } if (i+1 >= slen) break; else i++; /* advance to the next character */ } if (literal_f) { if (i+1 >= slen) { exit_error(PARAMETER_PROBLEM, "Bad literal placement at end of string"); } info->pattern[sindex] = s[i+1]; i += 2; /* skip over literal char */ literal_f = 0; } else if (hex_f) { if (i+1 >= slen) { exit_error(PARAMETER_PROBLEM, "Odd number of hex digits"); } if (i+2 >= slen) { /* must end with a "|" */ exit_error(PARAMETER_PROBLEM, "Invalid hex block"); } if (! isxdigit(s[i])) /* check for valid hex char */ exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i]); if (! isxdigit(s[i+1])) /* check for valid hex char */ exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i+1]); hextmp[0] = s[i]; hextmp[1] = s[i+1]; hextmp[2] = '\0'; if (! sscanf(hextmp, "%x", &schar)) exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i]); info->pattern[sindex] = (char) schar; if (s[i+2] == ' ') i += 3; /* spaces included in the hex block */ else i += 2; } else { /* the char is not part of hex data, so just copy */ info->pattern[sindex] = s[i]; i++; } if (sindex > XT_STRING_MAX_PATTERN_SIZE) exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s); sindex++; } info->patlen = sindex; } #define STRING 0x1 #define ALGO 0x2 #define FROM 0x4 #define TO 0x8 /* Function which parses command options; returns true if it ate an option */ static int string_parse(int c, char **argv, int invert, unsigned int *flags, const void *entry, struct xt_entry_match **match) { struct xt_string_info *stringinfo = (struct xt_string_info *)(*match)->data; switch (c) { case '1': if (*flags & FROM) exit_error(PARAMETER_PROBLEM, "Can't specify multiple --from"); stringinfo->from_offset = atoi(optarg); *flags |= FROM; break; case '2': if (*flags & TO) exit_error(PARAMETER_PROBLEM, "Can't specify multiple --to"); stringinfo->to_offset = atoi(optarg); *flags |= TO; break; case '3': if (*flags & ALGO) exit_error(PARAMETER_PROBLEM, "Can't specify multiple --algo"); parse_algo(optarg, stringinfo); *flags |= ALGO; break; case '4': if (*flags & STRING) exit_error(PARAMETER_PROBLEM, "Can't specify multiple --string"); check_inverse(optarg, &invert, &optind, 0); parse_string(argv[optind-1], stringinfo); if (invert) stringinfo->invert = 1; stringinfo->patlen=strlen((char *)&stringinfo->pattern); *flags |= STRING; break; case '5': if (*flags & STRING) exit_error(PARAMETER_PROBLEM, "Can't specify multiple --hex-string"); check_inverse(optarg, &invert, &optind, 0); parse_hex_string(argv[optind-1], stringinfo); /* sets length */ if (invert) stringinfo->invert = 1; *flags |= STRING; break; default: return 0; } return 1; } /* Final check; must have specified --string. */ static void string_check(unsigned int flags) { if (!(flags & STRING)) exit_error(PARAMETER_PROBLEM, "STRING match: You must specify `--string' or " "`--hex-string'"); if (!(flags & ALGO)) exit_error(PARAMETER_PROBLEM, "STRING match: You must specify `--algo'"); } /* Test to see if the string contains non-printable chars or quotes */ static unsigned short int is_hex_string(const char *str, const unsigned short int len) { unsigned int i; for (i=0; i < len; i++) if (! isprint(str[i])) return 1; /* string contains at least one non-printable char */ /* use hex output if the last char is a "\" */ if ((unsigned char) str[len-1] == 0x5c) return 1; return 0; } /* Print string with "|" chars included as one would pass to --hex-string */ static void print_hex_string(const char *str, const unsigned short int len) { unsigned int i; /* start hex block */ printf("\"|"); for (i=0; i < len; i++) { /* see if we need to prepend a zero */ if ((unsigned char) str[i] <= 0x0F) printf("0%x", (unsigned char) str[i]); else printf("%x", (unsigned char) str[i]); } /* close hex block */ printf("|\" "); } static void print_string(const char *str, const unsigned short int len) { unsigned int i; printf("\""); for (i=0; i < len; i++) { if ((unsigned char) str[i] == 0x22) /* escape any embedded quotes */ printf("%c", 0x5c); printf("%c", (unsigned char) str[i]); } printf("\" "); /* closing space and quote */ } /* Prints out the matchinfo. */ static void string_print(const void *ip, const struct xt_entry_match *match, int numeric) { const struct xt_string_info *info = (const struct xt_string_info*) match->data; if (is_hex_string(info->pattern, info->patlen)) { printf("STRING match %s", (info->invert) ? "!" : ""); print_hex_string(info->pattern, info->patlen); } else { printf("STRING match %s", (info->invert) ? "!" : ""); print_string(info->pattern, info->patlen); } printf("ALGO name %s ", info->algo); if (info->from_offset != 0) printf("FROM %u ", info->from_offset); if (info->to_offset != 0) printf("TO %u ", info->to_offset); } /* Saves the union ipt_matchinfo in parseable form to stdout. */ static void string_save(const void *ip, const struct xt_entry_match *match) { const struct xt_string_info *info = (const struct xt_string_info*) match->data; if (is_hex_string(info->pattern, info->patlen)) { printf("--hex-string %s", (info->invert) ? "! ": ""); print_hex_string(info->pattern, info->patlen); } else { printf("--string %s", (info->invert) ? "! ": ""); print_string(info->pattern, info->patlen); } printf("--algo %s ", info->algo); if (info->from_offset != 0) printf("--from %u ", info->from_offset); if (info->to_offset != 0) printf("--to %u ", info->to_offset); } static struct xtables_match string_match = { .name = "string", .family = AF_UNSPEC, .version = XTABLES_VERSION, .size = XT_ALIGN(sizeof(struct xt_string_info)), .userspacesize = offsetof(struct xt_string_info, config), .help = string_help, .init = string_init, .parse = string_parse, .final_check = string_check, .print = string_print, .save = string_save, .extra_opts = string_opts, }; void _init(void) { xtables_register_match(&string_match); }