clamonacc/misc/utils.c
51b69a09
 /*
90968035
  *  Copyright (C) 2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
51b69a09
  *
  *  Authors: Mickey Sola
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
  */
 
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <errno.h>
 #include <pthread.h>
9707132c
 #include <pwd.h>
56426d28
 #include "libclamav/clamav.h"
 #include "shared/optparser.h"
 #include "shared/output.h"
51b69a09
 
56426d28
 #include "utils.h"
 #include "clamd/scanner.h"
f6947528
 #include "../clamonacc.h"
ad49ff53
 #include "../client/client.h"
 #include "../scan/queue.h"
51b69a09
 
fe7a6972
 #if defined(FANOTIFY)
 
ad49ff53
 extern pthread_cond_t onas_scan_queue_empty_cond;
f972802f
 
51b69a09
 int onas_fan_checkowner(int pid, const struct optstruct *opts)
 {
9707132c
     struct passwd *pwd;
51b69a09
     char path[32];
     STATBUF sb;
43422579
     const struct optstruct *opt       = NULL;
     const struct optstruct *opt_root  = NULL;
9707132c
     const struct optstruct *opt_uname = NULL;
43422579
     int retry                         = 0;
51b69a09
 
     /* always ignore ourselves */
288057e9
     if (pid == (int)getpid()) {
b71960cd
         return CHK_SELF;
51b69a09
     }
 
     /* look up options */
43422579
     opt       = optget(opts, "OnAccessExcludeUID");
     opt_root  = optget(opts, "OnAccessExcludeRootUID");
     opt_uname = optget(opts, "OnAccessExcludeUname");
51b69a09
 
     /* we can return immediately if no uid exclusions were requested */
9707132c
     if (!(opt->enabled || opt_root->enabled || opt_uname->enabled))
b71960cd
         return CHK_CLEAN;
51b69a09
 
     /* perform exclusion checks if we can stat OK */
288057e9
     snprintf(path, sizeof(path), "/proc/%u", pid);
     if (CLAMSTAT(path, &sb) == 0) {
51b69a09
         /* check all our non-root UIDs first */
         if (opt->enabled) {
288057e9
             while (opt) {
                 if (opt->numarg == (long long)sb.st_uid)
b71960cd
                     return CHK_FOUND;
51b69a09
                 opt = opt->nextarg;
             }
         }
9707132c
         /* then check our unames */
         if (opt_uname->enabled) {
43422579
             while (opt_uname) {
f972802f
                 errno = 0;
43422579
                 pwd   = getpwuid(sb.st_uid);
                 if (NULL == pwd) {
                     if (errno) {
                         logg("*ClamMisc: internal error (failed to exclude event) ... %s\n", strerror(errno));
                         switch (errno) {
                             case EIO:
                                 logg("*ClamMisc: system i/o failed while retrieving username information (excluding for safety)\n");
                                 return CHK_FOUND;
                                 break;
                             case EINTR:
                                 logg("*ClamMisc: caught signal while retrieving username information from system (excluding for safety)\n");
                                 return CHK_FOUND;
                                 break;
                             case EMFILE:
                             case ENFILE:
                                 if (0 == retry) {
                                     logg("*ClamMisc: waiting for consumer thread to catch up then retrying ...\n");
                                     sleep(3);
                                     retry = 1;
                                     continue;
                                 } else {
                                     logg("*ClamMisc: fds have been exhausted ... attempting to force the consumer thread to catch up ... (excluding for safety)\n");
                                     pthread_cond_signal(&onas_scan_queue_empty_cond);
                                     sleep(3);
                                     return CHK_FOUND;
                                 }
                             case ERANGE:
                             default:
                                 logg("*ClamMisc: unknown error occurred (excluding for safety)\n");
                                 return CHK_FOUND;
                                 break;
                         }
                     }
                 } else {
                     if (!strncmp(opt_uname->strarg, pwd->pw_name, strlen(opt_uname->strarg))) {
                         return CHK_FOUND;
                     }
                 }
9707132c
                 opt_uname = opt_uname->nextarg;
             }
         }
51b69a09
         /* finally check root UID */
         if (opt_root->enabled) {
288057e9
             if (0 == (long long)sb.st_uid)
b71960cd
                 return CHK_FOUND;
51b69a09
         }
     } else if (errno == EACCES) {
9707132c
         logg("*ClamMisc: permission denied to stat /proc/%d to exclude UIDs... perhaps SELinux denial?\n", pid);
51b69a09
     } else if (errno == ENOENT) {
90968035
         /* TODO: should this be configurable? */
9707132c
         logg("ClamMisc: $/proc/%d vanished before UIDs could be excluded; scanning anyway\n", pid);
51b69a09
     }
 
b71960cd
     return CHK_CLEAN;
51b69a09
 }
fe7a6972
 
f6947528
 #endif
51b69a09
 
f6947528
 char **onas_get_opt_list(const char *fname, int *num_entries, cl_error_t *err)
 {
b71960cd
 
43422579
     FILE *opt_file = 0;
     STATBUF sb;
     char **opt_list = NULL;
     char **rlc_ptr  = NULL;
     uint64_t len    = 0;
     int32_t ret     = 0;
 
     *num_entries = 0;
 
     opt_list = cli_malloc(sizeof(char *));
     if (NULL == opt_list) {
         *err = CL_EMEM;
         return NULL;
     }
     opt_list[*num_entries] = NULL;
 
     errno    = 0;
     opt_file = fopen(fname, "r");
 
     if (NULL == opt_file) {
         logg("!ClamMisc: could not open path list file `%s', %s\n", fname, errno ? strerror(errno) : "");
         *err = CL_EARG;
         return NULL;
     }
 
     while ((ret = getline(opt_list + *num_entries, &len, opt_file)) != -1) {
 
         opt_list[*num_entries][strlen(opt_list[*num_entries]) - 1] = '\0';
         errno                                                      = 0;
         if (0 != CLAMSTAT(opt_list[*num_entries], &sb)) {
             logg("*ClamMisc: when parsing path list ... could not stat '%s' ... %s ... skipping\n", opt_list[*num_entries], strerror(errno));
             len = 0;
             free(opt_list[*num_entries]);
             opt_list[*num_entries] = NULL;
             continue;
         }
 
         if (!S_ISDIR(sb.st_mode)) {
             logg("*ClamMisc: when parsing path list ... '%s' is not a directory ... skipping\n", opt_list[*num_entries]);
             len = 0;
             free(opt_list[*num_entries]);
             opt_list[*num_entries] = NULL;
             continue;
         }
 
         if (strcmp(opt_list[*num_entries], "/") == 0) {
             logg("*ClamMisc: when parsing path list ... ignoring path '%s' while DDD is enabled ... skipping\n", opt_list[*num_entries]);
             logg("*ClamMisc: use the OnAccessMountPath configuration option to watch '%s'\n", opt_list[*num_entries]);
             len = 0;
             free(opt_list[*num_entries]);
             opt_list[*num_entries] = NULL;
             continue;
         }
 
         (*num_entries)++;
         rlc_ptr = cli_realloc(opt_list, sizeof(char *) * (*num_entries + 1));
         if (rlc_ptr) {
             opt_list               = rlc_ptr;
             opt_list[*num_entries] = NULL;
         } else {
             *err = CL_EMEM;
             fclose(opt_file);
             free_opt_list(opt_list, *num_entries);
             return NULL;
         }
 
         len = 0;
     }
 
     opt_list[*num_entries] = NULL;
     fclose(opt_file);
     return opt_list;
f6947528
 }
 
43422579
 void free_opt_list(char **opt_list, int entries)
f6947528
 {
 
43422579
     int i = 0;
     for (i; i < entries; i++) {
         if (opt_list[i]) {
             free(opt_list[i]);
             opt_list[i] = NULL;
         }
     }
f6947528
 
43422579
     free(opt_list);
     opt_list = NULL;
f6947528
 
43422579
     return;
51b69a09
 }