Browse code

clamav-for-windows draft#2

aCaB authored on 2010/07/07 10:01:55
Showing 9 changed files
... ...
@@ -188,14 +188,15 @@ CL_CLEAN = File is scanned
188 188
 CL_BREAK = Whitelisted by callback - file is skipped and marked as clean
189 189
 CL_VIRUS = Blacklisted by callback - file is skipped and marked as infected
190 190
 */
191
-extern void cl_engine_set_clcb_pre_scan(struct cl_engine *engine, clcb_pre_scan callback, void *context);
191
+extern void cl_engine_set_clcb_pre_scan(struct cl_engine *engine, clcb_pre_scan callback);
192 192
 
193 193
 
194
-typedef cl_error_t (*clcb_post_scan)(int fd, int result, void *context);
194
+typedef cl_error_t (*clcb_post_scan)(int fd, int result, const char *virname, void *context);
195 195
 /* POST-SCAN
196 196
 Input:
197 197
 fd      = File descriptor which is was scanned
198 198
 result  = The scan result for the file
199
+virname = Virus name if infected
199 200
 context = Opaque application provided data
200 201
 
201 202
 Output:
... ...
@@ -203,7 +204,7 @@ CL_CLEAN = Scan result is not overridden
203 203
 CL_BREAK = Whitelisted by callback - scan result is set to CL_CLEAN
204 204
 CL_VIRUS = Blacklisted by callback - scan result is set to CL_VIRUS
205 205
 */
206
-extern void cl_engine_set_clcb_post_scan(struct cl_engine *engine, clcb_post_scan callback, void *context);
206
+extern void cl_engine_set_clcb_post_scan(struct cl_engine *engine, clcb_post_scan callback);
207 207
 
208 208
 
209 209
 typedef int (*clcb_sigload)(const char *type, const char *name, void *context);
... ...
@@ -241,6 +242,7 @@ struct cl_cvd {		    /* field no. */
241 241
 
242 242
 /* file scanning */
243 243
 extern int cl_scandesc(int desc, const char **virname, unsigned long int *scanned, const struct cl_engine *engine, unsigned int scanoptions);
244
+extern int cl_scandesc_callback(int desc, const char **virname, unsigned long int *scanned, const struct cl_engine *engine, unsigned int scanoptions, void *context);
244 245
 
245 246
 extern int cl_scanfile(const char *filename, const char **virname, unsigned long int *scanned, const struct cl_engine *engine, unsigned int scanoptions);
246 247
 
... ...
@@ -1009,14 +1009,12 @@ int cli_bitset_test(bitset_t *bs, unsigned long bit_offset)
1009 1009
 	return (bs->bitset[char_offset] & ((unsigned char)1 << bit_offset));
1010 1010
 }
1011 1011
 
1012
-void cl_engine_set_clcb_pre_scan(struct cl_engine *engine, clcb_pre_scan callback, void *context) {
1012
+void cl_engine_set_clcb_pre_scan(struct cl_engine *engine, clcb_pre_scan callback) {
1013 1013
     engine->cb_pre_scan = callback;
1014
-    engine->cb_pre_scan_ctx = callback ? context : NULL;
1015 1014
 }
1016 1015
 
1017
-void cl_engine_set_clcb_post_scan(struct cl_engine *engine, clcb_post_scan callback, void *context) {
1016
+void cl_engine_set_clcb_post_scan(struct cl_engine *engine, clcb_post_scan callback) {
1018 1017
     engine->cb_post_scan = callback;
1019
-    engine->cb_post_scan_ctx = callback ? context : NULL;
1020 1018
 }
1021 1019
 
1022 1020
 void cl_engine_set_clcb_sigload(struct cl_engine *engine, clcb_sigload callback, void *context) {
... ...
@@ -125,6 +125,7 @@ typedef struct cli_ctx_tag {
125 125
     struct cli_dconf *dconf;
126 126
     fmap_t **fmap;
127 127
     bitset_t* hook_lsig_matches;
128
+    void *cb_ctx;
128 129
 #ifdef HAVE__INTERNAL__SHA_COLLECT
129 130
     char entry_filename[2048];
130 131
     int sha_collect;
... ...
@@ -251,9 +252,7 @@ struct cl_engine {
251 251
 
252 252
     /* Callback(s) */
253 253
     clcb_pre_scan cb_pre_scan;
254
-    void *cb_pre_scan_ctx;
255 254
     clcb_post_scan cb_post_scan;
256
-    void *cb_post_scan_ctx;
257 255
     clcb_sigload cb_sigload;
258 256
     void *cb_sigload_ctx;
259 257
 
... ...
@@ -1897,7 +1897,7 @@ static void emax_reached(cli_ctx *ctx) {
1897 1897
 #define ret_from_magicscan(retcode) do {							\
1898 1898
     cli_dbgmsg("cli_magic_scandesc: returning %d %s\n", retcode, __AT__);			\
1899 1899
     if(ctx->engine->cb_post_scan) {								\
1900
-	switch(ctx->engine->cb_post_scan(desc, retcode, ctx->engine->cb_post_scan_ctx)) {	\
1900
+	switch(ctx->engine->cb_post_scan(desc, retcode, ctx->virname ? *ctx->virname : NULL, ctx->cb_ctx)) {		\
1901 1901
 	case CL_BREAK:										\
1902 1902
 	    cli_dbgmsg("cli_magic_scandesc: file whitelisted by callback\n");			\
1903 1903
 	    return CL_CLEAN;									\
... ...
@@ -1970,7 +1970,7 @@ int cli_magic_scandesc(int desc, cli_ctx *ctx)
1970 1970
     }
1971 1971
 
1972 1972
     if(ctx->engine->cb_pre_scan) {
1973
-	switch(ctx->engine->cb_pre_scan(desc, ctx->engine->cb_pre_scan_ctx)) {
1973
+	switch(ctx->engine->cb_pre_scan(desc, ctx->cb_ctx)) {
1974 1974
 	case CL_BREAK:
1975 1975
 	    cli_dbgmsg("cli_magic_scandesc: file whitelisted by callback\n");
1976 1976
 	    funmap(*ctx->fmap);
... ...
@@ -2424,6 +2424,54 @@ int cl_scandesc(int desc, const char **virname, unsigned long int *scanned, cons
2424 2424
     return cli_scandesc_stats(desc, virname, NULL, NULL, scanned, engine, scanoptions);
2425 2425
 }
2426 2426
 
2427
+
2428
+int cl_scandesc_callback(int desc, const char **virname, unsigned long int *scanned, const struct cl_engine *engine, unsigned int scanoptions, void *context)
2429
+{
2430
+    cli_ctx ctx;
2431
+    int rc;
2432
+
2433
+    memset(&ctx, '\0', sizeof(cli_ctx));
2434
+    ctx.engine = engine;
2435
+    ctx.virname = virname;
2436
+    ctx.scanned = scanned;
2437
+    ctx.options = scanoptions;
2438
+    ctx.found_possibly_unwanted = 0;
2439
+    ctx.container_type = CL_TYPE_ANY;
2440
+    ctx.container_size = 0;
2441
+    ctx.dconf = (struct cli_dconf *) engine->dconf;
2442
+    ctx.cb_ctx = context;
2443
+    ctx.fmap = cli_calloc(sizeof(fmap_t *), ctx.engine->maxreclevel + 2);
2444
+    if(!ctx.fmap)
2445
+	return CL_EMEM;
2446
+    if (!(ctx.hook_lsig_matches = cli_bitset_init())) {
2447
+	free(ctx.fmap);
2448
+	return CL_EMEM;
2449
+    }
2450
+
2451
+#ifdef HAVE__INTERNAL__SHA_COLLECT
2452
+    if(scanoptions & CL_SCAN_INTERNAL_COLLECT_SHA) {
2453
+	char link[32];
2454
+	ssize_t linksz;
2455
+
2456
+	snprintf(link, sizeof(link), "/proc/self/fd/%u", desc);
2457
+	link[sizeof(link)-1]='\0';
2458
+	if((linksz=readlink(link, ctx.entry_filename, sizeof(ctx.entry_filename)))==-1) {
2459
+	    cli_errmsg("failed to resolve filename for descriptor %d (%s)\n", desc, link);
2460
+	    strcpy(ctx.entry_filename, "NO_IDEA");
2461
+	} else
2462
+	    ctx.entry_filename[linksz]='\0';
2463
+    } while(0);
2464
+#endif
2465
+
2466
+    rc = cli_magic_scandesc(desc, &ctx);
2467
+
2468
+    cli_bitset_free(ctx.hook_lsig_matches);
2469
+    free(ctx.fmap);
2470
+    if(rc == CL_CLEAN && ctx.found_possibly_unwanted)
2471
+    	rc = CL_VIRUS;
2472
+    return rc;
2473
+}
2474
+
2427 2475
 int cli_found_possibly_unwanted(cli_ctx* ctx)
2428 2476
 {
2429 2477
 	if(ctx->virname) {
... ...
@@ -45,7 +45,7 @@
45 45
 				Name="VCCLCompilerTool"
46 46
 				Optimization="0"
47 47
 				AdditionalIncludeDirectories="&quot;$(SolutionDir)&quot;;&quot;$(SolutionDir)..\libclamav&quot;;&quot;$(SolutionDir)compat&quot;;&quot;$(SolutionDir)3rdparty\zlib&quot;;&quot;$(SolutionDir)3rdparty\pthreads&quot;;&quot;$(SolutionDir)..&quot;"
48
-				PreprocessorDefinitions="WIN32_LEAN_AND_MEAN;HAVE_CONFIG_H;_BIND_TO_CURRENT_VCLIBS_VERSION=1;CLAMAV_EXPORTS"
48
+				PreprocessorDefinitions="WIN32_LEAN_AND_MEAN;HAVE_CONFIG_H;_BIND_TO_CURRENT_VCLIBS_VERSION=1;CLAMAPI= __declspec(dllexport)"
49 49
 				MinimalRebuild="true"
50 50
 				BasicRuntimeChecks="3"
51 51
 				RuntimeLibrary="3"
... ...
@@ -123,7 +123,7 @@
123 123
 				Optimization="2"
124 124
 				EnableIntrinsicFunctions="true"
125 125
 				AdditionalIncludeDirectories="&quot;$(SolutionDir)&quot;;&quot;$(SolutionDir)..\libclamav&quot;;&quot;$(SolutionDir)compat&quot;;&quot;$(SolutionDir)3rdparty\zlib&quot;;&quot;$(SolutionDir)3rdparty\pthreads&quot;;&quot;$(SolutionDir)..&quot;"
126
-				PreprocessorDefinitions="WIN32_LEAN_AND_MEAN;HAVE_CONFIG_H;_BIND_TO_CURRENT_VCLIBS_VERSION=1;CLAMAV_EXPORTS"
126
+				PreprocessorDefinitions="WIN32_LEAN_AND_MEAN;HAVE_CONFIG_H;_BIND_TO_CURRENT_VCLIBS_VERSION=1;CLAMAPI= __declspec(dllexport)"
127 127
 				RuntimeLibrary="2"
128 128
 				EnableFunctionLevelLinking="true"
129 129
 				UsePrecompiledHeader="0"
130 130
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+#if HAVE_CONFIG_H
1
+#include "clamav-config.h"
2
+#endif
3
+
4
+#include "clscanapi.h"
5
+
6
+typedef void (CALL_CONVENTION *CLAM_PASSWORD_CALLBACK)(int objectType, const wchar_t *pObjectName, wchar_t *pPassword, int *pPasswordLen, void *context);
7
+
8
+int CLAMAPI Scan_SetPasswordCallback(CClamAVScanner *pScanner, CLAM_PASSWORD_CALLBACK pfnCallback, void *pContext) {
9
+    return CLAMAPI_SUCCESS;
10
+}
11
+
12
+int CLAMAPI Scan_ScanObjectInMemory(CClamAVScanner *pScanner, const void *pObject, unsigned int objectSize, int objectType, int action, int impersonatePID, int *pScanStatus, PCLAM_SCAN_INFO_LIST *pInfoList) {
13
+    return CLAMAPI_SUCCESS;
14
+}
0 15
\ No newline at end of file
... ...
@@ -25,24 +25,21 @@
25 25
                                    CLAMAPI interface
26 26
 ***************************************************************************************/
27 27
 
28
-/* CLAMAPI declspec */
29
-#ifdef CLAMAV_EXPORTS
30
-#define CLAMAPI __declspec(dllexport)
31
-#else
32
-#define CLAMAPI __declspec(dllimport)
33
-#endif
34
-
35 28
 /* CLAMAPI calling convention. Please do not touch */
36 29
 #ifndef CALL_CONVENTION
37 30
 #define CALL_CONVENTION __cdecl
38 31
 #endif
39 32
 
33
+#ifndef CLAMAPI
34
+#define CLAMAPI
35
+#endif
36
+
40 37
 
41 38
 /* CLAMAPI - return codes */
42 39
 /* Always check for the return value of CLAMAPI's 
43 40
  * Possible values are:
44 41
  * - return_value == CLAMAPI_SUCCESS: API succeded
45
- * - return_value != CLAMAPI_SUCCESS: API failed (call ClamGetErrorMsg(return_value) to retrieve the error message)
42
+ * - return_value != CLAMAPI_SUCCESS: API failed (call Scan_GetErrorMsg(return_value) to retrieve the error message)
46 43
  */
47 44
 #define CLAMAPI_SUCCESS 0
48 45
 
... ...
@@ -67,9 +64,10 @@ enum CLAM_SCAN_OPTIONS {
67 67
 /* CLAMAPI SCAN PHASES */
68 68
 /* Define the scan phase to which the returned results refer to */
69 69
 typedef enum _CLAM_SCAN_PHASE {
70
+    SCAN_PHASE_INITIAL,	 /* ight before ClamAV starts scanning the entry (outer) file - in scan callback mode only */
70 71
     SCAN_PHASE_PRESCAN,	 /* Right before ClamAV starts scanning the current file - in scan callback mode only */
71 72
     SCAN_PHASE_POSTSCAN, /* After ClamAV has scanned the current file - in scan callback mode only */
72
-    SCAN_PHASE_FINAL	 /* Upon returning from ScanObject */
73
+    SCAN_PHASE_FINAL	 /* After ClamAV has scanned the entry (outer) file (callback) and upon returning from ScanObject */
73 74
 } CLAM_SCAN_PHASE;
74 75
 
75 76
 
... ...
@@ -88,12 +86,15 @@ typedef struct _CLAM_SCAN_INFO {
88 88
     /* Presence: ALWAYS */
89 89
     int cbSize;
90 90
 
91
+    /** A single field that can store information regarding packers, installers, compound objects etc **/
92
+    int flags;
93
+
91 94
     /** The phase to which the results refer to **/
92 95
     /* Presence: ALWAYS */
93 96
     CLAM_SCAN_PHASE scanPhase;
94 97
 
95 98
     /** Error condition **/
96
-    /* Possible values: CLAMAPI_SUCCESS if no error; call ClamGetErrorMsg(errorCode)
99
+    /* Possible values: CLAMAPI_SUCCESS if no error; call Scan_GetErrorMsg(errorCode)
97 100
      * to retrieve the error message */
98 101
     /* Presence: ALWAYS */
99 102
     int errorCode;
... ...
@@ -273,7 +274,7 @@ int CLAMAPI Scan_SetOption(CClamAVScanner *pScanner, int option, void *value, un
273 273
  * INPUT @param errorCode
274 274
  * NOTE: the returned string is not to be freed!
275 275
  */
276
-const wchar_t *ClamGetErrorMsg(int errorCode);
276
+CLAMAPI const wchar_t * Scan_GetErrorMsg(int errorCode);
277 277
 
278 278
 #ifdef __cplusplus
279 279
 }; /* extern "C" */
... ...
@@ -3,6 +3,9 @@
3 3
 // check scan funcs
4 4
 // after scan returns and ret!=CL_VIRUS pInfoList NULL or unchanged?
5 5
 // changed set option value to 0 or non 0
6
+// restore file position
7
+// cb context per instance or per scanobj ??
8
+// optional shit to really be OPTIONAL!
6 9
 
7 10
 /*
8 11
  * Copyright (C) 2010 Sourcefire, Inc.
... ...
@@ -49,6 +52,9 @@ unsigned int engine_refcnt;
49 49
 #define lock_engine()(WaitForSingleObject(engine_mutex, INFINITE) == WAIT_FAILED)
50 50
 #define unlock_engine() do {ReleaseMutex(engine_mutex);} while(0)
51 51
 
52
+cl_error_t prescan_cb(int fd, void *context);
53
+cl_error_t postscan_cb(int fd, int result, const char *virname, void *context);
54
+
52 55
 BOOL interface_setup(void) {
53 56
     if(!(engine_mutex = CreateMutex(NULL, FALSE, NULL)))
54 57
 	return FALSE;
... ...
@@ -127,6 +133,7 @@ int CLAMAPI Scan_Initialize(const wchar_t *pEnginesFolder, const wchar_t *pTempR
127 127
 	unlock_engine();
128 128
 	FAIL(CL_EMEM, "Not enough memory for a new engine");
129 129
     }
130
+    cl_engine_set_clcb_pre_scan(engine, prescan_cb);
130 131
     if(!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, pTempRoot, -1, tmpdir, sizeof(tmpdir), NULL, &cant_convert) || cant_convert) {
131 132
 	free_engine_and_unlock();
132 133
 	FAIL(CL_EARG, "Can't translate pTempRoot");
... ...
@@ -216,8 +223,11 @@ int CLAMAPI Scan_DestroyInstance(CClamAVScanner *pScanner) {
216 216
 
217 217
 int CLAMAPI Scan_SetScanCallback(CClamAVScanner *pScanner, CLAM_SCAN_CALLBACK pfnCallback, void *pContext) {
218 218
     instance *inst = (instance *)pScanner;
219
+    InterlockedIncrement(&inst->refcnt);
219 220
     inst->scancb = pfnCallback;
220 221
     inst->scancb_ctx = pContext;
222
+    InterlockedDecrement(&inst->refcnt);
223
+    
221 224
     WIN();
222 225
 }
223 226
 
... ...
@@ -327,11 +337,17 @@ int CLAMAPI Scan_ScanObject(CClamAVScanner *pScanner, const wchar_t *pObjectPath
327 327
     return res;
328 328
 }
329 329
 
330
+struct scan_ctx {
331
+    int entryfd;
332
+    instance *inst;
333
+};
334
+
330 335
 int CLAMAPI Scan_ScanObjectByHandle(CClamAVScanner *pScanner, HANDLE object, int *pScanStatus, PCLAM_SCAN_INFO_LIST *pInfoList) {
331 336
     instance *inst = (instance *)pScanner;
332 337
     HANDLE duphdl, self;
333 338
     char *virname;
334 339
     int fd, res;
340
+    struct scan_ctx sctx;
335 341
 
336 342
     InterlockedIncrement(&inst->refcnt);
337 343
 
... ...
@@ -347,7 +363,9 @@ int CLAMAPI Scan_ScanObjectByHandle(CClamAVScanner *pScanner, HANDLE object, int
347 347
 	FAIL(CL_EOPEN, "Open handle failed");
348 348
     }
349 349
 
350
-    res = cl_scandesc(fd, &virname, NULL, engine, inst->scanopts);
350
+    sctx.entryfd = fd;
351
+    sctx.inst = inst;
352
+    res = cl_scandesc_callback(fd, &virname, NULL, engine, inst->scanopts, &sctx);
351 353
     InterlockedDecrement(&inst->refcnt);
352 354
     close(fd);
353 355
 
... ...
@@ -385,3 +403,68 @@ int CLAMAPI Scan_DeleteScanInfo(CClamAVScanner *pScanner, PCLAM_SCAN_INFO_LIST p
385 385
     WIN();
386 386
 }
387 387
 
388
+cl_error_t prescan_cb(int fd, void *context) {
389
+    struct scan_ctx *sctx = (struct scan_ctx *)context;
390
+    instance *inst = sctx->inst;
391
+    CLAM_SCAN_INFO si;
392
+    CLAM_ACTION act;
393
+
394
+    logg("in prescan cb with %d %p\n", fd, context);
395
+    si.cbSize = sizeof(si);
396
+    si.flags = 0;
397
+    si.scanPhase = (fd == sctx->entryfd) ? SCAN_PHASE_INITIAL : SCAN_PHASE_PRESCAN;
398
+    si.errorCode = CLAMAPI_SUCCESS;
399
+    si.pThreatType = NULL;
400
+    si.pThreatName = NULL;
401
+    si.object = (HANDLE)_get_osfhandle(fd);
402
+    si.pInnerObjectPath = NULL;
403
+    inst->scancb(&si, &act, inst->scancb_ctx);
404
+    switch(act) {
405
+	case CLAM_ACTION_SKIP:
406
+	    logg("prescan cb result: SKIP\n");
407
+	    return CL_BREAK;
408
+	case CLAM_ACTION_ABORT:
409
+	    logg("prescan cb result: ABORT\n");
410
+	    return CL_VIRUS;
411
+	default:
412
+	    logg("prescan cb returned bogus value\n");
413
+	case CLAM_ACTION_CONTINUE:
414
+	    logg("prescan cb result: CONTINUE\n");
415
+	    return CL_CLEAN;
416
+    }
417
+}
418
+
419
+cl_error_t postscan_cb(int fd, int result, const char *virname, void *context) {
420
+    struct scan_ctx *sctx = (struct scan_ctx *)context;
421
+    instance *inst = sctx->inst;
422
+    CLAM_SCAN_INFO si;
423
+    CLAM_ACTION act;
424
+
425
+    logg("in prostscan cb with %d %d %s %p\n", fd, result, virname, context);
426
+    si.cbSize = sizeof(si);
427
+    si.flags = 0;
428
+    si.scanPhase = (fd == sctx->entryfd) ? SCAN_PHASE_FINAL : SCAN_PHASE_POSTSCAN;
429
+    si.errorCode = CLAMAPI_SUCCESS;
430
+    si.pThreatType = NULL;
431
+    si.pThreatName = (result == CL_VIRUS) ? L"Fixme" : NULL; /* FIXME */
432
+    si.object = (HANDLE)_get_osfhandle(fd);
433
+    si.pInnerObjectPath = NULL;
434
+    inst->scancb(&si, &act, inst->scancb_ctx);
435
+    switch(act) {
436
+	case CLAM_ACTION_SKIP:
437
+	    logg("postscan cb result: SKIP\n");
438
+	    return CL_BREAK;
439
+	case CLAM_ACTION_ABORT:
440
+	    logg("postscan cb result: ABORT\n");
441
+	    return CL_VIRUS;
442
+	default:
443
+	    logg("postscan cb returned bogus value\n");
444
+	case CLAM_ACTION_CONTINUE:
445
+	    logg("prescan cb result: CONTINUE\n");
446
+	    return CL_CLEAN;
447
+    }
448
+}
449
+
450
+CLAMAPI const wchar_t * Scan_GetErrorMsg(int errorCode) {
451
+    return L"w00t!"; /* FIXME */
452
+}
... ...
@@ -32,6 +32,9 @@ EXPORTS cl_statchkdir @24
32 32
 EXPORTS cl_statfree @25
33 33
 EXPORTS cl_statinidir @26
34 34
 EXPORTS cl_strerror @27
35
+EXPORTS cl_engine_set_clcb_pre_scan @28
36
+EXPORTS cl_engine_set_clcb_post_scan @29
37
+EXPORTS cl_scandesc_callback @30
35 38
 
36 39
 
37 40
 ; path variables