/src/detect-filemagic.c

https://github.com/decanio/suricata-tilera · C · 491 lines · 310 code · 80 blank · 101 comment · 109 complexity · 32db0ca332d03921b03950738690a688 MD5 · raw file

  1. /* Copyright (C) 2007-2012 Open Information Security Foundation
  2. *
  3. * You can copy, redistribute or modify this Program under the terms of
  4. * the GNU General Public License version 2 as published by the Free
  5. * Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * version 2 along with this program; if not, write to the Free Software
  14. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. * 02110-1301, USA.
  16. */
  17. /**
  18. * \file
  19. *
  20. * \author Victor Julien <victor@inliniac.net>
  21. *
  22. */
  23. #include "suricata-common.h"
  24. #include "threads.h"
  25. #include "debug.h"
  26. #include "decode.h"
  27. #include "detect.h"
  28. #include "detect-parse.h"
  29. #include "detect-engine.h"
  30. #include "detect-engine-mpm.h"
  31. #include "detect-engine-state.h"
  32. #include "flow.h"
  33. #include "flow-var.h"
  34. #include "flow-util.h"
  35. #include "util-debug.h"
  36. #include "util-spm-bm.h"
  37. #include "util-magic.h"
  38. #include "util-print.h"
  39. #include "util-unittest.h"
  40. #include "util-unittest-helper.h"
  41. #include "app-layer.h"
  42. #include "stream-tcp.h"
  43. #include "detect-filemagic.h"
  44. #include "conf.h"
  45. #include "util-magic.h"
  46. static int DetectFilemagicMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
  47. uint8_t, File *, Signature *, SigMatch *);
  48. static int DetectFilemagicSetup (DetectEngineCtx *, Signature *, char *);
  49. static void DetectFilemagicRegisterTests(void);
  50. static void DetectFilemagicFree(void *);
  51. /**
  52. * \brief Registration function for keyword: filemagic
  53. */
  54. void DetectFilemagicRegister(void) {
  55. sigmatch_table[DETECT_FILEMAGIC].name = "filemagic";
  56. sigmatch_table[DETECT_FILEMAGIC].desc = "match on the information libmagic returns about a file";
  57. sigmatch_table[DETECT_FILEMAGIC].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/File-keywords#filemagic";
  58. sigmatch_table[DETECT_FILEMAGIC].FileMatch = DetectFilemagicMatch;
  59. sigmatch_table[DETECT_FILEMAGIC].alproto = ALPROTO_HTTP;
  60. sigmatch_table[DETECT_FILEMAGIC].Setup = DetectFilemagicSetup;
  61. sigmatch_table[DETECT_FILEMAGIC].Free = DetectFilemagicFree;
  62. sigmatch_table[DETECT_FILEMAGIC].RegisterTests = DetectFilemagicRegisterTests;
  63. SCLogDebug("registering filemagic rule option");
  64. return;
  65. }
  66. #define FILEMAGIC_MIN_SIZE 512
  67. /**
  68. * \brief run the magic check
  69. *
  70. * \param file the file
  71. *
  72. * \retval -1 error
  73. * \retval 0 ok
  74. */
  75. int FilemagicGlobalLookup(File *file) {
  76. if (file == NULL || file->chunks_head == NULL) {
  77. SCReturnInt(-1);
  78. }
  79. /* initial chunk already matching our requirement */
  80. if (file->chunks_head->len >= FILEMAGIC_MIN_SIZE) {
  81. file->magic = MagicGlobalLookup(file->chunks_head->data, FILEMAGIC_MIN_SIZE);
  82. } else {
  83. uint8_t *buf = SCMalloc(FILEMAGIC_MIN_SIZE);
  84. uint32_t size = 0;
  85. if (likely(buf != NULL)) {
  86. FileData *ffd = file->chunks_head;
  87. for ( ; ffd != NULL; ffd = ffd->next) {
  88. uint32_t copy_len = ffd->len;
  89. if (size + ffd->len > FILEMAGIC_MIN_SIZE)
  90. copy_len = FILEMAGIC_MIN_SIZE - size;
  91. memcpy(buf + size, ffd->data, copy_len);
  92. size += copy_len;
  93. if (size >= FILEMAGIC_MIN_SIZE) {
  94. file->magic = MagicGlobalLookup(buf, size);
  95. break;
  96. }
  97. /* file is done but smaller than FILEMAGIC_MIN_SIZE */
  98. if (ffd->next == NULL && file->state >= FILE_STATE_CLOSED) {
  99. file->magic = MagicGlobalLookup(buf, size);
  100. break;
  101. }
  102. }
  103. SCFree(buf);
  104. }
  105. }
  106. SCReturnInt(0);
  107. }
  108. /**
  109. * \brief run the magic check
  110. *
  111. * \param file the file
  112. *
  113. * \retval -1 error
  114. * \retval 0 ok
  115. */
  116. int FilemagicThreadLookup(magic_t *ctx, File *file) {
  117. if (ctx == NULL || file == NULL || file->chunks_head == NULL) {
  118. SCReturnInt(-1);
  119. }
  120. /* initial chunk already matching our requirement */
  121. if (file->chunks_head->len >= FILEMAGIC_MIN_SIZE) {
  122. file->magic = MagicThreadLookup(ctx, file->chunks_head->data, FILEMAGIC_MIN_SIZE);
  123. } else {
  124. uint8_t *buf = SCMalloc(FILEMAGIC_MIN_SIZE);
  125. uint32_t size = 0;
  126. if (likely(buf != NULL)) {
  127. FileData *ffd = file->chunks_head;
  128. for ( ; ffd != NULL; ffd = ffd->next) {
  129. uint32_t copy_len = ffd->len;
  130. if (size + ffd->len > FILEMAGIC_MIN_SIZE)
  131. copy_len = FILEMAGIC_MIN_SIZE - size;
  132. memcpy(buf + size, ffd->data, copy_len);
  133. size += copy_len;
  134. if (size >= FILEMAGIC_MIN_SIZE) {
  135. file->magic = MagicThreadLookup(ctx, buf, size);
  136. break;
  137. }
  138. /* file is done but smaller than FILEMAGIC_MIN_SIZE */
  139. if (ffd->next == NULL && file->state >= FILE_STATE_CLOSED) {
  140. file->magic = MagicThreadLookup(ctx, buf, size);
  141. break;
  142. }
  143. }
  144. SCFree(buf);
  145. }
  146. }
  147. SCReturnInt(0);
  148. }
  149. /**
  150. * \brief match the specified filemagic
  151. *
  152. * \param t thread local vars
  153. * \param det_ctx pattern matcher thread local data
  154. * \param f *LOCKED* flow
  155. * \param flags direction flags
  156. * \param file file being inspected
  157. * \param s signature being inspected
  158. * \param m sigmatch that we will cast into DetectFilemagicData
  159. *
  160. * \retval 0 no match
  161. * \retval 1 match
  162. */
  163. static int DetectFilemagicMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
  164. Flow *f, uint8_t flags, File *file, Signature *s, SigMatch *m)
  165. {
  166. SCEnter();
  167. int ret = 0;
  168. DetectFilemagicData *filemagic = (DetectFilemagicData *)m->ctx;
  169. if (file->txid < det_ctx->tx_id)
  170. SCReturnInt(0);
  171. if (file->txid > det_ctx->tx_id)
  172. SCReturnInt(0);
  173. DetectFilemagicThreadData *tfilemagic = (DetectFilemagicThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, filemagic->thread_ctx_id);
  174. if (tfilemagic == NULL) {
  175. SCReturnInt(0);
  176. }
  177. if (file->magic == NULL) {
  178. FilemagicThreadLookup(&tfilemagic->ctx, file);
  179. }
  180. if (file->magic != NULL) {
  181. SCLogDebug("magic %s", file->magic);
  182. /* we include the \0 in the inspection, so patterns can match on the
  183. * end of the string. */
  184. if (BoyerMooreNocase(filemagic->name, filemagic->len, (uint8_t *)file->magic,
  185. strlen(file->magic) + 1, filemagic->bm_ctx->bmGs,
  186. filemagic->bm_ctx->bmBc) != NULL)
  187. {
  188. #ifdef DEBUG
  189. if (SCLogDebugEnabled()) {
  190. char *name = SCMalloc(filemagic->len + 1);
  191. if (name != NULL) {
  192. memcpy(name, filemagic->name, filemagic->len);
  193. name[filemagic->len] = '\0';
  194. SCLogDebug("will look for filemagic %s", name);
  195. }
  196. }
  197. #endif
  198. if (!(filemagic->flags & DETECT_CONTENT_NEGATED)) {
  199. ret = 1;
  200. }
  201. } else if (filemagic->flags & DETECT_CONTENT_NEGATED) {
  202. SCLogDebug("negated match");
  203. ret = 1;
  204. }
  205. }
  206. SCReturnInt(ret);
  207. }
  208. /**
  209. * \brief Parse the filemagic keyword
  210. *
  211. * \param idstr Pointer to the user provided option
  212. *
  213. * \retval filemagic pointer to DetectFilemagicData on success
  214. * \retval NULL on failure
  215. */
  216. static DetectFilemagicData *DetectFilemagicParse (char *str)
  217. {
  218. DetectFilemagicData *filemagic = NULL;
  219. /* We have a correct filemagic option */
  220. filemagic = SCMalloc(sizeof(DetectFilemagicData));
  221. if (unlikely(filemagic == NULL))
  222. goto error;
  223. memset(filemagic, 0x00, sizeof(DetectFilemagicData));
  224. if (DetectContentDataParse ("filemagic", str, &filemagic->name, &filemagic->len, &filemagic->flags) == -1) {
  225. goto error;
  226. }
  227. filemagic->bm_ctx = BoyerMooreCtxInit(filemagic->name, filemagic->len);
  228. if (filemagic->bm_ctx == NULL) {
  229. goto error;
  230. }
  231. SCLogDebug("flags %02X", filemagic->flags);
  232. if (filemagic->flags & DETECT_CONTENT_NEGATED) {
  233. SCLogDebug("negated filemagic");
  234. }
  235. BoyerMooreCtxToNocase(filemagic->bm_ctx, filemagic->name, filemagic->len);
  236. #ifdef DEBUG
  237. if (SCLogDebugEnabled()) {
  238. char *name = SCMalloc(filemagic->len + 1);
  239. if (name != NULL) {
  240. memcpy(name, filemagic->name, filemagic->len);
  241. name[filemagic->len] = '\0';
  242. SCLogDebug("will look for filemagic %s", name);
  243. }
  244. }
  245. #endif
  246. return filemagic;
  247. error:
  248. if (filemagic != NULL)
  249. DetectFilemagicFree(filemagic);
  250. return NULL;
  251. }
  252. static void *DetectFilemagicThreadInit(void *data) {
  253. char *filename = NULL;
  254. FILE *fd = NULL;
  255. DetectFilemagicData *filemagic = (DetectFilemagicData *)data;
  256. BUG_ON(filemagic == NULL);
  257. DetectFilemagicThreadData *t = SCMalloc(sizeof(DetectFilemagicThreadData));
  258. if (unlikely(t == NULL)) {
  259. SCLogError(SC_ERR_LUAJIT_ERROR, "couldn't alloc ctx memory");
  260. return NULL;
  261. }
  262. memset(t, 0x00, sizeof(DetectFilemagicThreadData));
  263. t->ctx = magic_open(0);
  264. if (t->ctx == NULL) {
  265. SCLogError(SC_ERR_MAGIC_OPEN, "magic_open failed: %s", magic_error(t->ctx));
  266. goto error;
  267. }
  268. (void)ConfGet("magic-file", &filename);
  269. if (filename != NULL) {
  270. SCLogInfo("using magic-file %s", filename);
  271. if ( (fd = fopen(filename, "r")) == NULL) {
  272. SCLogWarning(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno));
  273. goto error;
  274. }
  275. fclose(fd);
  276. }
  277. if (magic_load(t->ctx, filename) != 0) {
  278. SCLogError(SC_ERR_MAGIC_LOAD, "magic_load failed: %s", magic_error(t->ctx));
  279. goto error;
  280. }
  281. return (void *)t;
  282. error:
  283. if (t->ctx)
  284. magic_close(t->ctx);
  285. SCFree(t);
  286. return NULL;
  287. }
  288. static void DetectFilemagicThreadFree(void *ctx) {
  289. if (ctx != NULL) {
  290. DetectFilemagicThreadData *t = (DetectFilemagicThreadData *)ctx;
  291. if (t->ctx)
  292. magic_close(t->ctx);
  293. SCFree(t);
  294. }
  295. }
  296. /**
  297. * \brief this function is used to parse filemagic options
  298. * \brief into the current signature
  299. *
  300. * \param de_ctx pointer to the Detection Engine Context
  301. * \param s pointer to the Current Signature
  302. * \param str pointer to the user provided "filemagic" option
  303. *
  304. * \retval 0 on Success
  305. * \retval -1 on Failure
  306. */
  307. static int DetectFilemagicSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
  308. {
  309. DetectFilemagicData *filemagic = NULL;
  310. SigMatch *sm = NULL;
  311. filemagic = DetectFilemagicParse(str);
  312. if (filemagic == NULL)
  313. goto error;
  314. filemagic->thread_ctx_id = DetectRegisterThreadCtxFuncs(de_ctx, "filemagic",
  315. DetectFilemagicThreadInit, (void *)filemagic,
  316. DetectFilemagicThreadFree, 1);
  317. if (filemagic->thread_ctx_id == -1)
  318. goto error;
  319. /* Okay so far so good, lets get this into a SigMatch
  320. * and put it in the Signature. */
  321. sm = SigMatchAlloc();
  322. if (sm == NULL)
  323. goto error;
  324. sm->type = DETECT_FILEMAGIC;
  325. sm->ctx = (void *)filemagic;
  326. SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_FILEMATCH);
  327. if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_HTTP) {
  328. SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
  329. goto error;
  330. }
  331. AppLayerHtpNeedFileInspection();
  332. /** \todo remove this once we support more than http */
  333. s->alproto = ALPROTO_HTTP;
  334. s->file_flags |= (FILE_SIG_NEED_FILE|FILE_SIG_NEED_MAGIC);
  335. return 0;
  336. error:
  337. if (filemagic != NULL)
  338. DetectFilemagicFree(filemagic);
  339. if (sm != NULL)
  340. SCFree(sm);
  341. return -1;
  342. }
  343. /**
  344. * \brief this function will free memory associated with DetectFilemagicData
  345. *
  346. * \param filemagic pointer to DetectFilemagicData
  347. */
  348. static void DetectFilemagicFree(void *ptr) {
  349. if (ptr != NULL) {
  350. DetectFilemagicData *filemagic = (DetectFilemagicData *)ptr;
  351. if (filemagic->bm_ctx != NULL) {
  352. BoyerMooreCtxDeInit(filemagic->bm_ctx);
  353. }
  354. if (filemagic->name != NULL)
  355. SCFree(filemagic->name);
  356. SCFree(filemagic);
  357. }
  358. }
  359. #ifdef UNITTESTS /* UNITTESTS */
  360. /**
  361. * \test DetectFilemagicTestParse01
  362. */
  363. int DetectFilemagicTestParse01 (void) {
  364. DetectFilemagicData *dnd = DetectFilemagicParse("\"secret.pdf\"");
  365. if (dnd != NULL) {
  366. DetectFilemagicFree(dnd);
  367. return 1;
  368. }
  369. return 0;
  370. }
  371. /**
  372. * \test DetectFilemagicTestParse02
  373. */
  374. int DetectFilemagicTestParse02 (void) {
  375. int result = 0;
  376. DetectFilemagicData *dnd = DetectFilemagicParse("\"backup.tar.gz\"");
  377. if (dnd != NULL) {
  378. if (dnd->len == 13 && memcmp(dnd->name, "backup.tar.gz", 13) == 0) {
  379. result = 1;
  380. }
  381. DetectFilemagicFree(dnd);
  382. return result;
  383. }
  384. return 0;
  385. }
  386. /**
  387. * \test DetectFilemagicTestParse03
  388. */
  389. int DetectFilemagicTestParse03 (void) {
  390. int result = 0;
  391. DetectFilemagicData *dnd = DetectFilemagicParse("\"cmd.exe\"");
  392. if (dnd != NULL) {
  393. if (dnd->len == 7 && memcmp(dnd->name, "cmd.exe", 7) == 0) {
  394. result = 1;
  395. }
  396. DetectFilemagicFree(dnd);
  397. return result;
  398. }
  399. return 0;
  400. }
  401. #endif /* UNITTESTS */
  402. /**
  403. * \brief this function registers unit tests for DetectFilemagic
  404. */
  405. void DetectFilemagicRegisterTests(void) {
  406. #ifdef UNITTESTS /* UNITTESTS */
  407. UtRegisterTest("DetectFilemagicTestParse01", DetectFilemagicTestParse01, 1);
  408. UtRegisterTest("DetectFilemagicTestParse02", DetectFilemagicTestParse02, 1);
  409. UtRegisterTest("DetectFilemagicTestParse03", DetectFilemagicTestParse03, 1);
  410. #endif /* UNITTESTS */
  411. }