Browse code

clamdscan: real-path checks for fd-pass/streaming

Real-path checks are still needed in clamdscan when doing fd-passing and
streaming. This commit remedies that and improves some of the error
handling.

In addition, some cleanup to eliminate warnings on Windows added to the
shared code.

Micah Snyder authored on 2020/08/06 14:33:44
Showing 5 changed files
... ...
@@ -412,59 +412,81 @@ struct client_serial_data {
412 412
 
413 413
 /* FTW callback for scanning in non IDSESSION mode
414 414
  * Returns SUCCESS or BREAK on success, CL_EXXX on error */
415
-static int serial_callback(STATBUF *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data)
415
+static cl_error_t serial_callback(STATBUF *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data)
416 416
 {
417
+    int status = CL_EOPEN;
418
+
417 419
     struct client_serial_data *c = (struct client_serial_data *)data->data;
418 420
     int sockd, ret;
419
-    const char *f = filename;
421
+    const char *f       = filename;
422
+    char *real_filename = NULL;
420 423
 
421 424
     UNUSEDPARAM(sb);
422 425
 
423
-    if (chkpath(path))
424
-        return CL_SUCCESS;
426
+    if (reason != visit_directory_toplev) {
427
+        if (CL_SUCCESS != cli_realpath((const char *)path, &real_filename)) {
428
+            logg("*Failed to determine real filename of %s.\n", path);
429
+        } else {
430
+            path = real_filename;
431
+        }
432
+    }
433
+
434
+    if (chkpath(path)) {
435
+        status = CL_SUCCESS;
436
+        goto done;
437
+    }
425 438
     c->files++;
426 439
     switch (reason) {
427 440
         case error_stat:
428 441
             logg("!Can't access file %s\n", path);
429 442
             c->errors++;
430
-            return CL_SUCCESS;
443
+            status = CL_SUCCESS;
444
+            goto done;
431 445
         case error_mem:
432 446
             logg("!Memory allocation failed in ftw\n");
433 447
             c->errors++;
434
-            return CL_EMEM;
448
+            status = CL_EMEM;
449
+            goto done;
435 450
         case warning_skipped_dir:
436 451
             logg("^Directory recursion limit reached\n");
437 452
         case warning_skipped_link:
438
-            return CL_SUCCESS;
453
+            status = CL_SUCCESS;
454
+            goto done;
439 455
         case warning_skipped_special:
440 456
             logg("^%s: Not supported file type\n", path);
441 457
             c->errors++;
442
-            return CL_SUCCESS;
458
+            status = CL_SUCCESS;
459
+            goto done;
443 460
         case visit_directory_toplev:
444
-            if (c->scantype >= STREAM)
445
-                return CL_SUCCESS;
446
-            f        = path;
447
-            filename = NULL;
461
+            if (c->scantype >= STREAM) {
462
+                status = CL_SUCCESS;
463
+                goto done;
464
+            }
465
+            f = path;
448 466
         case visit_file:
449 467
             break;
450 468
     }
451 469
 
452 470
     if ((sockd = dconnect()) < 0) {
453
-        if (filename) free(filename);
454 471
         c->errors++;
455
-        return CL_EOPEN;
472
+        goto done;
456 473
     }
457 474
     ret = dsresult(sockd, c->scantype, f, &c->printok, &c->errors);
458
-    if (filename) free(filename);
459 475
     closesocket(sockd);
460 476
     if (ret < 0) {
461 477
         c->errors++;
462
-        return CL_EOPEN;
478
+        goto done;
463 479
     }
464 480
     c->infected += ret;
465
-    if (reason == visit_directory_toplev)
466
-        return CL_BREAK;
467
-    return CL_SUCCESS;
481
+    if (reason == visit_directory_toplev) {
482
+        status = CL_BREAK;
483
+        goto done;
484
+    }
485
+
486
+    status = CL_SUCCESS;
487
+done:
488
+    free(filename);
489
+    return status;
468 490
 }
469 491
 
470 492
 /* Non-IDSESSION handler
... ...
@@ -571,43 +593,62 @@ static int dspresult(struct client_parallel_data *c)
571 571
 
572 572
 /* FTW callback for scanning in IDSESSION mode
573 573
  * Returns SUCCESS on success, CL_EXXX or BREAK on error */
574
-static int parallel_callback(STATBUF *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data)
574
+static cl_error_t parallel_callback(STATBUF *sb, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data)
575 575
 {
576
+    cl_error_t status = CL_EOPEN;
577
+
576 578
     struct client_parallel_data *c = (struct client_parallel_data *)data->data;
577
-    struct SCANID *cid;
578
-    int res = CL_CLEAN;
579
+    struct SCANID *cid             = NULL;
580
+    int res                        = CL_CLEAN;
581
+
582
+    char *real_filename = NULL;
579 583
 
580 584
     UNUSEDPARAM(sb);
585
+    UNUSEDPARAM(path);
586
+
587
+    if (reason != visit_directory_toplev) {
588
+        if (CL_SUCCESS != cli_realpath((const char *)filename, &real_filename)) {
589
+            logg("*Failed to determine real filename of %s.\n", filename);
590
+        } else {
591
+            free(filename);
592
+            filename = real_filename;
593
+        }
594
+    }
581 595
 
582
-    if (chkpath(path))
583
-        return CL_SUCCESS;
596
+    if (chkpath(filename)) {
597
+        goto done;
598
+    }
584 599
     c->files++;
585 600
     switch (reason) {
586 601
         case error_stat:
587
-            logg("!Can't access file %s\n", path);
602
+            logg("!Can't access file %s\n", filename);
588 603
             c->errors++;
589
-            return CL_SUCCESS;
604
+            status = CL_SUCCESS;
605
+            goto done;
590 606
         case error_mem:
591 607
             logg("!Memory allocation failed in ftw\n");
592 608
             c->errors++;
593
-            return CL_EMEM;
609
+            status = CL_EMEM;
610
+            goto done;
594 611
         case warning_skipped_dir:
595 612
             logg("^Directory recursion limit reached\n");
596
-            return CL_SUCCESS;
613
+            status = CL_SUCCESS;
614
+            goto done;
597 615
         case warning_skipped_special:
598
-            logg("^%s: Not supported file type\n", path);
616
+            logg("^%s: Not supported file type\n", filename);
599 617
             c->errors++;
600 618
         case warning_skipped_link:
601 619
         case visit_directory_toplev:
602
-            return CL_SUCCESS;
620
+            status = CL_SUCCESS;
621
+            goto done;
603 622
         case visit_file:
604 623
             break;
605 624
     }
606 625
 
607 626
     while (1) {
608 627
         /* consume all the available input to let some of the clamd
609
-	 * threads blocked on send() to be dead.
610
-	 * by doing so we shouldn't deadlock on the next recv() */
628
+	     * threads blocked on send() to be dead.
629
+	     * by doing so we shouldn't deadlock on the next recv() */
611 630
         fd_set rfds, wfds;
612 631
         FD_ZERO(&rfds);
613 632
         FD_SET(c->sockd, &rfds);
... ...
@@ -615,31 +656,20 @@ static int parallel_callback(STATBUF *sb, char *filename, const char *path, enum
615 615
         FD_SET(c->sockd, &wfds);
616 616
         if (select(c->sockd + 1, &rfds, &wfds, NULL, NULL) < 0) {
617 617
             if (errno == EINTR) continue;
618
-            free(filename);
619 618
             logg("!select() failed during session: %s\n", strerror(errno));
620
-            return CL_BREAK;
619
+            status = CL_BREAK;
620
+            goto done;
621 621
         }
622 622
         if (FD_ISSET(c->sockd, &rfds)) {
623 623
             if (dspresult(c)) {
624
-                free(filename);
625
-                return CL_BREAK;
624
+                status = CL_BREAK;
625
+                goto done;
626 626
             } else
627 627
                 continue;
628 628
         }
629 629
         if (FD_ISSET(c->sockd, &wfds)) break;
630 630
     }
631 631
 
632
-    cid = (struct SCANID *)malloc(sizeof(struct SCANID));
633
-    if (!cid) {
634
-        free(filename);
635
-        logg("!Failed to allocate scanid entry: %s\n", strerror(errno));
636
-        return CL_BREAK;
637
-    }
638
-    cid->id   = ++c->lastid;
639
-    cid->file = filename;
640
-    cid->next = c->ids;
641
-    c->ids    = cid;
642
-
643 632
     switch (c->scantype) {
644 633
 #ifdef HAVE_FD_PASSING
645 634
         case FILDES:
... ...
@@ -653,13 +683,32 @@ static int parallel_callback(STATBUF *sb, char *filename, const char *path, enum
653 653
     if (res <= 0) {
654 654
         c->printok = 0;
655 655
         c->errors++;
656
-        c->ids = cid->next;
657
-        c->lastid--;
658
-        free(cid);
656
+        status = res ? CL_BREAK : CL_SUCCESS;
657
+        goto done;
658
+    }
659
+
660
+    cid = (struct SCANID *)malloc(sizeof(struct SCANID));
661
+    if (!cid) {
662
+        logg("!Failed to allocate scanid entry: %s\n", strerror(errno));
663
+        status = CL_BREAK;
664
+        goto done;
665
+    }
666
+
667
+    cid->id   = ++c->lastid;
668
+    cid->file = filename;
669
+    cid->next = c->ids;
670
+    c->ids    = cid;
671
+
672
+    /* Give up ownership of the filename to the client parralel scan ID list */
673
+    filename = NULL;
674
+
675
+    status = CL_SUCCESS;
676
+
677
+done:
678
+    if (NULL != filename) {
659 679
         free(filename);
660
-        return res ? CL_BREAK : CL_SUCCESS;
661 680
     }
662
-    return CL_SUCCESS;
681
+    return status;
663 682
 }
664 683
 
665 684
 /* IDSESSION handler
... ...
@@ -929,7 +929,7 @@ struct cli_ftw_cbdata {
929 929
  * after an error, we call the callback with reason == error,
930 930
  * and if it returns CL_BREAK we break.
931 931
  */
932
-typedef int (*cli_ftw_cb)(STATBUF *stat_buf, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data);
932
+typedef cl_error_t (*cli_ftw_cb)(STATBUF *stat_buf, char *filename, const char *path, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data);
933 933
 
934 934
 /*
935 935
  * returns 1 if the path should be skipped and 0 otherwise
... ...
@@ -696,7 +696,9 @@ int actsetup(const struct optstruct *opts)
696 696
 {
697 697
     int move = optget(opts, "move")->enabled;
698 698
     if (move || optget(opts, "copy")->enabled) {
699
+#ifndef _WIN32
699 700
         cl_error_t ret;
701
+#endif
700 702
         actarget = optget(opts, move ? "move" : "copy")->strarg;
701 703
 #ifndef _WIN32
702 704
         ret = cli_realpath((const char *)actarget, &actarget);
... ...
@@ -256,11 +256,9 @@ int filecopy(const char *src, const char *dest)
256 256
 #endif
257 257
 }
258 258
 
259
-int close_std_descriptors() {
260
-#ifdef _WIN32
261
-    return -1;
262
-#else
263
-
259
+#ifndef _WIN32
260
+int close_std_descriptors()
261
+{
264 262
     int fds[3], i;
265 263
 
266 264
     fds[0] = open("/dev/null", O_RDONLY);
... ...
@@ -289,67 +287,51 @@ int close_std_descriptors() {
289 289
             close(fds[i]);
290 290
 
291 291
     return 0;
292
-#endif
293 292
 }
294 293
 
295
-
296
-int daemonize_all_return(void) {
297
-#ifdef _WIN32
298
-    fputs("Background mode is not supported on your operating system\n", stderr);
299
-    return -1;
300
-#else
294
+int daemonize_all_return(void)
295
+{
301 296
     pid_t pid;
302 297
 
303 298
     pid = fork();
304 299
 
305
-    if (0 == pid){
300
+    if (0 == pid) {
306 301
         setsid();
307 302
     }
308 303
     return pid;
309
-
310
-#endif
311 304
 }
312 305
 
313
-int daemonize(void) {
314
-#ifdef _WIN32
315
-    fputs("Background mode is not supported on your operating system\n", stderr);
316
-    return -1;
317
-#else
318
-
306
+int daemonize(void)
307
+{
319 308
     int ret = 0;
320 309
 
321 310
     ret = close_std_descriptors();
322
-    if (ret){
311
+    if (ret) {
323 312
         return ret;
324 313
     }
325 314
 
326
-    ret = daemonize_all_return();
327
-    pid_t pid = (pid_t) ret;
315
+    ret       = daemonize_all_return();
316
+    pid_t pid = (pid_t)ret;
328 317
     /*parent process.*/
329
-    if (pid > 0){
318
+    if (pid > 0) {
330 319
         exit(0);
331 320
     }
332 321
 
333 322
     return pid;
334
-#endif
335 323
 }
336 324
 
337
-
338
-static void daemonize_child_initialized_handler(int sig){
325
+static void daemonize_child_initialized_handler(int sig)
326
+{
339 327
     (void)(sig);
340 328
     exit(0);
341 329
 }
342 330
 
343
-int daemonize_parent_wait(){
344
-#ifdef _WIN32
345
-    fputs("Background mode is not supported on your operating system\n", stderr);
346
-    return -1;
347
-#else
348
-
331
+int daemonize_parent_wait()
332
+{
349 333
     int daemonizePid = daemonize_all_return();
350 334
     if (daemonizePid == -1) {
351 335
         return -1;
352
-    } else if (daemonizePid){ //parent
336
+    } else if (daemonizePid) { //parent
353 337
         /* The parent will wait until either the child process
354 338
          * exits, or signals the parent that it's initialization is
355 339
          * complete.  If it exits, it is due to an error condition,
... ...
@@ -363,33 +345,27 @@ int daemonize_parent_wait(){
363 363
         sigemptyset(&(sig.sa_mask));
364 364
         sig.sa_handler = daemonize_child_initialized_handler;
365 365
 
366
-        if (0 != sigaction(SIGINT, &sig, NULL)){
366
+        if (0 != sigaction(SIGINT, &sig, NULL)) {
367 367
             perror("sigaction");
368 368
             return -1;
369 369
         }
370 370
 
371 371
         int exitStatus;
372 372
         wait(&exitStatus);
373
-        if (WIFEXITED(exitStatus)){ //error
373
+        if (WIFEXITED(exitStatus)) { //error
374 374
             exitStatus = WEXITSTATUS(exitStatus);
375 375
             exit(exitStatus);
376 376
         }
377
-
378 377
     }
379 378
     return 0;
380
-#endif
381 379
 }
382 380
 
383
-void daemonize_signal_parent(pid_t parentPid){
384
-#ifdef _WIN32
385
-    fputs("Background mode is not supported on your operating system\n", stderr);
386
-    return -1;
387
-#else
381
+void daemonize_signal_parent(pid_t parentPid)
382
+{
388 383
     close_std_descriptors();
389
-    kill(parentPid, SIGINT) ;
390
-#endif
384
+    kill(parentPid, SIGINT);
391 385
 }
392
-
386
+#endif
393 387
 
394 388
 int match_regex(const char *filename, const char *pattern)
395 389
 {
... ...
@@ -63,24 +63,27 @@ int check_flevel(void);
63 63
 const char *filelist(const struct optstruct *opts, int *err);
64 64
 int filecopy(const char *src, const char *dest);
65 65
 
66
+#ifndef _WIN32
66 67
 /*Returns 0 on success (only the child process returns.*/
67 68
 int daemonize(void);
68 69
 
69 70
 /*closes stdin, stdout, stderr.  This is called by daemonize, but not
70 71
  * daemonize_all_return.  Users of daemonize_all_return should call this
71 72
  * when initialization is complete.*/
72
-int close_std_descriptors() ;
73
+int close_std_descriptors();
74
+
73 75
 /*Returns the return value of fork.  All processes return */
74 76
 int daemonize_all_return(void);
77
+
75 78
 /*Parent waits for a SIGINT or the child process to exit.  If
76 79
  * it receives a SIGINT, it exits with exit code 0.  If the child
77 80
  * exits (error), it exits with the child process's exit code.*/
78 81
 int daemonize_parent_wait();
82
+
79 83
 /*Sends a SIGINT to the parent process.  It also closes stdin, stdout, 
80 84
  * and stderr.*/
81 85
 void daemonize_signal_parent(pid_t parentPid);
82
-
83
-
86
+#endif
84 87
 
85 88
 const char *get_version(void);
86 89
 int match_regex(const char *filename, const char *pattern);