/* ebt_string * * Author: * Bernie Harris * * February, 2018 * * Based on: * libxt_string.c, Copyright (C) 2000 Emmanuel Roger */ #include #include #include #include #include #include #include #include "../include/ebtables_u.h" #include #include #define STRING_FROM '1' #define STRING_TO '2' #define STRING_ALGO '3' #define STRING_ICASE '4' #define STRING '5' #define STRING_HEX '6' #define OPT_STRING_FROM (1 << 0) #define OPT_STRING_TO (1 << 1) #define OPT_STRING_ALGO (1 << 2) #define OPT_STRING_ICASE (1 << 3) #define OPT_STRING (1 << 4) #define OPT_STRING_HEX (1 << 5) static const struct option opts[] = { { "string-from" , required_argument, 0, STRING_FROM }, { "string-to" , required_argument, 0, STRING_TO }, { "string-algo" , required_argument, 0, STRING_ALGO }, { "string-icase" , no_argument, 0, STRING_ICASE }, { "string" , required_argument, 0, STRING }, { "string-hex" , required_argument, 0, STRING_HEX }, { 0 } }; static void print_help() { printf( "string options:\n" "--string-from offset : Offset to start searching from (default: 0)\n" "--string-to offset : Offset to stop searching (default: packet size)\n" "--string-algo algorithm : Algorithm (bm = Boyer-Moore, kmp = Knuth-Pratt-Morris)\n" "--string-icase : Ignore case when searching\n" "--string [!] string : Match a string in a packet\n" "--string-hex [!] string : Match a hex string in a packet, e.g. |0D 0A|, |0D0A|, netfilter|03|org\n"); } static void init(struct ebt_entry_match *match) { struct xt_string_info *info = (struct xt_string_info *)match->data; info->to_offset = UINT16_MAX; } static void parse_string(const char *s, struct xt_string_info *info) { /* xt_string does not need \0 at the end of the pattern */ if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) { strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE); info->patlen = strnlen(s, XT_STRING_MAX_PATTERN_SIZE); return; } ebt_print_error3("STRING 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) { ebt_print_error3("STRING must contain at least one char"); } while (i < slen) { if (s[i] == '\\' && !hex_f) { literal_f = 1; } else if (s[i] == '\\') { ebt_print_error3("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) { ebt_print_error3("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) { ebt_print_error3("Odd number of hex digits"); } if (i+2 >= slen) { /* must end with a "|" */ ebt_print_error3("Invalid hex block"); } if (! isxdigit(s[i])) /* check for valid hex char */ ebt_print_error3("Invalid hex char '%c'", s[i]); if (! isxdigit(s[i+1])) /* check for valid hex char */ ebt_print_error3("Invalid hex char '%c'", s[i+1]); hextmp[0] = s[i]; hextmp[1] = s[i+1]; hextmp[2] = '\0'; if (! sscanf(hextmp, "%x", &schar)) ebt_print_error3("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) ebt_print_error3("STRING too long \"%s\"", s); sindex++; } info->patlen = sindex; } static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, unsigned int *flags, struct ebt_entry_match **match) { struct xt_string_info *info = (struct xt_string_info *)(*match)->data; switch (c) { case STRING_FROM: ebt_check_option2(flags, OPT_STRING_FROM); if (ebt_check_inverse2(optarg)) ebt_print_error2("Unexpected `!' after --string-from"); info->from_offset = (__u16)strtoul(optarg, NULL, 10); break; case STRING_TO: ebt_check_option2(flags, OPT_STRING_TO); if (ebt_check_inverse2(optarg)) ebt_print_error2("Unexpected `!' after --string-to"); info->to_offset = (__u16)strtoul(optarg, NULL, 10); break; case STRING_ALGO: ebt_check_option2(flags, OPT_STRING_ALGO); if (ebt_check_inverse2(optarg)) ebt_print_error2("Unexpected `!' after --string-algo"); if (snprintf(info->algo, sizeof(info->algo), "%s", optarg) >= sizeof(info->algo)) ebt_print_error2("\"%s\" is truncated", info->algo); break; case STRING_ICASE: ebt_check_option2(flags, OPT_STRING_ICASE); if (ebt_check_inverse2(optarg)) ebt_print_error2("Unexpected `!' after --string-icase"); info->u.v1.flags |= XT_STRING_FLAG_IGNORECASE; break; case STRING: ebt_check_option2(flags, OPT_STRING); parse_string(optarg, info); if (ebt_check_inverse2(optarg)) { info->u.v1.flags |= XT_STRING_FLAG_INVERT; } break; case STRING_HEX: ebt_check_option2(flags, OPT_STRING_HEX); parse_hex_string(optarg, info); if (ebt_check_inverse2(optarg)) { info->u.v1.flags |= XT_STRING_FLAG_INVERT; } break; default: return 0; } return 1; } static void final_check(const struct ebt_u_entry *entry, const struct ebt_entry_match *match, const char *name, unsigned int hookmask, unsigned int time) { struct xt_string_info *info = (struct xt_string_info *)match->data; if (info->to_offset < info->from_offset) { ebt_print_error3("'to' offset should not be less than 'from' " "offset"); } } /* 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])) { /* string contains at least one non-printable char */ return 1; } } /* use hex output if the last char is a "\" */ if (str[len-1] == '\\') return 1; return 0; } /* Print string with "|" chars included as one would pass to --string-hex */ 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++) printf("%02x", (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 (str[i] == '\"' || str[i] == '\\') putchar('\\'); printf("%c", (unsigned char) str[i]); } printf("\" "); /* closing quote */ } static void print(const struct ebt_u_entry *entry, const struct ebt_entry_match *match) { const struct xt_string_info *info = (const struct xt_string_info *) match->data; int invert = info->u.v1.flags & XT_STRING_FLAG_INVERT; if (is_hex_string(info->pattern, info->patlen)) { printf("--string-hex %s", invert ? "! " : ""); print_hex_string(info->pattern, info->patlen); } else { printf("--string %s", invert ? "! " : ""); print_string(info->pattern, info->patlen); } printf("--string-algo %s ", info->algo); if (info->from_offset != 0) printf("--string-from %u ", info->from_offset); if (info->to_offset != 0) printf("--string-to %u ", info->to_offset); if (info->u.v1.flags & XT_STRING_FLAG_IGNORECASE) printf("--string-icase "); } static int compare(const struct ebt_entry_match *m1, const struct ebt_entry_match *m2) { const struct xt_string_info *info1 = (const struct xt_string_info *) m1->data; const struct xt_string_info *info2 = (const struct xt_string_info *) m2->data; if (info1->from_offset != info2->from_offset) return 0; if (info1->to_offset != info2->to_offset) return 0; if (info1->u.v1.flags != info2->u.v1.flags) return 0; if (info1->patlen != info2->patlen) return 0; if (strncmp (info1->algo, info2->algo, XT_STRING_MAX_ALGO_NAME_SIZE) != 0) return 0; if (strncmp (info1->pattern, info2->pattern, info1->patlen) != 0) return 0; return 1; } static struct ebt_u_match string_match = { .name = "string", .revision = 1, .size = sizeof(struct xt_string_info), .help = print_help, .init = init, .parse = parse, .final_check = final_check, .print = print, .compare = compare, .extra_ops = opts, }; static void _INIT(void) { ebt_register_match(&string_match); }