| /* |
| * Copyright (C) 2002 - 2007 Tomasz Kojm <tkojm@clamav.net> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * 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 <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| #include "cfgparser.h" |
| #include "misc.h" |
| |
| #include "str.h" |
| |
| struct cfgoption cfg_options[] = { |
| {"LogFile", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD}, |
| {"LogFileUnlock", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"LogFileMaxSize", OPT_COMPSIZE, 1048576, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM}, |
| {"LogTime", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM}, |
| {"LogClean", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"LogVerbose", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM}, |
| {"LogSyslog", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM}, |
| {"LogFacility", OPT_QUOTESTR, -1, "LOG_LOCAL6", 0, OPT_CLAMD | OPT_FRESHCLAM}, |
| {"PidFile", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM}, |
| {"TemporaryDirectory", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD}, |
| {"ScanPE", OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| {"ScanELF", OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| {"DetectBrokenExecutables", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"ScanMail", OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| {"MailFollowURLs", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"MailMaxRecursion", OPT_NUM, 64, NULL, 0, OPT_CLAMD}, |
| {"PhishingSignatures", OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| {"PhishingScanURLs",OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| /* these are FP prone options, if default isn't used */ |
| {"PhishingAlwaysBlockCloak", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"PhishingAlwaysBlockSSLMismatch", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"PhishingRestrictedScan", OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| /* end of FP prone options */ |
| {"DetectPUA", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"AlgorithmicDetection", OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| {"ScanHTML", OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| {"ScanOLE2", OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| {"ScanPDF", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"ScanArchive", OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| {"ArchiveMaxFileSize", OPT_COMPSIZE, 10485760, NULL, 0, OPT_CLAMD}, |
| {"ArchiveMaxRecursion", OPT_NUM, 8, NULL, 0, OPT_CLAMD}, |
| {"ArchiveMaxFiles", OPT_NUM, 1000, NULL, 0, OPT_CLAMD}, |
| {"ArchiveMaxCompressionRatio", OPT_NUM, 250, NULL, 0, OPT_CLAMD}, |
| {"ArchiveLimitMemoryUsage", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"ArchiveBlockEncrypted", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"ArchiveBlockMax", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"DatabaseDirectory", OPT_QUOTESTR, -1, DATADIR, 0, OPT_CLAMD | OPT_FRESHCLAM}, |
| {"TCPAddr", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD}, |
| {"TCPSocket", OPT_NUM, -1, NULL, 0, OPT_CLAMD}, |
| {"LocalSocket", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD}, |
| {"MaxConnectionQueueLength", OPT_NUM, 15, NULL, 0, OPT_CLAMD}, |
| {"StreamMaxLength", OPT_COMPSIZE, 10485760, NULL, 0, OPT_CLAMD}, |
| {"StreamMinPort", OPT_NUM, 1024, NULL, 0, OPT_CLAMD}, |
| {"StreamMaxPort", OPT_NUM, 2048, NULL, 0, OPT_CLAMD}, |
| {"MaxThreads", OPT_NUM, 10, NULL, 0, OPT_CLAMD}, |
| {"ReadTimeout", OPT_NUM, 120, NULL, 0, OPT_CLAMD}, |
| {"IdleTimeout", OPT_NUM, 30, NULL, 0, OPT_CLAMD}, |
| {"MaxDirectoryRecursion", OPT_NUM, 15, NULL, 0, OPT_CLAMD}, |
| {"FollowDirectorySymlinks", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"FollowFileSymlinks", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"ExitOnOOM", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"Foreground", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM}, |
| {"Debug", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM}, |
| {"LeaveTemporaryFiles", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, |
| {"FixStaleSocket", OPT_BOOL, 1, NULL, 0, OPT_CLAMD}, |
| {"User", OPT_QUOTESTR, -1, NULL, 0, OPT_CLAMD}, |
| {"AllowSupplementaryGroups", OPT_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM}, |
| {"SelfCheck", OPT_NUM, 1800, NULL, 0, OPT_CLAMD}, |
| {"VirusEvent", OPT_FULLSTR, -1, NULL, 0, OPT_CLAMD}, |
| {"ClamukoScanOnAccess", OPT_BOOL, -1, NULL, 0, OPT_CLAMD}, |
| {"ClamukoScanOnOpen", OPT_BOOL, -1, NULL, 0, OPT_CLAMD}, |
| {"ClamukoScanOnClose", OPT_BOOL, -1, NULL, 0, OPT_CLAMD}, |
| {"ClamukoScanOnExec", OPT_BOOL, -1, NULL, 0, OPT_CLAMD}, |
| {"ClamukoIncludePath", OPT_QUOTESTR, -1, NULL, 1, OPT_CLAMD}, |
| {"ClamukoExcludePath", OPT_QUOTESTR, -1, NULL, 1, OPT_CLAMD}, |
| {"ClamukoMaxFileSize", OPT_COMPSIZE, 5242880, NULL, 0, OPT_CLAMD}, |
| {"DatabaseOwner", OPT_QUOTESTR, -1, CLAMAVUSER, 0, OPT_FRESHCLAM}, |
| {"Checks", OPT_NUM, 12, NULL, 0, OPT_FRESHCLAM}, |
| {"UpdateLogFile", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"DNSDatabaseInfo", OPT_QUOTESTR, -1, "current.cvd.clamav.net", 0, OPT_FRESHCLAM}, |
| {"DatabaseMirror", OPT_QUOTESTR, -1, NULL, 1, OPT_FRESHCLAM}, |
| {"MaxAttempts", OPT_NUM, 3, NULL, 0, OPT_FRESHCLAM}, |
| {"ScriptedUpdates", OPT_BOOL, 1, NULL, 0, OPT_FRESHCLAM}, |
| {"HTTPProxyServer", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"HTTPProxyPort", OPT_NUM, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"HTTPProxyUsername", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"HTTPProxyPassword", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"HTTPUserAgent", OPT_FULLSTR, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"NotifyClamd", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"OnUpdateExecute", OPT_FULLSTR, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"OnErrorExecute", OPT_FULLSTR, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"OnOutdatedExecute", OPT_FULLSTR, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"LocalIPAddress", OPT_QUOTESTR, -1, NULL, 0, OPT_FRESHCLAM}, |
| {"ConnectTimeout", OPT_NUM, 30, NULL, 0, OPT_FRESHCLAM}, |
| {"ReceiveTimeout", OPT_NUM, 30, NULL, 0, OPT_FRESHCLAM}, |
| |
| {"DevACOnly", OPT_BOOL, -1, NULL, 0, OPT_CLAMD}, |
| {"DevACDepth", OPT_NUM, -1, NULL, 0, OPT_CLAMD}, |
| |
| {NULL, 0, 0, NULL, 0, 0} |
| }; |
| |
| static int regcfg(struct cfgstruct **copt, const char *optname, char *strarg, int numarg, short multiple); |
| |
| struct cfgstruct *getcfg(const char *cfgfile, int verbose) |
| { |
| char buff[LINE_LENGTH], *name, *arg, *c; |
| FILE *fs; |
| int line = 0, i, found, ctype, calc, val; |
| struct cfgstruct *copt = NULL; |
| struct cfgoption *pt; |
| |
| |
| for(i = 0; ; i++) { |
| pt = &cfg_options[i]; |
| if(!pt->name) |
| break; |
| |
| if(regcfg(&copt, pt->name, pt->strarg ? strdup(pt->strarg) : NULL, pt->numarg, pt->multiple) < 0) { |
| fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); |
| freecfg(copt); |
| return NULL; |
| } |
| } |
| |
| if((fs = fopen(cfgfile, "rb")) == NULL) { |
| /* do not print error message here! */ |
| freecfg(copt); |
| return NULL; |
| } |
| |
| while(fgets(buff, LINE_LENGTH, fs)) { |
| line++; |
| |
| if(buff[0] == '#') |
| continue; |
| |
| if(!strncmp("Example", buff, 7)) { |
| if(verbose) |
| fprintf(stderr, "ERROR: Please edit the example config file %s.\n", cfgfile); |
| fclose(fs); |
| freecfg(copt); |
| return NULL; |
| } |
| |
| if((name = cli_strtok(buff, 0, " \r\n"))) { |
| arg = cli_strtok(buff, 1, " \r\n"); |
| found = 0; |
| for(i = 0; ; i++) { |
| pt = &cfg_options[i]; |
| if(pt->name) { |
| if(!strcmp(name, pt->name)) { |
| found = 1; |
| switch(pt->argtype) { |
| case OPT_STR: |
| /* deprecated. Use OPT_QUOTESTR instead since it behaves like this, but supports quotes to allow values to contain whitespace */ |
| if(!arg) { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name); |
| fclose(fs); |
| free(name); |
| freecfg(copt); |
| return NULL; |
| } |
| if(regcfg(&copt, name, arg, -1, pt->multiple) < 0) { |
| fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| break; |
| case OPT_FULLSTR: |
| /* an ugly hack of the above case */ |
| if(!arg) { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name); |
| fclose(fs); |
| free(name); |
| freecfg(copt); |
| return NULL; |
| } |
| free(arg); |
| arg = strstr(buff, " "); |
| arg = strdup(++arg); |
| if((arg) && (c = strpbrk(arg, "\n\r"))) |
| *c = '\0'; |
| if((!arg) || (regcfg(&copt, name, arg, -1, pt->multiple) < 0)) { |
| fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| break; |
| case OPT_QUOTESTR: |
| /* an ugly hack of the above case */ |
| if(!arg) { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires string argument.\n", line, name); |
| fclose(fs); |
| free(name); |
| freecfg(copt); |
| return NULL; |
| } |
| if((*arg == '\'') || (*arg == '"')) { |
| free(arg); |
| c = strstr(buff, " "); |
| arg = strdup(c+2); |
| if(arg) { |
| if((c = strchr(arg, c[1]))) |
| *c = '\0'; |
| else { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s missing closing quote.\n", line, name); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| } |
| } |
| if((!arg) || (regcfg(&copt, name, arg, -1, pt->multiple) < 0)) { |
| fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| break; |
| case OPT_NUM: |
| if(!arg || !isnumb(arg)) { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical argument.\n", line, name); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| if(regcfg(&copt, name, NULL, atoi(arg), pt->multiple) < 0) { |
| fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| free(arg); |
| break; |
| case OPT_COMPSIZE: |
| if(!arg) { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires argument.\n", line, name); |
| fclose(fs); |
| free(name); |
| freecfg(copt); |
| return NULL; |
| } |
| ctype = tolower(arg[strlen(arg) - 1]); |
| if(ctype == 'm' || ctype == 'k') { |
| char *cpy = (char *) calloc(strlen(arg), 1); |
| strncpy(cpy, arg, strlen(arg) - 1); |
| if(!isnumb(cpy)) { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| if(ctype == 'm') |
| calc = atoi(cpy) * 1024 * 1024; |
| else |
| calc = atoi(cpy) * 1024; |
| free(cpy); |
| } else { |
| if(!isnumb(arg)) { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires numerical (raw/K/M) argument.\n", line, name); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| calc = atoi(arg); |
| } |
| free(arg); |
| if(regcfg(&copt, name, NULL, calc, pt->multiple) < 0) { |
| fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| break; |
| case OPT_BOOL: |
| |
| if(!arg) { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires boolean argument.\n", line, name); |
| fclose(fs); |
| free(name); |
| freecfg(copt); |
| return NULL; |
| } |
| |
| if(!strcasecmp(arg, "yes") || !strcmp(arg, "1") || !strcasecmp(arg, "true")) { |
| val = 1; |
| } else if(!strcasecmp(arg, "no") || !strcmp(arg, "0") || !strcasecmp(arg, "false")) { |
| val = 0; |
| } else { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s requires boolean argument.\n", line, name); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| free(arg); |
| if(regcfg(&copt, name, NULL, val, pt->multiple) < 0) { |
| fprintf(stderr, "ERROR: Can't register new options (not enough memory)\n"); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| break; |
| default: |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Option %s is of unknown type %d\n", line, name, pt->argtype); |
| fclose(fs); |
| free(name); |
| free(arg); |
| freecfg(copt); |
| return NULL; |
| } |
| } |
| } else |
| break; |
| } |
| |
| if(!found) { |
| if(verbose) |
| fprintf(stderr, "ERROR: Parse error at line %d: Unknown option %s.\n", line, name); |
| free(name); |
| fclose(fs); |
| freecfg(copt); |
| return NULL; |
| } |
| free(name); |
| } |
| } |
| |
| fclose(fs); |
| return copt; |
| } |
| |
| void freecfg(struct cfgstruct *copt) |
| { |
| struct cfgstruct *handler; |
| struct cfgstruct *arg; |
| |
| while(copt) { |
| arg = copt->nextarg; |
| while(arg) { |
| if(arg->strarg) { |
| free(arg->optname); |
| free(arg->strarg); |
| handler = arg; |
| arg = arg->nextarg; |
| free(handler); |
| } else |
| arg = arg->nextarg; |
| } |
| if(copt->optname) |
| free(copt->optname); |
| |
| if(copt->strarg) |
| free(copt->strarg); |
| |
| handler = copt; |
| copt = copt->next; |
| free(handler); |
| } |
| return; |
| } |
| |
| const struct cfgstruct *cfgopt(const struct cfgstruct *copt, const char *optname) |
| { |
| while(copt) { |
| if(copt->optname && !strcmp(copt->optname, optname)) |
| return copt; |
| |
| copt = copt->next; |
| } |
| |
| return NULL; |
| } |
| |
| static struct cfgstruct *cfgopt_i(struct cfgstruct *copt, const char *optname) |
| { |
| while(copt) { |
| if(copt->optname && !strcmp(copt->optname, optname)) |
| return copt; |
| |
| copt = copt->next; |
| } |
| |
| return NULL; |
| } |
| |
| static int regcfg(struct cfgstruct **copt, const char *optname, char *strarg, int numarg, short multiple) |
| { |
| struct cfgstruct *newnode, *pt; |
| |
| |
| newnode = (struct cfgstruct *) malloc(sizeof(struct cfgstruct)); |
| |
| if(!newnode) |
| return -1; |
| |
| newnode->optname = optname ? strdup(optname) : NULL; |
| newnode->nextarg = NULL; |
| newnode->next = NULL; |
| newnode->enabled = 0; |
| newnode->multiple = multiple; |
| |
| if(strarg) { |
| newnode->strarg = strarg; |
| newnode->enabled = 1; |
| } else { |
| newnode->strarg = NULL; |
| } |
| |
| newnode->numarg = numarg; |
| if(numarg != -1 && numarg != 0) |
| newnode->enabled = 1; |
| |
| if((pt = cfgopt_i(*copt, optname))) { |
| if(pt->multiple) { |
| |
| if(pt->enabled) { |
| while(pt->nextarg) |
| pt = pt->nextarg; |
| |
| pt->nextarg = newnode; |
| } else { |
| if(pt->strarg) |
| free(pt->strarg); |
| pt->strarg = newnode->strarg; |
| pt->numarg = newnode->numarg; |
| pt->enabled = newnode->enabled; |
| if(newnode->optname) |
| free(newnode->optname); |
| free(newnode); |
| } |
| return 3; /* registered additional argument */ |
| |
| } else { |
| if(pt->strarg) |
| free(pt->strarg); |
| pt->strarg = newnode->strarg; |
| pt->numarg = newnode->numarg; |
| pt->enabled = newnode->enabled; |
| if(newnode->optname) |
| free(newnode->optname); |
| free(newnode); |
| return 2; |
| } |
| |
| } else { |
| newnode->next = *copt; |
| *copt = newnode; |
| return 1; |
| } |
| } |
| |