/* * Copyright (C) 2002 - 2007 Tomasz Kojm * * 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 #include #include #include #include "shared/cfgparser.h" #include "shared/misc.h" #include "libclamav/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}, {"LogTime", OPT_BOOL, 0, NULL, 0, OPT_CLAMD}, {"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 */ {"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, 0, 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}, {"NodalCoreAcceleration", OPT_BOOL, 0, 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}, {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; } }