libxt_string.c 9.6 KB

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