Example #1
0
static void milter_exit(int sig) {
    const struct optstruct *opt;

    logg("*clamav-milter: milter_exit, signal %d\n", sig);

#ifndef _WIN32
    if((opt = optget(opts, "MilterSocket"))) {
        if(unlink(opt->strarg) == -1)
            logg("!Can't unlink the socket file %s\n", opt->strarg);
        else
            logg("Socket file removed.\n");
    }
#endif

    logg("clamav-milter: stopped\n");

    optfree(opts);

    logg_close();
    cpool_free();
    localnets_free();
    whitelist_free();
}
Example #2
0
int main(int argc, char **argv)
{
	int ds, dms, ret;
	double mb, rmb;
	struct timeval t1, t2;
#ifndef _WIN32
	sigset_t sigset;
#endif
	struct optstruct *opts;
	const struct optstruct *opt;

    if(check_flevel())
	exit(2);

#if !defined(_WIN32) && !defined(C_BEOS)
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGXFSZ);
    sigprocmask(SIG_SETMASK, &sigset, NULL);
#endif

    cl_initialize_crypto();


    if((opts = optparse(NULL, argc, argv, 1, OPT_CLAMSCAN, 0, NULL)) == NULL) {
	mprintf("!Can't parse command line options\n");
	return 2;
    }

    if(optget(opts, "verbose")->enabled) {
	mprintf_verbose = 1;
	logg_verbose = 1;
    }

    if(optget(opts, "quiet")->enabled)
	mprintf_quiet = 1;

    if(optget(opts, "stdout")->enabled)
	mprintf_stdout = 1;


    if(optget(opts, "debug")->enabled) {
#if defined(C_LINUX)
	    /* [email protected]: create a dump if needed */
	    struct rlimit rlim;

	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
	if(setrlimit(RLIMIT_CORE, &rlim) < 0)
	    perror("setrlimit");
#endif
	cl_debug(); /* enable debug messages */
    }

    if (optget(opts, "gen-mdb")->enabled) {
        cl_always_gen_section_hash();
    }

    if(optget(opts, "version")->enabled) {
	print_version(optget(opts, "database")->strarg);
	optfree(opts);
	return 0;
    }

    if(optget(opts, "help")->enabled) {
	optfree(opts);
    	help();
	return 0;
    }

    if(optget(opts, "recursive")->enabled)
	recursion = 1;

    if(optget(opts, "infected")->enabled)
	printinfected = 1;

    if(optget(opts, "suppress-ok-results")->enabled)
	printclean = 0;

    if(optget(opts, "bell")->enabled)
	bell = 1;

    /* initialize logger */
    if((opt = optget(opts, "log"))->enabled) {
	logg_file = opt->strarg;
	if(logg("#\n-------------------------------------------------------------------------------\n\n")) {
	    mprintf("!Problem with internal logger.\n");
	    optfree(opts);
	    return 2;
	}
    } else 
	logg_file = NULL;

    if(actsetup(opts)) {
	optfree(opts);
	logg_close();
	exit(2);
    }

    memset(&info, 0, sizeof(struct s_info));

    gettimeofday(&t1, NULL);

    ret = scanmanager(opts);

    if(!optget(opts, "no-summary")->enabled) {
	gettimeofday(&t2, NULL);

    ds = t2.tv_sec - t1.tv_sec;
	dms = t2.tv_usec - t1.tv_usec;
	ds -= (dms < 0) ? (1):(0);
	dms += (dms < 0) ? (1000000):(0);
	logg("\n----------- SCAN SUMMARY -----------\n");
	logg("Known viruses: %u\n", info.sigs);
	logg("Engine version: %s\n", get_version());
	logg("Scanned directories: %u\n", info.dirs);
	logg("Scanned files: %u\n", info.files);
	logg("Infected files: %u\n", info.ifiles);
	if(info.errors)
	    logg("Total errors: %u\n", info.errors);
	if(notremoved) {
	    logg("Not removed: %u\n", notremoved);
	}
	if(notmoved) {
	    logg("Not %s: %u\n", optget(opts, "copy")->enabled ? "moved" : "copied", notmoved);
	}
	mb = info.blocks * (CL_COUNT_PRECISION / 1024) / 1024.0;
	logg("Data scanned: %2.2lf MB\n", mb);
	rmb = info.rblocks * (CL_COUNT_PRECISION / 1024) / 1024.0;
	logg("Data read: %2.2lf MB (ratio %.2f:1)\n", rmb, info.rblocks ? (double)info.blocks/(double)info.rblocks : 0);
	logg("Time: %u.%3.3u sec (%u m %u s)\n", ds, dms/1000, ds/60, ds%60);
    }

    optfree(opts);

    cl_cleanup_crypto();

    return ret;
}
Example #3
0
int main(int argc, char **argv) {
    char *my_socket, *pt;
    const struct optstruct *opt;
    struct optstruct *opts;
    time_t currtime;
    mode_t umsk;
    int ret;

    cl_initialize_crypto();

    memset(&descr, 0, sizeof(struct smfiDesc));
    descr.xxfi_name = "ClamAV";			/* filter name */
    descr.xxfi_version = SMFI_VERSION;		/* milter version */
    descr.xxfi_flags = SMFIF_QUARANTINE;	/* flags */
    descr.xxfi_connect = clamfi_connect;	/* connection info filter */
    descr.xxfi_envfrom = clamfi_envfrom;	/* envelope sender filter */
    descr.xxfi_envrcpt = clamfi_envrcpt;	/* envelope recipient filter */
    descr.xxfi_header = clamfi_header;		/* header filter */
    descr.xxfi_body = clamfi_body;		/* body block */
    descr.xxfi_eom = clamfi_eom;		/* end of message */
    descr.xxfi_abort = clamfi_abort;		/* message aborted */

    opts = optparse(NULL, argc, argv, 1, OPT_MILTER, 0, NULL);
    if (!opts) {
	mprintf("!Can't parse command line options\n");
	return 1;
    }

    if(optget(opts, "help")->enabled) {
	printf("Usage: %s [-c <config-file>]\n\n", argv[0]);
	printf("    --help                   -h       Show this help\n");
	printf("    --version                -V       Show version and exit\n");
	printf("    --config-file <file>     -c       Read configuration from file\n\n");
	optfree(opts);
	return 0;
    }

    if(opts->filename) {
	int x;
	for(x = 0; opts->filename[x]; x++)
	    mprintf("^Ignoring option %s\n", opts->filename[x]);
    }

    if(optget(opts, "version")->enabled) {
	printf("clamav-milter %s\n", get_version());
	optfree(opts);
	return 0;
    }

    pt = strdup(optget(opts, "config-file")->strarg);
    if (pt == NULL) {
	printf("Unable to allocate memory for config file\n");
	return 1;
    }
    if((opts = optparse(pt, 0, NULL, 1, OPT_MILTER, 0, opts)) == NULL) {
	printf("%s: cannot parse config file %s\n", argv[0], pt);
	free(pt);
	return 1;
    }
    free(pt);

    if((opt = optget(opts, "Chroot"))->enabled) {
	if(chdir(opt->strarg) != 0) {
	    logg("!Cannot change directory to %s\n", opt->strarg);
	    return 1;
	}
	if(chroot(opt->strarg) != 0) {
	    logg("!chroot to %s failed. Are you root?\n", opt->strarg);
	    return 1;
	}
    }

    pt = optget(opts, "AddHeader")->strarg;
    if (strcasecmp(pt, "No")) {
	char myname[255];

	if (((opt = optget(opts, "ReportHostname"))->enabled &&
	     strncpy(myname, opt->strarg, sizeof(myname))) ||
	    !gethostname(myname, sizeof(myname))) {

	    myname[sizeof(myname)-1] = '\0';
	    snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s at %s",
		     get_version(), myname);
	} else {
	    snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s",
		     get_version());
	}
	xvirushdr[sizeof(xvirushdr)-1] = '\0';

	descr.xxfi_flags |= SMFIF_ADDHDRS;

	if (strcasecmp(pt, "Add")) { /* Replace or Yes */
	    descr.xxfi_flags |= SMFIF_CHGHDRS;
	    addxvirus = 1;
	} else { /* Add */
	    addxvirus = 2;
	}
    }

    if(!(my_socket = optget(opts, "MilterSocket")->strarg)) {
	logg("!Please configure the MilterSocket directive\n");
	logg_close();
	optfree(opts);
	return 1;
    }

    if(smfi_setconn(my_socket) == MI_FAILURE) {
	logg("!smfi_setconn failed\n");
	logg_close();
	optfree(opts);
	return 1;
    }
    if(smfi_register(descr) == MI_FAILURE) {
	logg("!smfi_register failed\n");
	logg_close();
	optfree(opts);
	return 1;
    }
    opt = optget(opts, "FixStaleSocket");
    umsk = umask(0777); /* socket is created with 000 to avoid races */
    if(smfi_opensocket(opt->enabled) == MI_FAILURE) {
	logg("!Failed to create socket %s\n", my_socket);
	logg_close();
	optfree(opts);
	return 1;
    }
    umask(umsk); /* restore umask */
    if(strncmp(my_socket, "inet:", 5) && strncmp(my_socket, "inet6:", 6)) {
	/* set group ownership and perms on the local socket */
	char *sock_name = my_socket;
	mode_t sock_mode;
	if(!strncmp(my_socket, "unix:", 5))
	    sock_name += 5;
	if(!strncmp(my_socket, "local:", 6))
	    sock_name += 6;
	if(*my_socket == ':')
	    sock_name ++;

	if(optget(opts, "MilterSocketGroup")->enabled) {
	    char *gname = optget(opts, "MilterSocketGroup")->strarg, *end;
	    gid_t sock_gid = strtol(gname, &end, 10);
	    if(*end) {
		struct group *pgrp = getgrnam(gname);
		if(!pgrp) {
		    logg("!Unknown group %s\n", gname);
		    logg_close();
		    optfree(opts);
		    return 1;
		}
		sock_gid = pgrp->gr_gid;
	    }
	    if(chown(sock_name, -1, sock_gid)) {
		logg("!Failed to change socket ownership to group %s\n", gname);
		logg_close();
		optfree(opts);
		return 1;
	    }
	}

	if ((opt = optget(opts, "User"))->enabled) {
	    struct passwd *user;
	    if ((user = getpwnam(opt->strarg)) == NULL) {
		logg("ERROR: Can't get information about user %s.\n",
			opt->strarg);
		logg_close();
		optfree(opts);
		return 1;
	    }

	    if(chown(sock_name, user->pw_uid, -1)) {
		logg("!Failed to change socket ownership to user %s\n", user->pw_name);
		optfree(opts);
		logg_close();
		return 1;
	    }
	}

	if(optget(opts, "MilterSocketMode")->enabled) {
	    char *end;
	    sock_mode = strtol(optget(opts, "MilterSocketMode")->strarg, &end, 8);
	    if(*end) {
		logg("!Invalid MilterSocketMode %s\n", optget(opts, "MilterSocketMode")->strarg);
		logg_close();
		optfree(opts);
		return 1;
	    }
	} else
	    sock_mode = 0777 & ~umsk;

	if(chmod(sock_name, sock_mode & 0666)) {
	    logg("!Cannot set milter socket permission to %s\n", optget(opts, "MilterSocketMode")->strarg);
	    logg_close();
	    optfree(opts);
	    return 1;
	}
    }

    if(geteuid() == 0 && (opt = optget(opts, "User"))->enabled) {
        struct passwd *user = NULL;
	if((user = getpwnam(opt->strarg)) == NULL) {
	    fprintf(stderr, "ERROR: Can't get information about user %s.\n", opt->strarg);
	    optfree(opts);
	    return 1;
	}

	if(optget(opts, "AllowSupplementaryGroups")->enabled) {
#ifdef HAVE_INITGROUPS
	    if(initgroups(opt->strarg, user->pw_gid)) {
		fprintf(stderr, "ERROR: initgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#else
	    mprintf("!AllowSupplementaryGroups: initgroups() is not available, please disable AllowSupplementaryGroups\n");
	    optfree(opts);
	    return 1;
#endif
	} else {
#ifdef HAVE_SETGROUPS
	    if(setgroups(1, &user->pw_gid)) {
		fprintf(stderr, "ERROR: setgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#endif
	}

	if(setgid(user->pw_gid)) {
	    fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
	    optfree(opts);
	    return 1;
	}

	if(setuid(user->pw_uid)) {
	    fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
	    optfree(opts);
	    return 1;
	}
    }

    logg_lock = !optget(opts, "LogFileUnlock")->enabled;
    logg_time = optget(opts, "LogTime")->enabled;
    logg_size = optget(opts, "LogFileMaxSize")->numarg;
    logg_verbose = mprintf_verbose = optget(opts, "LogVerbose")->enabled;
    if (logg_size)
        logg_rotate = optget(opts, "LogRotate")->enabled;

    if((opt = optget(opts, "LogFile"))->enabled) {
	logg_file = opt->strarg;
	if(!cli_is_abspath(logg_file)) {
	    fprintf(stderr, "ERROR: LogFile requires full path.\n");
	    logg_close();
	    optfree(opts);
	    return 1;
	}
    } else
	logg_file = NULL;

#if defined(USE_SYSLOG) && !defined(C_AIX)
    if(optget(opts, "LogSyslog")->enabled) {
	int fac;

	opt = optget(opts, "LogFacility");
	if((fac = logg_facility(opt->strarg)) == -1) {
	    logg("!LogFacility: %s: No such facility.\n", opt->strarg);
	    logg_close();
	    optfree(opts);
	    return 1;
	}

	openlog("clamav-milter", LOG_PID, fac);
	logg_syslog = 1;
    }
#endif

    time(&currtime);
    if(logg("#+++ Started at %s", ctime(&currtime))) {
	fprintf(stderr, "ERROR: Can't initialize the internal logger\n");
	logg_close();
	optfree(opts);
	return 1;
    }
    if((opt = optget(opts, "TemporaryDirectory"))->enabled)
	tempdir = opt->strarg;

    if(localnets_init(opts) || init_actions(opts)) {
	logg_close();
	optfree(opts);
	return 1;
    }

    if((opt = optget(opts, "Whitelist"))->enabled && whitelist_init(opt->strarg)) {
	localnets_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    if((opt = optget(opts, "SkipAuthenticated"))->enabled && smtpauth_init(opt->strarg)) {
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    multircpt = optget(opts, "SupportMultipleRecipients")->enabled;
    
    if(!optget(opts, "Foreground")->enabled) {
	if(daemonize() == -1) {
	    logg("!daemonize() failed\n");
	    localnets_free();
	    whitelist_free();
	    cpool_free();
	    logg_close();
	    optfree(opts);
	    return 1;
	}
	if(chdir("/") == -1)
	    logg("^Can't change current working directory to root\n");
    }

    maxfilesize = optget(opts, "MaxFileSize")->numarg;
    if(!maxfilesize) {
	logg("^Invalid MaxFileSize, using default (%d)\n", CLI_DEFAULT_MAXFILESIZE);
	maxfilesize = CLI_DEFAULT_MAXFILESIZE;
    }
    readtimeout = optget(opts, "ReadTimeout")->numarg;

    cpool_init(opts);
    if (!cp) {
	logg("!Failed to init the socket pool\n");
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }	

    if((opt = optget(opts, "PidFile"))->enabled) {
	FILE *fd;
	mode_t old_umask = umask(0002);

	if((fd = fopen(opt->strarg, "w")) == NULL) {
	    logg("!Can't save PID in file %s\n", opt->strarg);
	} else {
	    if (fprintf(fd, "%u\n", (unsigned int)getpid())<0) {
	    	logg("!Can't save PID in file %s\n", opt->strarg);
	    }
	    fclose(fd);
	}
	umask(old_umask);
    }

    ret = smfi_main();

    optfree(opts);

    logg_close();
    cpool_free();
    localnets_free();
    whitelist_free();

    return ret;
}
Example #4
0
int main(int argc, char **argv)
{
	int ds, dms, ret, infected = 0, err = 0;
	struct timeval t1, t2;
	time_t starttime;
        struct optstruct *opts;
        const struct optstruct *opt;
#ifndef _WIN32
	struct sigaction sigact;
#endif

    if((opts = optparse(NULL, argc, argv, 1, OPT_CLAMDSCAN, OPT_CLAMSCAN, NULL)) == NULL) {
	mprintf("!Can't parse command line options\n");
	return 2;
    }

    if((clamdopts = optparse(optget(opts, "config-file")->strarg, 0, NULL, 1, OPT_CLAMD, 0, NULL)) == NULL) {
	logg("!Can't parse clamd configuration file %s\n", optget(opts, "config-file")->strarg);
	return 2;
    }

    if(optget(opts, "verbose")->enabled) {
	mprintf_verbose = 1;
	logg_verbose = 1;
    }

    if(optget(opts, "quiet")->enabled)
	mprintf_quiet = 1;

    if(optget(opts, "stdout")->enabled)
	mprintf_stdout = 1;

    if(optget(opts, "version")->enabled) {
	print_server_version(opts);
	optfree(opts);
	optfree(clamdopts);
	exit(0);
    }

    if(optget(opts, "help")->enabled) {
	optfree(opts);
	optfree(clamdopts);
    	help();
    }

    if(optget(opts, "infected")->enabled)
	printinfected = 1;

    /* initialize logger */

    if((opt = optget(opts, "log"))->enabled) {
	logg_file = opt->strarg;
	if(logg("--------------------------------------\n")) {
	    mprintf("!Problem with internal logger.\n");
	    optfree(opts);
	    optfree(clamdopts);
	    exit(2);
	}
    } else 
	logg_file = NULL;


    if(optget(opts, "reload")->enabled) {
	ret = reload_clamd_database(opts);
	optfree(opts);
	optfree(clamdopts);
	logg_close();
	exit(ret);
    }

    if(actsetup(opts)) {
	optfree(opts);
	optfree(clamdopts);
	logg_close();
	exit(2);
    }

#ifndef _WIN32
    memset(&sigact, 0, sizeof(struct sigaction));
    sigact.sa_handler = SIG_IGN;
    sigemptyset(&sigact.sa_mask);
    sigaddset(&sigact.sa_mask, SIGPIPE);
    sigaction(SIGPIPE, &sigact, NULL);
#endif

    time(&starttime);
    /* ctime() does \n, but I need it once more */

    gettimeofday(&t1, NULL);

    ret = client(opts, &infected, &err);
    optfree(clamdopts);

    /* TODO: Implement STATUS in clamd */
    if(!optget(opts, "no-summary")->enabled) {
	gettimeofday(&t2, NULL);
	ds = t2.tv_sec - t1.tv_sec;
	dms = t2.tv_usec - t1.tv_usec;
	ds -= (dms < 0) ? (1):(0);
	dms += (dms < 0) ? (1000000):(0);
	logg("\n----------- SCAN SUMMARY -----------\n");
	logg("Infected files: %d\n", infected);
	if(err)
	    logg("Total errors: %d\n", err);
	if(notremoved) {
	    logg("Not removed: %d\n", notremoved);
	}
	if(notmoved) {
	    logg("Not moved: %d\n", notmoved);
	}
	logg("Time: %d.%3.3d sec (%d m %d s)\n", ds, dms/1000, ds/60, ds%60);
    }

    logg_close();
    optfree(opts);
    cl_cleanup_crypto();
    exit(ret);
}
Example #5
0
int main(int argc, char **argv)
{
        static struct cl_engine *engine = NULL;
	const struct optstruct *opt;
#ifndef	_WIN32
        struct passwd *user = NULL;
	struct sigaction sa;
	struct rlimit rlim;
#endif
	time_t currtime;
	const char *dbdir, *cfgfile;
	char *pua_cats = NULL, *pt;
	int ret, tcpsock = 0, localsock = 0, i, min_port, max_port;
	unsigned int sigs = 0;
	int lsockets[2], nlsockets = 0;
	unsigned int dboptions = 0;
#ifdef C_LINUX
	STATBUF sb;
#endif

    if(check_flevel())
	exit(1);

#ifndef _WIN32
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_IGN;
    sigaction(SIGHUP, &sa, NULL);
    sigaction(SIGUSR2, &sa, NULL);
#endif

    if((opts = optparse(NULL, argc, argv, 1, OPT_CLAMD, 0, NULL)) == NULL) {
	mprintf("!Can't parse command line options\n");
	return 1;
    }

    if(optget(opts, "help")->enabled) {
    	help();
	optfree(opts);
	return 0;
    }

    if(optget(opts, "debug")->enabled) {
#if defined(C_LINUX)
	/* [email protected]: create a dump if needed */
	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
	if(setrlimit(RLIMIT_CORE, &rlim) < 0)
	    perror("setrlimit");
#endif
	debug_mode = 1;
    }

    /* parse the config file */
    cfgfile = optget(opts, "config-file")->strarg;
    pt = strdup(cfgfile);
    if((opts = optparse(cfgfile, 0, NULL, 1, OPT_CLAMD, 0, opts)) == NULL) {
	fprintf(stderr, "ERROR: Can't open/parse the config file %s\n", pt);
	free(pt);
	return 1;
    }
    free(pt);

    if(optget(opts, "version")->enabled) {
	print_version(optget(opts, "DatabaseDirectory")->strarg);
	optfree(opts);
	return 0;
    }

    /* drop privileges */
#ifndef _WIN32
    if(geteuid() == 0 && (opt = optget(opts, "User"))->enabled) {
	if((user = getpwnam(opt->strarg)) == NULL) {
	    fprintf(stderr, "ERROR: Can't get information about user %s.\n", opt->strarg);
	    optfree(opts);
	    return 1;
	}

	if(optget(opts, "AllowSupplementaryGroups")->enabled) {
#ifdef HAVE_INITGROUPS
	    if(initgroups(opt->strarg, user->pw_gid)) {
		fprintf(stderr, "ERROR: initgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#else
	    mprintf("!AllowSupplementaryGroups: initgroups() is not available, please disable AllowSupplementaryGroups in %s\n", cfgfile);
	    optfree(opts);
	    return 1;
#endif
	} else {
#ifdef HAVE_SETGROUPS
	    if(setgroups(1, &user->pw_gid)) {
		fprintf(stderr, "ERROR: setgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#endif
	}

	if(setgid(user->pw_gid)) {
	    fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
	    optfree(opts);
	    return 1;
	}

	if(setuid(user->pw_uid)) {
	    fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
	    optfree(opts);
	    return 1;
	}
    }
#endif

    /* initialize logger */
    logg_lock = !optget(opts, "LogFileUnlock")->enabled;
    logg_time = optget(opts, "LogTime")->enabled;
    logok = optget(opts, "LogClean")->enabled;
    logg_size = optget(opts, "LogFileMaxSize")->numarg;
    logg_verbose = mprintf_verbose = optget(opts, "LogVerbose")->enabled;
    if (logg_size)
        logg_rotate = optget(opts, "LogRotate")->enabled;
    mprintf_send_timeout = optget(opts, "SendBufTimeout")->numarg;

    do { /* logger initialized */

    if((opt = optget(opts, "LogFile"))->enabled) {
	char timestr[32];
	logg_file = opt->strarg;
	if(!cli_is_abspath(logg_file)) {
	    fprintf(stderr, "ERROR: LogFile requires full path.\n");
	    ret = 1;
	    break;
	}
	time(&currtime);
	if(logg("#+++ Started at %s", cli_ctime(&currtime, timestr, sizeof(timestr)))) {
	    fprintf(stderr, "ERROR: Can't initialize the internal logger\n");
	    ret = 1;
	    break;
	}
    } else
	logg_file = NULL;

    if (optget(opts,"DevLiblog")->enabled)
	cl_set_clcb_msg(msg_callback);
    if((ret = cl_init(CL_INIT_DEFAULT))) {
	logg("!Can't initialize libclamav: %s\n", cl_strerror(ret));
	ret = 1;
	break;
    }

    if(optget(opts, "Debug")->enabled) /* enable debug messages in libclamav */ {
	cl_debug();
	logg_verbose = 2;
    }

#if defined(USE_SYSLOG) && !defined(C_AIX)
    if(optget(opts, "LogSyslog")->enabled) {
	    int fac = LOG_LOCAL6;

	opt = optget(opts, "LogFacility");
	if((fac = logg_facility(opt->strarg)) == -1) {
	    logg("!LogFacility: %s: No such facility.\n", opt->strarg);
	    ret = 1;
	    break;
	}

	openlog("clamd", LOG_PID, fac);
	logg_syslog = 1;
    }
#endif

#ifdef C_LINUX
    procdev = 0;
    if(CLAMSTAT("/proc", &sb) != -1 && !sb.st_size)
	procdev = sb.st_dev;
#endif

    /* check socket type */

    if(optget(opts, "TCPSocket")->enabled)
	tcpsock = 1;

    if(optget(opts, "LocalSocket")->enabled)
	localsock = 1;

    if(!tcpsock && !localsock) {
	logg("!Please define server type (local and/or TCP).\n");
	ret = 1;
	break;
    }

    logg("#clamd daemon %s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")\n", get_version());

#ifndef _WIN32
    if(user)
	logg("#Running as user %s (UID %u, GID %u)\n", user->pw_name, user->pw_uid, user->pw_gid);
#endif

#if defined(RLIMIT_DATA) && defined(C_BSD)
    if (getrlimit(RLIMIT_DATA, &rlim) == 0) {
       /* bb #1941.
        * On 32-bit FreeBSD if you set ulimit -d to >2GB then mmap() will fail
        * too soon (after ~120 MB).
        * Set limit lower than 2G if on 32-bit */
       uint64_t lim = rlim.rlim_cur;
       if (sizeof(void*) == 4 &&
           lim > (1ULL << 31)) {
           rlim.rlim_cur = 1ULL << 31;
           if (setrlimit(RLIMIT_DATA, &rlim) < 0)
               logg("!setrlimit(RLIMIT_DATA) failed: %s\n", strerror(errno));
           else
               logg("Running on 32-bit system, and RLIMIT_DATA > 2GB, lowering to 2GB!\n");
       }
    }
#endif


    if(logg_size)
	logg("#Log file size limited to %u bytes.\n", logg_size);
    else
	logg("#Log file size limit disabled.\n");

    min_port = optget(opts, "StreamMinPort")->numarg;
    max_port = optget(opts, "StreamMaxPort")->numarg;
    if (min_port < 1024 || min_port > max_port || max_port > 65535) {
	logg("!Invalid StreamMinPort/StreamMaxPort: %d, %d\n", min_port, max_port);
	ret = 1;
	break;
    }

    if(!(engine = cl_engine_new())) {
	logg("!Can't initialize antivirus engine\n");
	ret = 1;
	break;
    }

    /* load the database(s) */
    dbdir = optget(opts, "DatabaseDirectory")->strarg;
    logg("#Reading databases from %s\n", dbdir);

    if(optget(opts, "DetectPUA")->enabled) {
	dboptions |= CL_DB_PUA;

	if((opt = optget(opts, "ExcludePUA"))->enabled) {
	    dboptions |= CL_DB_PUA_EXCLUDE;
	    i = 0;
	    logg("#Excluded PUA categories:");
	    while(opt) {
		if(!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
		    logg("!Can't allocate memory for pua_cats\n");
		    cl_engine_free(engine);
		    ret = 1;
		    break;
		}
		logg("# %s", opt->strarg);
		sprintf(pua_cats + i, ".%s", opt->strarg);
		i += strlen(opt->strarg) + 1;
		pua_cats[i] = 0;
		opt = opt->nextarg;
	    }
	    if (ret)
		break;
	    logg("#\n");
	    pua_cats[i] = '.';
	    pua_cats[i + 1] = 0;
	}

	if((opt = optget(opts, "IncludePUA"))->enabled) {
	    if(pua_cats) {
		logg("!ExcludePUA and IncludePUA cannot be used at the same time\n");
		free(pua_cats);
		ret = 1;
		break;
	    }
	    dboptions |= CL_DB_PUA_INCLUDE;
	    i = 0;
	    logg("#Included PUA categories:");
	    while(opt) {
		if(!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
		    logg("!Can't allocate memory for pua_cats\n");
		    ret = 1;
		    break;
		}
		logg("# %s", opt->strarg);
		sprintf(pua_cats + i, ".%s", opt->strarg);
		i += strlen(opt->strarg) + 1;
		pua_cats[i] = 0;
		opt = opt->nextarg;
	    }
	    if (ret)
		break;
	    logg("#\n");
	    pua_cats[i] = '.';
	    pua_cats[i + 1] = 0;
	}

	if(pua_cats) {
	    if((ret = cl_engine_set_str(engine, CL_ENGINE_PUA_CATEGORIES, pua_cats))) {
		logg("!cli_engine_set_str(CL_ENGINE_PUA_CATEGORIES) failed: %s\n", cl_strerror(ret));
		free(pua_cats);
		ret = 1;
		break;
	    }
	    free(pua_cats);
	}
    } else {
	logg("#Not loading PUA signatures.\n");
    }

    if(optget(opts, "OfficialDatabaseOnly")->enabled) {
	dboptions |= CL_DB_OFFICIAL_ONLY;
	logg("#Only loading official signatures.\n");
    }

    /* set the temporary dir */
    if((opt = optget(opts, "TemporaryDirectory"))->enabled) {
	if((ret = cl_engine_set_str(engine, CL_ENGINE_TMPDIR, opt->strarg))) {
	    logg("!cli_engine_set_str(CL_ENGINE_TMPDIR) failed: %s\n", cl_strerror(ret));
	    ret = 1;
	    break;
	}
    }

    cl_engine_set_clcb_hash(engine, hash_callback);
    detstats_clear();

    if(optget(opts, "LeaveTemporaryFiles")->enabled)
	cl_engine_set_num(engine, CL_ENGINE_KEEPTMP, 1);

    if(optget(opts, "PhishingSignatures")->enabled)
	dboptions |= CL_DB_PHISHING;
    else
	logg("#Not loading phishing signatures.\n");

    if(optget(opts,"Bytecode")->enabled) {
	dboptions |= CL_DB_BYTECODE;
	if((opt = optget(opts,"BytecodeSecurity"))->enabled) {
	    enum bytecode_security s;
	    if (!strcmp(opt->strarg, "TrustSigned")) {
		s = CL_BYTECODE_TRUST_SIGNED;
		logg("#Bytecode: Security mode set to \"TrustSigned\".\n");
	    } else if (!strcmp(opt->strarg, "Paranoid")) {
		s = CL_BYTECODE_TRUST_NOTHING;
		logg("#Bytecode: Security mode set to \"Paranoid\".\n");
	    } else {
		logg("!Unable to parse bytecode security setting:%s\n",
		    opt->strarg);
		ret = 1;
		break;
	    }
	    if ((ret = cl_engine_set_num(engine, CL_ENGINE_BYTECODE_SECURITY, s))) {
		logg("^Invalid bytecode security setting %s: %s\n", opt->strarg, cl_strerror(ret));
		ret = 1;
		break;
	    }
	}
	if((opt = optget(opts,"BytecodeUnsigned"))->enabled) {
	    dboptions |= CL_DB_BYTECODE_UNSIGNED;
	    logg("#Bytecode: Enabled support for unsigned bytecode.\n");
	}
	if((opt = optget(opts,"BytecodeMode"))->enabled) {
	    enum bytecode_mode mode;
	    if (!strcmp(opt->strarg, "ForceJIT"))
		mode = CL_BYTECODE_MODE_JIT;
	    else if(!strcmp(opt->strarg, "ForceInterpreter"))
		mode = CL_BYTECODE_MODE_INTERPRETER;
	    else if(!strcmp(opt->strarg, "Test"))
		mode = CL_BYTECODE_MODE_TEST;
	    else
		mode = CL_BYTECODE_MODE_AUTO;
	    cl_engine_set_num(engine, CL_ENGINE_BYTECODE_MODE, mode);
	}
	if((opt = optget(opts,"BytecodeTimeout"))->enabled) {
	    cl_engine_set_num(engine, CL_ENGINE_BYTECODE_TIMEOUT, opt->numarg);
	}
    } else
	logg("#Bytecode support disabled.\n");

    if(optget(opts,"PhishingScanURLs")->enabled)
	dboptions |= CL_DB_PHISHING_URLS;
    else
	logg("#Disabling URL based phishing detection.\n");

    if(optget(opts,"DevACOnly")->enabled) {
	logg("#Only using the A-C matcher.\n");
	cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
    }

    if((opt = optget(opts, "DevACDepth"))->enabled) {
        cl_engine_set_num(engine, CL_ENGINE_AC_MAXDEPTH, opt->numarg);
	logg("#Max A-C depth set to %u\n", (unsigned int) opt->numarg);
    }

    if((ret = cl_load(dbdir, engine, &sigs, dboptions))) {
	logg("!%s\n", cl_strerror(ret));
	ret = 1;
	break;
    }

    if (optget(opts, "DisableCertCheck")->enabled)
        engine->dconf->pe |= PE_CONF_DISABLECERT;

    logg("#Loaded %u signatures.\n", sigs);
    if((ret = cl_engine_compile(engine)) != 0) {
	logg("!Database initialization error: %s\n", cl_strerror(ret));
	ret = 1;
	break;
    }

    if(tcpsock) {
	if ((lsockets[nlsockets] = tcpserver(opts)) == -1) {
	    ret = 1;
	    break;
	}
	nlsockets++;
    }
#ifndef _WIN32
    if(localsock) {
	mode_t sock_mode, umsk = umask(0777); /* socket is created with 000 to avoid races */
	if ((lsockets[nlsockets] = localserver(opts)) == -1) {
	    ret = 1;
	    umask(umsk);
	    break;
	}
	umask(umsk); /* restore umask */
	if(optget(opts, "LocalSocketGroup")->enabled) {
	    char *gname = optget(opts, "LocalSocketGroup")->strarg, *end;
	    gid_t sock_gid = strtol(gname, &end, 10);
	    if(*end) {
		struct group *pgrp = getgrnam(gname);
		if(!pgrp) {
		    logg("!Unknown group %s\n", gname);
		    ret = 1;
		    break;
		}
		sock_gid = pgrp->gr_gid;
	    }
	    if(chown(optget(opts, "LocalSocket")->strarg, -1, sock_gid)) {
		logg("!Failed to change socket ownership to group %s\n", gname);
		ret = 1;
		break;
	    }
	}
	if(optget(opts, "LocalSocketMode")->enabled) {
	    char *end;
	    sock_mode = strtol(optget(opts, "LocalSocketMode")->strarg, &end, 8);
	    if(*end) {
		logg("!Invalid LocalSocketMode %s\n", optget(opts, "LocalSocketMode")->strarg);
		ret = 1;
		break;
	    }
	} else
	    sock_mode = 0777 /* & ~umsk*/; /* conservative default: umask was 0 in clamd < 0.96 */

	if(chmod(optget(opts, "LocalSocket")->strarg, sock_mode & 0666)) {
	    logg("!Cannot set socket permission to %s\n", optget(opts, "LocalSocketMode")->strarg);
	    ret = 1;
	    break;
	}

	nlsockets++;
    }

    /* fork into background */
    if(!optget(opts, "Foreground")->enabled) {
#ifdef C_BSD	    
	/* workaround for OpenBSD bug, see https://wwws.clamav.net/bugzilla/show_bug.cgi?id=885 */
	for(ret=0;ret<nlsockets;ret++) {
		if (fcntl(lsockets[ret], F_SETFL, fcntl(lsockets[ret], F_GETFL) | O_NONBLOCK) == -1) {
            logg("!fcntl for lsockets[] failed\n");
            close(lsockets[ret]);
            ret = 1;
            break;
        }
	}
#endif
	gengine = engine;
	atexit(free_engine);
	if(daemonize() == -1) {
	    logg("!daemonize() failed: %s\n", strerror(errno));
	    ret = 1;
	    break;
	}
	gengine = NULL;
#ifdef C_BSD
	for(ret=0;ret<nlsockets;ret++) {
		if (fcntl(lsockets[ret], F_SETFL, fcntl(lsockets[ret], F_GETFL) & ~O_NONBLOCK) == -1) {
            logg("!fcntl for lsockets[] failed\n");
            close(lsockets[ret]);
            ret = 1;
            break;
        }
	}
#endif
	if(!debug_mode)
	    if(chdir("/") == -1)
		logg("^Can't change current working directory to root\n");

    } else
        foreground = 1;
#endif

    ret = recvloop_th(lsockets, nlsockets, engine, dboptions, opts);

    } while (0);

    logg("*Closing the main socket%s.\n", (nlsockets > 1) ? "s" : "");

    for (i = 0; i < nlsockets; i++) {
	closesocket(lsockets[i]);
    }

#ifndef _WIN32
    if(nlsockets && localsock) {
	opt = optget(opts, "LocalSocket");
	if(unlink(opt->strarg) == -1)
	    logg("!Can't unlink the socket file %s\n", opt->strarg);
	else
	    logg("Socket file removed.\n");
    }
#endif

    logg_close();
    optfree(opts);

    return ret;
}
Example #6
0
int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsigned int dboptions, const struct optstruct *opts)
{
	int max_threads, max_queue, readtimeout, ret = 0;
	unsigned int options = 0;
	char timestr[32];
#ifndef	_WIN32
	struct sigaction sigact;
	sigset_t sigset;
	struct rlimit rlim;
#endif
	mode_t old_umask;
	const struct optstruct *opt;
	char buff[BUFFSIZE + 1];
	pid_t mainpid;
	int idletimeout;
	unsigned long long val;
	size_t i, j, rr_last = 0;
	pthread_t accept_th;
	pthread_mutex_t fds_mutex = PTHREAD_MUTEX_INITIALIZER;
	pthread_mutex_t recvfds_mutex = PTHREAD_MUTEX_INITIALIZER;
	struct acceptdata acceptdata = ACCEPTDATA_INIT(&fds_mutex, &recvfds_mutex);
	struct fd_data *fds = &acceptdata.recv_fds;
	time_t start_time, current_time;
	unsigned int selfchk;
	threadpool_t *thr_pool;

#if defined(FANOTIFY) || defined(CLAMAUTH)
	pthread_t fan_pid;
	pthread_attr_t fan_attr;
	struct thrarg *tharg = NULL; /* shut up gcc */
#endif

#ifndef	_WIN32
	memset(&sigact, 0, sizeof(struct sigaction));
#endif

    /* set up limits */
    if((opt = optget(opts, "MaxScanSize"))->active) {
	if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) {
	    logg("!cl_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
	    cl_engine_free(engine);
	    return 1;
	}
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL);
    if(val)
    	logg("Limits: Global size limit set to %llu bytes.\n", val);
    else
    	logg("^Limits: Global size limit protection disabled.\n");

    if((opt = optget(opts, "MaxFileSize"))->active) {
	if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILESIZE, opt->numarg))) {
	    logg("!cl_engine_set_num(CL_ENGINE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
	    cl_engine_free(engine);
	    return 1;
	}
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, NULL);
    if(val)
    	logg("Limits: File size limit set to %llu bytes.\n", val);
    else
    	logg("^Limits: File size limit protection disabled.\n");

#ifndef _WIN32
    if(getrlimit(RLIMIT_FSIZE, &rlim) == 0) {
	if(rlim.rlim_cur < (rlim_t) cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, NULL))
	    logg("^System limit for file size is lower than engine->maxfilesize\n");
	if(rlim.rlim_cur < (rlim_t) cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL))
	    logg("^System limit for file size is lower than engine->maxscansize\n");
    } else {
	logg("^Cannot obtain resource limits for file size\n");
    }
#endif

    if((opt = optget(opts, "MaxRecursion"))->active) {
	if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECURSION, opt->numarg))) {
	    logg("!cl_engine_set_num(CL_ENGINE_MAX_RECURSION) failed: %s\n", cl_strerror(ret));
	    cl_engine_free(engine);
	    return 1;
	}
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_RECURSION, NULL);
    if(val)
    	logg("Limits: Recursion level limit set to %u.\n", (unsigned int) val);
    else
    	logg("^Limits: Recursion level limit protection disabled.\n");

    if((opt = optget(opts, "MaxFiles"))->active) {
	if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILES, opt->numarg))) {
	    logg("!cl_engine_set_num(CL_ENGINE_MAX_FILES) failed: %s\n", cl_strerror(ret));
	    cl_engine_free(engine);
	    return 1;
	}
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_FILES, NULL);
    if(val)
    	logg("Limits: Files limit set to %u.\n", (unsigned int) val);
    else
    	logg("^Limits: Files limit protection disabled.\n");

#ifndef _WIN32
    if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
	logg("*Limits: Core-dump limit is %lu.\n", (unsigned long)rlim.rlim_cur);
    }
#endif

    /* Engine max sizes */

    if((opt = optget(opts, "MaxEmbeddedPE"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_EMBEDDEDPE, opt->numarg))) {
            logg("!cli_engine_set_num(CL_ENGINE_MAX_EMBEDDEDPE) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_EMBEDDEDPE, NULL);
    logg("Limits: MaxEmbeddedPE limit set to %llu bytes.\n", val);

    if((opt = optget(opts, "MaxHTMLNormalize"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNORMALIZE, opt->numarg))) {
            logg("!cli_engine_set_num(CL_ENGINE_MAX_HTMLNORMALIZE) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_HTMLNORMALIZE, NULL);
    logg("Limits: MaxHTMLNormalize limit set to %llu bytes.\n", val);

    if((opt = optget(opts, "MaxHTMLNoTags"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNOTAGS, opt->numarg))) {
            logg("!cli_engine_set_num(CL_ENGINE_MAX_HTMLNOTAGS) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_HTMLNOTAGS, NULL);
    logg("Limits: MaxHTMLNoTags limit set to %llu bytes.\n", val);

    if((opt = optget(opts, "MaxScriptNormalize"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCRIPTNORMALIZE, opt->numarg))) {
            logg("!cli_engine_set_num(CL_ENGINE_MAX_SCRIPTNORMALIZE) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_SCRIPTNORMALIZE, NULL);
    logg("Limits: MaxScriptNormalize limit set to %llu bytes.\n", val);

    if((opt = optget(opts, "MaxZipTypeRcg"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ZIPTYPERCG, opt->numarg))) {
            logg("!cli_engine_set_num(CL_ENGINE_MAX_ZIPTYPERCG) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_ZIPTYPERCG, NULL);
    logg("Limits: MaxZipTypeRcg limit set to %llu bytes.\n", val);

    if((opt = optget(opts, "MaxPartitions"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_PARTITIONS, opt->numarg))) {
            logg("!cli_engine_set_num(MaxPartitions) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_PARTITIONS, NULL);
    logg("Limits: MaxPartitions limit set to %llu.\n", val);

    if((opt = optget(opts, "MaxIconsPE"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ICONSPE, opt->numarg))) {
            logg("!cli_engine_set_num(MaxIconsPE) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_ICONSPE, NULL);
    logg("Limits: MaxIconsPE limit set to %llu.\n", val);

    if((opt = optget(opts, "PCREMatchLimit"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MATCH_LIMIT, opt->numarg))) {
            logg("!cli_engine_set_num(PCREMatchLimit) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_PCRE_MATCH_LIMIT, NULL);
    logg("Limits: PCREMatchLimit limit set to %llu.\n", val);

    if((opt = optget(opts, "PCRERecMatchLimit"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_RECMATCH_LIMIT, opt->numarg))) {
            logg("!cli_engine_set_num(PCRERecMatchLimit) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_PCRE_RECMATCH_LIMIT, NULL);
    logg("Limits: PCRERecMatchLimit limit set to %llu.\n", val);

    if((opt = optget(opts, "PCREMaxFileSize"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, opt->numarg))) {
            logg("!cli_engine_set_num(PCREMaxFileSize) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, NULL);
    logg("Limits: PCREMaxFileSize limit set to %llu.\n", val);

    if(optget(opts, "ScanArchive")->enabled) {
	logg("Archive support enabled.\n");
	options |= CL_SCAN_ARCHIVE;

	if(optget(opts, "ArchiveBlockEncrypted")->enabled) {
	    logg("Archive: Blocking encrypted archives.\n");
	    options |= CL_SCAN_BLOCKENCRYPTED;
	}

    } else {
	logg("Archive support disabled.\n");
    }

    if(optget(opts, "AlgorithmicDetection")->enabled) {
	logg("Algorithmic detection enabled.\n");
	options |= CL_SCAN_ALGORITHMIC;
    } else {
	logg("Algorithmic detection disabled.\n");
    }

    if(optget(opts, "ScanPE")->enabled) {
	logg("Portable Executable support enabled.\n");
	options |= CL_SCAN_PE;
    } else {
	logg("Portable Executable support disabled.\n");
    }

    if(optget(opts, "ScanELF")->enabled) {
	logg("ELF support enabled.\n");
	options |= CL_SCAN_ELF;
    } else {
	logg("ELF support disabled.\n");
    }

    if(optget(opts, "ScanPE")->enabled || optget(opts, "ScanELF")->enabled) {
	if(optget(opts, "DetectBrokenExecutables")->enabled) {
	    logg("Detection of broken executables enabled.\n");
	    options |= CL_SCAN_BLOCKBROKEN;
	}
    }

    if(optget(opts, "ScanMail")->enabled) {
	logg("Mail files support enabled.\n");
	options |= CL_SCAN_MAIL;

	if(optget(opts, "ScanPartialMessages")->enabled) {
	    logg("Mail: RFC1341 handling enabled.\n");
	    options |= CL_SCAN_PARTIAL_MESSAGE;
	}

    } else {
	logg("Mail files support disabled.\n");
    }

    if(optget(opts, "ScanOLE2")->enabled) {
	logg("OLE2 support enabled.\n");
	options |= CL_SCAN_OLE2;
	if(optget(opts, "OLE2BlockMacros")->enabled) {
	    logg("OLE2: Blocking all VBA macros.\n");
	    options |= CL_SCAN_BLOCKMACROS;
	}
    } else {
	logg("OLE2 support disabled.\n");
    }

    if(optget(opts, "ScanPDF")->enabled) {
	logg("PDF support enabled.\n");
	options |= CL_SCAN_PDF;
    } else {
	logg("PDF support disabled.\n");
    }

    if(optget(opts, "ScanSWF")->enabled) {
	logg("SWF support enabled.\n");
	options |= CL_SCAN_SWF;
    } else {
	logg("SWF support disabled.\n");
    }

    if(optget(opts, "ScanHTML")->enabled) {
	logg("HTML support enabled.\n");
	options |= CL_SCAN_HTML;
    } else {
	logg("HTML support disabled.\n");
    }

    if(optget(opts,"PhishingScanURLs")->enabled) {

	if(optget(opts,"PhishingAlwaysBlockCloak")->enabled) {
	    options |= CL_SCAN_PHISHING_BLOCKCLOAK; 
	    logg("Phishing: Always checking for cloaked urls\n");
	}

	if(optget(opts,"PhishingAlwaysBlockSSLMismatch")->enabled) {
	    options |= CL_SCAN_PHISHING_BLOCKSSL;
	    logg("Phishing: Always checking for ssl mismatches\n");
	}
    }

    if(optget(opts,"PartitionIntersection")->enabled) {
        options |= CL_SCAN_PARTITION_INTXN;
        logg("Raw DMG: Always checking for partitons intersections\n");
    }

    if(optget(opts,"HeuristicScanPrecedence")->enabled) {
	    options |= CL_SCAN_HEURISTIC_PRECEDENCE;
	    logg("Heuristic: precedence enabled\n");
    }

    if(optget(opts, "StructuredDataDetection")->enabled) {
        options |= CL_SCAN_STRUCTURED;

	if((opt = optget(opts, "StructuredMinCreditCardCount"))->enabled) {
	    if((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_CC_COUNT, opt->numarg))) {
		logg("!cl_engine_set_num(CL_ENGINE_MIN_CC_COUNT) failed: %s\n", cl_strerror(ret));
		cl_engine_free(engine);
		return 1;
	    }
	}
	val = cl_engine_get_num(engine, CL_ENGINE_MIN_CC_COUNT, NULL);
	logg("Structured: Minimum Credit Card Number Count set to %u\n", (unsigned int) val);

	if((opt = optget(opts, "StructuredMinSSNCount"))->enabled) {
	    if((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_SSN_COUNT, opt->numarg))) {
		logg("!cl_engine_set_num(CL_ENGINE_MIN_SSN_COUNT) failed: %s\n", cl_strerror(ret));
		cl_engine_free(engine);
		return 1;
	    }
	}
	val = cl_engine_get_num(engine, CL_ENGINE_MIN_SSN_COUNT, NULL);
        logg("Structured: Minimum Social Security Number Count set to %u\n", (unsigned int) val);

        if(optget(opts, "StructuredSSNFormatNormal")->enabled)
            options |= CL_SCAN_STRUCTURED_SSN_NORMAL;

        if(optget(opts, "StructuredSSNFormatStripped")->enabled)
	    options |= CL_SCAN_STRUCTURED_SSN_STRIPPED;
    }

#ifdef HAVE__INTERNAL__SHA_COLLECT
    if(optget(opts, "DevCollectHashes")->enabled)
	options |= CL_SCAN_INTERNAL_COLLECT_SHA;
#endif

    selfchk = optget(opts, "SelfCheck")->numarg;
    if(!selfchk) {
	logg("Self checking disabled.\n");
    } else {
	logg("Self checking every %u seconds.\n", selfchk);
    }

    /* save the PID */
    mainpid = getpid();
    if((opt = optget(opts, "PidFile"))->enabled) {
	    FILE *fd;
	old_umask = umask(0002);
	if((fd = fopen(opt->strarg, "w")) == NULL) {
	    logg("!Can't save PID in file %s\n", opt->strarg);
	} else {
	    if (fprintf(fd, "%u\n", (unsigned int) mainpid)<0) {
	    	logg("!Can't save PID in file %s\n", opt->strarg);
	    }
	    fclose(fd);
	}
	umask(old_umask);
    }

    logg("*Listening daemon: PID: %u\n", (unsigned int) mainpid);
    max_threads = optget(opts, "MaxThreads")->numarg;
    max_queue = optget(opts, "MaxQueue")->numarg;
    acceptdata.commandtimeout = optget(opts, "CommandReadTimeout")->numarg;
    readtimeout = optget(opts, "ReadTimeout")->numarg;

#if !defined(_WIN32) && defined(RLIMIT_NOFILE)
    if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
	/* don't warn if default value is too high, silently fix it */
	unsigned maxrec;
	int max_max_queue;
	unsigned warn = optget(opts, "MaxQueue")->active;
	const unsigned clamdfiles = 6;
	/* Condition to not run out of file descriptors:
	 * MaxThreads * MaxRecursion + (MaxQueue - MaxThreads) + CLAMDFILES < RLIMIT_NOFILE 
	 * CLAMDFILES is 6: 3 standard FD + logfile + 2 FD for reloading the DB
	 * */
#ifdef C_SOLARIS
#ifdef HAVE_ENABLE_EXTENDED_FILE_STDIO
	if (enable_extended_FILE_stdio(-1, -1) == -1) {
	    logg("^Unable to set extended FILE stdio, clamd will be limited to max 256 open files\n");
	    rlim.rlim_cur = rlim.rlim_cur > 255 ? 255 : rlim.rlim_cur;
	}
#elif !defined(_LP64)
	if (rlim.rlim_cur > 255) {
	    rlim.rlim_cur = 255;
	    logg("^Solaris only supports 256 open files for 32-bit processes, you need at least Solaris 10u4, or compile as 64-bit to support more!\n");
	}
#endif
#endif
	opt = optget(opts,"MaxRecursion");
	maxrec = opt->numarg;
	max_max_queue = rlim.rlim_cur - maxrec * max_threads - clamdfiles + max_threads;
	if (max_queue < max_threads) {
	    max_queue = max_threads;
	    if (warn)
		logg("^MaxQueue value too low, increasing to: %d\n", max_queue);
	}
	if (max_max_queue < max_threads) {
	    logg("^MaxThreads * MaxRecursion is too high: %d, open file descriptor limit is: %lu\n",
		 maxrec*max_threads, (unsigned long)rlim.rlim_cur);
	    max_max_queue = max_threads;
	}
	if (max_queue > max_max_queue) {
	    max_queue = max_max_queue;
	    if (warn)
		logg("^MaxQueue value too high, lowering to: %d\n", max_queue);
	} else if (max_queue < 2*max_threads && max_queue < max_max_queue) {
	    max_queue = 2*max_threads;
	    if (max_queue > max_max_queue)
		max_queue = max_max_queue;
	    /* always warn here */
	    logg("^MaxQueue is lower than twice MaxThreads, increasing to: %d\n", max_queue);
	}
    }
#endif
    logg("*MaxQueue set to: %d\n", max_queue);
    acceptdata.max_queue = max_queue;

    if(optget(opts, "ScanOnAccess")->enabled)

#if defined(FANOTIFY) || defined(CLAMAUTH)
    {
        do {
	    if(pthread_attr_init(&fan_attr)) break;
	    pthread_attr_setdetachstate(&fan_attr, PTHREAD_CREATE_JOINABLE);
	    if(!(tharg = (struct thrarg *) malloc(sizeof(struct thrarg)))) break;
	    tharg->opts = opts;
	    tharg->engine = engine;
	    tharg->options = options;
	    if(!pthread_create(&fan_pid, &fan_attr, onas_fan_th, tharg)) break;
	    free(tharg);
	    tharg=NULL;
	} while(0);
	if (!tharg) logg("!Unable to start on-access scan\n");
    }
#else
	logg("!On-access scan is not available\n");
#endif


#ifndef	_WIN32
    /* set up signal handling */
    sigfillset(&sigset);
    sigdelset(&sigset, SIGINT);
    sigdelset(&sigset, SIGTERM);
    sigdelset(&sigset, SIGSEGV);
    sigdelset(&sigset, SIGHUP);
    sigdelset(&sigset, SIGPIPE);
    sigdelset(&sigset, SIGUSR2);
    /* The behavior of a process is undefined after it ignores a 
     * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
    sigdelset(&sigset, SIGFPE);
    sigdelset(&sigset, SIGILL);
    sigdelset(&sigset, SIGSEGV);
#ifdef SIGBUS    
    sigdelset(&sigset, SIGBUS);
#endif
    sigdelset(&sigset, SIGTSTP);
    sigdelset(&sigset, SIGCONT);
    sigprocmask(SIG_SETMASK, &sigset, NULL);

    /* SIGINT, SIGTERM, SIGSEGV */
    sigact.sa_handler = sighandler_th;
    sigemptyset(&sigact.sa_mask);
    sigaddset(&sigact.sa_mask, SIGINT);
    sigaddset(&sigact.sa_mask, SIGTERM);
    sigaddset(&sigact.sa_mask, SIGHUP);
    sigaddset(&sigact.sa_mask, SIGPIPE);
    sigaddset(&sigact.sa_mask, SIGUSR2);
    sigaction(SIGINT, &sigact, NULL);
    sigaction(SIGTERM, &sigact, NULL);
    sigaction(SIGHUP, &sigact, NULL);
    sigaction(SIGPIPE, &sigact, NULL);
    sigaction(SIGUSR2, &sigact, NULL);
#endif

    idletimeout = optget(opts, "IdleTimeout")->numarg;

    for (i=0;i < nsockets;i++)
	if (fds_add(&acceptdata.fds, socketds[i], 1, 0) == -1) {
	    logg("!fds_add failed\n");
	    cl_engine_free(engine);
	    return 1;
	}
#ifdef _WIN32
	event_wake_accept = CreateEvent(NULL, TRUE, FALSE, NULL);
	event_wake_recv = CreateEvent(NULL, TRUE, FALSE, NULL);
#else
    if (pipe(acceptdata.syncpipe_wake_recv) == -1 ||
	(pipe(acceptdata.syncpipe_wake_accept) == -1)) {

	logg("!pipe failed\n");
	exit(-1);
    }
    syncpipe_wake_recv_w = acceptdata.syncpipe_wake_recv[1];

    if (fds_add(fds, acceptdata.syncpipe_wake_recv[0], 1, 0) == -1 ||
	fds_add(&acceptdata.fds, acceptdata.syncpipe_wake_accept[0], 1, 0)) {
	logg("!failed to add pipe fd\n");
	exit(-1);
    }
#endif

    if ((thr_pool = thrmgr_new(max_threads, idletimeout, max_queue, scanner_thread)) == NULL) {
	logg("!thrmgr_new failed\n");
	exit(-1);
    }

    if (pthread_create(&accept_th, NULL, acceptloop_th, &acceptdata)) {
	logg("!pthread_create failed\n");
	exit(-1);
    }

    time(&start_time);
    for(;;) {
	int new_sd;

	/* Block waiting for connection on any of the sockets */
	pthread_mutex_lock(fds->buf_mutex);
	fds_cleanup(fds);
	/* signal that we can accept more connections */
	if (fds->nfds <= (unsigned)max_queue)
	    pthread_cond_signal(&acceptdata.cond_nfds);
	new_sd = fds_poll_recv(fds, selfchk ? (int)selfchk : -1, 1, event_wake_recv);
#ifdef _WIN32
	ResetEvent(event_wake_recv);
#else
	if (!fds->nfds) {
	    /* at least the dummy/sync pipe should have remained */
	    logg("!All recv() descriptors gone: fatal\n");
	    pthread_mutex_lock(&exit_mutex);
	    progexit = 1;
	    pthread_mutex_unlock(&exit_mutex);
	    pthread_mutex_unlock(fds->buf_mutex);
	    break;
	}
#endif
	if (new_sd == -1 && errno != EINTR) {
	    logg("!Failed to poll sockets, fatal\n");
	    pthread_mutex_lock(&exit_mutex);
	    progexit = 1;
	    pthread_mutex_unlock(&exit_mutex);
	}


	if(fds->nfds) i = (rr_last + 1) % fds->nfds;
	for (j = 0;  j < fds->nfds && new_sd >= 0; j++, i = (i+1) % fds->nfds) {
	    size_t pos = 0;
	    int error = 0;
	    struct fd_buf *buf = &fds->buf[i];
	    if (!buf->got_newdata)
		continue;

#ifndef _WIN32
	    if (buf->fd == acceptdata.syncpipe_wake_recv[0]) {
		/* dummy sync pipe, just to wake us */
		if (read(buf->fd, buff, sizeof(buff)) < 0) {
		    logg("^Syncpipe read failed\n");
		}
		continue;
	    }
#endif
	    if (buf->got_newdata == -1) {
		if (buf->mode == MODE_WAITREPLY) {
		    logg("$mode WAIT_REPLY -> closed\n");
		    buf->fd = -1;
		    thrmgr_group_terminate(buf->group);
		    thrmgr_group_finished(buf->group, EXIT_ERROR);
		    continue;
		} else {
		    logg("$client read error or EOF on read\n");
		    error = 1;
		}
	    }

	    if (buf->fd != -1 && buf->got_newdata == -2) {
		logg("$Client read timed out\n");
		mdprintf(buf->fd, "COMMAND READ TIMED OUT\n");
		error = 1;
	    }

	    rr_last = i;
	    if (buf->mode == MODE_WAITANCILL) {
		buf->mode = MODE_COMMAND;
		logg("$mode -> MODE_COMMAND\n");
	    }
	    while (!error && buf->fd != -1 && buf->buffer && pos < buf->off &&
		   buf->mode != MODE_WAITANCILL) {
		client_conn_t conn;
		const char *cmd = NULL;
		int rc;
		/* New data available to read on socket. */

		memset(&conn, 0, sizeof(conn));
		conn.scanfd = buf->recvfd;
		buf->recvfd = -1;
		conn.sd = buf->fd;
		conn.options = options;
		conn.opts = opts;
		conn.thrpool = thr_pool;
		conn.engine = engine;
		conn.group = buf->group;
		conn.id = buf->id;
		conn.quota = buf->quota;
		conn.filename = buf->dumpname;
		conn.mode = buf->mode;
		conn.term = buf->term;

		/* Parse & dispatch command */
		cmd = parse_dispatch_cmd(&conn, buf, &pos, &error, opts, readtimeout);

		if (conn.mode == MODE_COMMAND && !cmd)
		    break;
		if (!error) {
		    if (buf->mode == MODE_WAITREPLY && buf->off) {
			/* Client is not supposed to send anything more */
			logg("^Client sent garbage after last command: %lu bytes\n", (unsigned long)buf->off);
			buf->buffer[buf->off] = '\0';
			logg("$Garbage: %s\n", buf->buffer);
			error = 1;
		    } else if (buf->mode == MODE_STREAM) {
			rc = handle_stream(&conn, buf, opts, &error, &pos, readtimeout);
			if (rc == -1)
			    break;
			else
			    continue;
		    }
		}
		if (error && error != CL_ETIMEOUT) {
		    conn_reply_error(&conn, "Error processing command.");
		}
	    }
	    if (error) {
		if (buf->dumpfd != -1) {
		    close(buf->dumpfd);
		    if (buf->dumpname) {
			cli_unlink(buf->dumpname);
			free(buf->dumpname);
		    }
		    buf->dumpfd = -1;
		}
		thrmgr_group_terminate(buf->group);
		if (thrmgr_group_finished(buf->group, EXIT_ERROR)) {
		    if (buf->fd < 0) {
			logg("$Skipping shutdown of bad socket after error (FD %d)\n", buf->fd);
		    }
		    else {
			logg("$Shutting down socket after error (FD %d)\n", buf->fd);
			shutdown(buf->fd, 2);
			closesocket(buf->fd);
		    }
		} else
		    logg("$Socket not shut down due to active tasks\n");
		buf->fd = -1;
	    }
	}
	pthread_mutex_unlock(fds->buf_mutex);

	/* handle progexit */
	pthread_mutex_lock(&exit_mutex);
	if (progexit) {
	    pthread_mutex_unlock(&exit_mutex);
	    pthread_mutex_lock(fds->buf_mutex);
	    for (i=0;i < fds->nfds; i++) {
		if (fds->buf[i].fd == -1)
		    continue;
		thrmgr_group_terminate(fds->buf[i].group);
		if (thrmgr_group_finished(fds->buf[i].group, EXIT_ERROR)) {
		    logg("$Shutdown closed fd %d\n", fds->buf[i].fd);
		    shutdown(fds->buf[i].fd, 2);
		    closesocket(fds->buf[i].fd);
		    fds->buf[i].fd = -1;
		}
	    }
	    pthread_mutex_unlock(fds->buf_mutex);
	    break;
	}
	pthread_mutex_unlock(&exit_mutex);

	/* SIGHUP */
	if (sighup) {
	    logg("SIGHUP caught: re-opening log file.\n");
	    logg_close();
	    sighup = 0;
	    if(!logg_file && (opt = optget(opts, "LogFile"))->enabled)
		logg_file = opt->strarg;
	}

	/* SelfCheck */
	if(selfchk) {
	    time(&current_time);
	    if((current_time - start_time) >= (time_t)selfchk) {
		if(reload_db(engine, dboptions, opts, TRUE, &ret)) {
		    pthread_mutex_lock(&reload_mutex);
		    reload = 1;
		    pthread_mutex_unlock(&reload_mutex);
		}
		time(&start_time);
	    }
	}

	/* DB reload */
	pthread_mutex_lock(&reload_mutex);
	if(reload) {
	    pthread_mutex_unlock(&reload_mutex);

	    engine = reload_db(engine, dboptions, opts, FALSE, &ret);
	    if(ret) {
		logg("Terminating because of a fatal error.\n");
		if(new_sd >= 0)
		    closesocket(new_sd);
		break;
	    }

	    pthread_mutex_lock(&reload_mutex);
	    reload = 0;
	    time(&reloaded_time);
	    pthread_mutex_unlock(&reload_mutex);

#if defined(FANOTIFY) || defined(CLAMAUTH)
	    if(optget(opts, "ScanOnAccess")->enabled && tharg) {
		tharg->engine = engine;
	    }
#endif
	    time(&start_time);
	} else {
	    pthread_mutex_unlock(&reload_mutex);
	}
    }

    pthread_mutex_lock(&exit_mutex);
    progexit = 1;
    pthread_mutex_unlock(&exit_mutex);
#ifdef _WIN32
    SetEvent(event_wake_accept);
#else
    if (write(acceptdata.syncpipe_wake_accept[1], "", 1) < 0) {
	logg("^Write to syncpipe failed\n");
    }
#endif
    /* Destroy the thread manager.
     * This waits for all current tasks to end
     */
    logg("*Waiting for all threads to finish\n");
    thrmgr_destroy(thr_pool);
#if defined(FANOTIFY) || defined(CLAMAUTH)
    if(optget(opts, "ScanOnAccess")->enabled && tharg) {
	logg("Stopping on-access scan\n");
	pthread_mutex_lock(&logg_mutex);
	pthread_kill(fan_pid, SIGUSR1);
	pthread_mutex_unlock(&logg_mutex);
	pthread_join(fan_pid, NULL);
    free(tharg);
    }
#endif
    if(engine) {
	thrmgr_setactiveengine(NULL);
	cl_engine_free(engine);
    }

    pthread_join(accept_th, NULL);
    fds_free(fds);
    pthread_mutex_destroy(fds->buf_mutex);
    pthread_cond_destroy(&acceptdata.cond_nfds);
#ifdef _WIN32
    CloseHandle(event_wake_accept);
    CloseHandle(event_wake_recv);
#else
    close(acceptdata.syncpipe_wake_accept[1]);
    close(acceptdata.syncpipe_wake_recv[1]);
#endif
    if(dbstat.entries)
	cl_statfree(&dbstat);
    logg("*Shutting down the main socket%s.\n", (nsockets > 1) ? "s" : "");
    for (i = 0; i < nsockets; i++)
	shutdown(socketds[i], 2);

    if((opt = optget(opts, "PidFile"))->enabled) {
	if(unlink(opt->strarg) == -1)
	    logg("!Can't unlink the pid file %s\n", opt->strarg);
	else
	    logg("Pid file removed.\n");
    }

    time(&current_time);
    logg("--- Stopped at %s", cli_ctime(&current_time, timestr, sizeof(timestr)));

    return ret;
} 
Example #7
0
int main(int argc, char **argv) {
    char *my_socket, *pt;
    const struct optstruct *opt;
    struct optstruct *opts;
    time_t currtime;
    int ret;

    memset(&descr, 0, sizeof(struct smfiDesc));
    descr.xxfi_name = "ClamAV";			/* filter name */
    descr.xxfi_version = SMFI_VERSION;		/* milter version */
    descr.xxfi_flags = SMFIF_QUARANTINE;	/* flags */
    descr.xxfi_connect = clamfi_connect;	/* connection info filter */
    descr.xxfi_envfrom = clamfi_envfrom;	/* envelope sender filter */
    descr.xxfi_envrcpt = clamfi_envrcpt;	/* envelope recipient filter */
    descr.xxfi_header = clamfi_header;		/* header filter */
    descr.xxfi_body = clamfi_body;		/* body block */
    descr.xxfi_eom = clamfi_eom;		/* end of message */
    descr.xxfi_abort = clamfi_abort;		/* message aborted */

    opts = optparse(NULL, argc, argv, 1, OPT_MILTER, 0, NULL);
    if (!opts) {
	mprintf("!Can't parse command line options\n");
	return 1;
    }

    if(optget(opts, "help")->enabled) {
	printf("Usage: %s [-c <config-file>]\n\n", argv[0]);
	printf("    --help                   -h       Show this help\n");
	printf("    --version                -V       Show version and exit\n");
	printf("    --config-file <file>     -c       Read configuration from file\n\n");
	optfree(opts);
	return 0;
    }

    if(opts->filename) {
	int x;
	for(x = 0; opts->filename[x]; x++)
	    mprintf("^Ignoring option %s\n", opts->filename[x]);
    }

    if(optget(opts, "version")->enabled) {
	printf("clamav-milter %s\n", get_version());
	optfree(opts);
	return 0;
    }

    pt = strdup(optget(opts, "config-file")->strarg);
    if((opts = optparse(pt, 0, NULL, 1, OPT_MILTER, 0, opts)) == NULL) {
	printf("%s: cannot parse config file %s\n", argv[0], pt);
	free(pt);
	return 1;
    }
    free(pt);

    if((opt = optget(opts, "Chroot"))->enabled) {
	if(chdir(opt->strarg) != 0) {
	    logg("!Cannot change directory to %s\n", opt->strarg);
	    return 1;
	}
	if(chroot(opt->strarg) != 0) {
	    logg("!chroot to %s failed. Are you root?\n", opt->strarg);
	    return 1;
	}
    }

    if(geteuid() == 0 && (opt = optget(opts, "User"))->enabled) {
        struct passwd *user = NULL;
	if((user = getpwnam(opt->strarg)) == NULL) {
	    fprintf(stderr, "ERROR: Can't get information about user %s.\n", opt->strarg);
	    optfree(opts);
	    return 1;
	}

	if(optget(opts, "AllowSupplementaryGroups")->enabled) {
#ifdef HAVE_INITGROUPS
	    if(initgroups(opt->strarg, user->pw_gid)) {
		fprintf(stderr, "ERROR: initgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#else
	    mprintf("!AllowSupplementaryGroups: initgroups() is not available, please disable AllowSupplementaryGroups\n");
	    optfree(opts);
	    return 1;
#endif
	} else {
#ifdef HAVE_SETGROUPS
	    if(setgroups(1, &user->pw_gid)) {
		fprintf(stderr, "ERROR: setgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#endif
	}

	if(setgid(user->pw_gid)) {
	    fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
	    optfree(opts);
	    return 1;
	}

	if(setuid(user->pw_uid)) {
	    fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
	    optfree(opts);
	    return 1;
	}
    }

    logg_lock = !optget(opts, "LogFileUnlock")->enabled;
    logg_time = optget(opts, "LogTime")->enabled;
    logg_size = optget(opts, "LogFileMaxSize")->numarg;
    logg_verbose = mprintf_verbose = optget(opts, "LogVerbose")->enabled;

    if((opt = optget(opts, "LogFile"))->enabled) {
	logg_file = opt->strarg;
	if(strlen(logg_file) < 2 || logg_file[0] != '/') {
	    fprintf(stderr, "ERROR: LogFile requires full path.\n");
	    logg_close();
	    optfree(opts);
	    return 1;
	}
    } else
	logg_file = NULL;

#if defined(USE_SYSLOG) && !defined(C_AIX)
    if(optget(opts, "LogSyslog")->enabled) {
	int fac;

	opt = optget(opts, "LogFacility");
	if((fac = logg_facility(opt->strarg)) == -1) {
	    logg("!LogFacility: %s: No such facility.\n", opt->strarg);
	    logg_close();
	    optfree(opts);
	    return 1;
	}

	openlog("clamav-milter", LOG_PID, fac);
	logg_syslog = 1;
    }
#endif

    time(&currtime);
    if(logg("#+++ Started at %s", ctime(&currtime))) {
	fprintf(stderr, "ERROR: Can't initialize the internal logger\n");
	logg_close();
	optfree(opts);
	return 1;
    }
    if((opt = optget(opts, "TemporaryDirectory"))->enabled)
	tempdir = opt->strarg;

    if(localnets_init(opts) || init_actions(opts)) {
	logg_close();
	optfree(opts);
	return 1;
    }

    if((opt = optget(opts, "Whitelist"))->enabled && whitelist_init(opt->strarg)) {
	localnets_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    if((opt = optget(opts, "SkipAuthenticated"))->enabled && smtpauth_init(opt->strarg)) {
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    pt = optget(opts, "AddHeader")->strarg;
    if(strcasecmp(pt, "No")) {
	char myname[255];

	if(!gethostname(myname, sizeof(myname))) {
	    myname[sizeof(myname)-1] = '\0';
	    snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s at %s", get_version(), myname);
	    xvirushdr[sizeof(xvirushdr)-1] = '\0';
	} else {
	    snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s", get_version());
	    xvirushdr[sizeof(xvirushdr)-1] = '\0';
	}

	descr.xxfi_flags |= SMFIF_ADDHDRS;

	if(strcasecmp(pt, "Add")) { /* Replace or Yes */
	    descr.xxfi_flags |= SMFIF_CHGHDRS;
	    addxvirus = 1;
	} else { /* Add */
	    addxvirus = 2;
	}
    }
    
    if(!(my_socket = optget(opts, "MilterSocket")->strarg)) {
	logg("!Please configure the MilterSocket directive\n");
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    if(!optget(opts, "Foreground")->enabled) {
	if(daemonize() == -1) {
	    logg("!daemonize() failed\n");
	    localnets_free();
	    whitelist_free();
	    cpool_free();
	    logg_close();
	    optfree(opts);
	    return 1;
	}
	if(chdir("/") == -1)
	    logg("^Can't change current working directory to root\n");
    }

    if(smfi_setconn(my_socket) == MI_FAILURE) {
	logg("!smfi_setconn failed\n");
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }
    if(smfi_register(descr) == MI_FAILURE) {
	logg("!smfi_register failed\n");
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }
    opt = optget(opts, "FixStaleSocket");
    if(smfi_opensocket(opt->enabled) == MI_FAILURE) {
	logg("!Failed to create socket %s\n", my_socket);
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }

    maxfilesize = optget(opts, "MaxFileSize")->numarg;
    readtimeout = optget(opts, "ReadTimeout")->numarg;

    cpool_init(opts);
    if (!cp) {
	logg("!Failed to init the socket pool\n");
	localnets_free();
	whitelist_free();
	logg_close();
	optfree(opts);
	return 1;
    }	

    if((opt = optget(opts, "PidFile"))->enabled) {
	FILE *fd;
	mode_t old_umask = umask(0006);

	if((fd = fopen(opt->strarg, "w")) == NULL) {
	    logg("!Can't save PID in file %s\n", opt->strarg);
	} else {
	    if (fprintf(fd, "%u", (unsigned int)getpid())<0) {
	    	logg("!Can't save PID in file %s\n", opt->strarg);
	    }
	    fclose(fd);
	}
	umask(old_umask);
    }

    ret = smfi_main();

    optfree(opts);

    logg_close();
    cpool_free();
    localnets_free();
    whitelist_free();

    return ret;
}
Example #8
0
int main(int argc, char **argv)
{
	int ds, dms, ret;
	double mb, rmb;
	struct timeval t1, t2;
#ifndef C_WINDOWS
	struct timezone tz;
	sigset_t sigset;
#endif
	struct optstruct *opts;
	const struct optstruct *opt;

#if defined(C_WINDOWS) && defined(CL_THREAD_SAFE)
    if(!pthread_win32_process_attach_np()) {
	mprintf("!Can't start the win32 pthreads layer\n");
	return 72;
    }
#endif

#if !defined(C_WINDOWS) && !defined(C_BEOS)
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGXFSZ);
    sigprocmask(SIG_SETMASK, &sigset, NULL);
#endif


    if((opts = optparse(NULL, argc, argv, 1, OPT_CLAMSCAN, 0, NULL)) == NULL) {
	mprintf("!Can't parse command line options\n");
	return 40;
    }

    if(optget(opts, "verbose")->enabled) {
	mprintf_verbose = 1;
	logg_verbose = 1;
    }

    if(optget(opts, "quiet")->enabled)
	mprintf_quiet = 1;

    if(optget(opts, "stdout")->enabled)
	mprintf_stdout = 1;


    if(optget(opts, "debug")->enabled) {
#if defined(C_LINUX)
	    /* [email protected]: create a dump if needed */
	    struct rlimit rlim;

	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
	if(setrlimit(RLIMIT_CORE, &rlim) < 0)
	    perror("setrlimit");
#endif
	cl_debug(); /* enable debug messages */
    }

    if(optget(opts, "version")->enabled) {
	print_version(optget(opts, "database")->strarg);
	optfree(opts);
	return 0;
    }

    if(optget(opts, "help")->enabled) {
	optfree(opts);
    	help();
	return 0;
    }

    if(optget(opts, "recursive")->enabled)
	recursion = 1;

    if(optget(opts, "infected")->enabled)
	printinfected = 1;

    if(optget(opts, "bell")->enabled)
	bell = 1;

    /* initialize logger */
    if((opt = optget(opts, "log"))->enabled) {
	logg_file = opt->strarg;
	if(logg("#\n-------------------------------------------------------------------------------\n\n")) {
	    mprintf("!Problem with internal logger.\n");
	    optfree(opts);
	    return 62;
	}
    } else 
	logg_file = NULL;

    if(actsetup(opts)) {
	optfree(opts);
	logg_close();
	exit(2);
    }

    memset(&info, 0, sizeof(struct s_info));

#ifdef C_WINDOWS
    _set_fmode(_O_BINARY);
#ifdef CL_DEBUG
    {
	_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
	_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
    }
#endif	
    gettimeofday(&t1, NULL);
#else
    gettimeofday(&t1, &tz);
#endif

    ret = scanmanager(opts);

    if(!optget(opts, "no-summary")->enabled) {
#ifdef C_WINDOWS
	gettimeofday(&t2, NULL);
#else
	gettimeofday(&t2, &tz);
#endif
	ds = t2.tv_sec - t1.tv_sec;
	dms = t2.tv_usec - t1.tv_usec;
	ds -= (dms < 0) ? (1):(0);
	dms += (dms < 0) ? (1000000):(0);
	logg("\n----------- SCAN SUMMARY -----------\n");
	logg("Known viruses: %u\n", info.sigs);
	logg("Engine version: %s\n", get_version());
	logg("Scanned directories: %u\n", info.dirs);
	logg("Scanned files: %u\n", info.files);
	logg("Infected files: %u\n", info.ifiles);
	if(notremoved) {
	    logg("Not removed: %u\n", notremoved);
	}
	if(notmoved) {
	    logg("Not %s: %u\n", optget(opts, "copy")->enabled ? "moved" : "copied", notmoved);
	}
	mb = info.blocks * (CL_COUNT_PRECISION / 1024) / 1024.0;
	logg("Data scanned: %2.2lf MB\n", mb);
	rmb = info.rblocks * (CL_COUNT_PRECISION / 1024) / 1024.0;
	logg("Data read: %2.2lf MB (ratio %.2f:1)\n", rmb, info.rblocks ? (double)info.blocks/(double)info.rblocks : 0);
	logg("Time: %u.%3.3u sec (%u m %u s)\n", ds, dms/1000, ds/60, ds%60);
    }

    optfree(opts);

#if defined(C_WINDOWS) && defined(CL_THREAD_SAFE)
    if(!pthread_win32_process_detach_np()) {
	logg("!Can't stop the win32 pthreads layer\n");
	return 72;
    }
#endif

    return ret;
}
Example #9
0
int acceptloop_th(int socketd, struct cl_node *root, const struct cfgstruct *copt)
{
	int new_sd, max_threads, stdopt;
	unsigned int options = 0;
	threadpool_t *thr_pool;
	struct sigaction sigact;
	mode_t old_umask;
	struct cl_limits limits;
	sigset_t sigset;
	client_conn_t *client_conn;
	struct cfgstruct *cpt;
#ifdef HAVE_STRERROR_R
	char buff[BUFFSIZE + 1];
#endif
	unsigned int selfchk;
	time_t start_time, current_time;
	pid_t mainpid;
	int idletimeout;

#if defined(C_BIGSTACK) || defined(C_BSD)
        size_t stacksize;
#endif

#ifdef CLAMUKO
	pthread_t clamuko_pid;
	pthread_attr_t clamuko_attr;
	struct thrarg *tharg = NULL; /* shut up gcc */
#endif
	memset(&sigact, 0, sizeof(struct sigaction));

    /* save the PID */
    mainpid = getpid();
    if((cpt = cfgopt(copt, "PidFile"))) {
	    FILE *fd;
	old_umask = umask(0006);
	if((fd = fopen(cpt->strarg, "w")) == NULL) {
	    logg("!Can't save PID in file %s\n", cpt->strarg);
	} else {
	    fprintf(fd, "%d", (int) mainpid);
	    fclose(fd);
	}
	umask(old_umask);
    }

    logg("*Listening daemon: PID: %d\n", getpid());
    if((cpt = cfgopt(copt, "MaxThreads"))) {
	max_threads = cpt->numarg;
    } else {
	max_threads = CL_DEFAULT_MAXTHREADS;
    }

    if(cfgopt(copt, "DisableDefaultScanOptions")) {
	logg("RECOMMENDED OPTIONS DISABLED.\n");
	stdopt = 0;
    } else {
	options |= CL_SCAN_STDOPT;
	stdopt = 1;
    }

    if(stdopt || cfgopt(copt, "ScanArchive") || cfgopt(copt, "ClamukoScanArchive")) {

	/* set up limits */
	memset(&limits, 0, sizeof(struct cl_limits));

	if((cpt = cfgopt(copt, "ArchiveMaxFileSize"))) {
	    if((limits.maxfilesize = cpt->numarg)) {
		logg("Archive: Archived file size limit set to %d bytes.\n", limits.maxfilesize);
	    } else {
		logg("^Archive: File size limit protection disabled.\n");
	    }
	} else {
	    limits.maxfilesize = 10485760;
	    logg("Archive: Archived file size limit set to %d bytes.\n", limits.maxfilesize);
	}

	if((cpt = cfgopt(copt, "ArchiveMaxRecursion"))) {
	    if((limits.maxreclevel = cpt->numarg)) {
		logg("Archive: Recursion level limit set to %d.\n", limits.maxreclevel);
	    } else {
		logg("^Archive: Recursion level limit protection disabled.\n");
	    }
	} else {
	    limits.maxreclevel = 8;
	    logg("Archive: Recursion level limit set to %d.\n", limits.maxreclevel);
	}

	if((cpt = cfgopt(copt, "ArchiveMaxFiles"))) {
	    if((limits.maxfiles = cpt->numarg)) {
		logg("Archive: Files limit set to %d.\n", limits.maxfiles);
	    } else {
		logg("^Archive: Files limit protection disabled.\n");
	    }
	} else {
	    limits.maxfiles = 1000;
	    logg("Archive: Files limit set to %d.\n", limits.maxfiles);
	}

	if((cpt = cfgopt(copt, "ArchiveMaxCompressionRatio"))) {
	    if((limits.maxratio = cpt->numarg)) {
		logg("Archive: Compression ratio limit set to %d.\n", limits.maxratio);
	    } else {
		logg("^Archive: Compression ratio limit disabled.\n");
	    }
	} else {
	    limits.maxratio = 250;
	    logg("Archive: Compression ratio limit set to %d.\n", limits.maxratio);
	}

	if(cfgopt(copt, "ArchiveLimitMemoryUsage")) {
	    limits.archivememlim = 1;
	    logg("Archive: Limited memory usage.\n");
	} else {
	    limits.archivememlim = 0;
	}

    }

    if(stdopt || cfgopt(copt, "ScanArchive")) {
	logg("Archive support enabled.\n");
	options |= CL_SCAN_ARCHIVE;

	if(cfgopt(copt, "ScanRAR")) {
	    logg("Archive: RAR support enabled.\n");
	} else {
	    logg("Archive: RAR support disabled.\n");
	    options |= CL_SCAN_DISABLERAR;
	}

	if(cfgopt(copt, "ArchiveBlockEncrypted")) {
	    logg("Archive: Blocking encrypted archives.\n");
	    options |= CL_SCAN_BLOCKENCRYPTED;
	}

	if(cfgopt(copt, "ArchiveBlockMax")) {
	    logg("Archive: Blocking archives that exceed limits.\n");
	    options |= CL_SCAN_BLOCKMAX;
	}

    } else {
	logg("Archive support disabled.\n");
    }

    if(stdopt || cfgopt(copt, "ScanPE")) {
	logg("Portable Executable support enabled.\n");
	options |= CL_SCAN_PE;

	if(cfgopt(copt, "DetectBrokenExecutables")) {
	    logg("Detection of broken executables enabled.\n");
	    options |= CL_SCAN_BLOCKBROKEN;
	}

    } else {
	logg("Portable Executable support disabled.\n");
    }

    if(stdopt || cfgopt(copt, "ScanMail")) {
	logg("Mail files support enabled.\n");
	options |= CL_SCAN_MAIL;

	if(cfgopt(copt, "MailFollowURLs")) {
	    logg("Mail: URL scanning enabled.\n");
	    options |= CL_SCAN_MAILURL;
	}

    } else {
	logg("Mail files support disabled.\n");
    }

    if(stdopt || cfgopt(copt, "ScanOLE2")) {
	logg("OLE2 support enabled.\n");
	options |= CL_SCAN_OLE2;
    } else {
	logg("OLE2 support disabled.\n");
    }

    if(stdopt || cfgopt(copt, "ScanHTML")) {
	logg("HTML support enabled.\n");
	options |= CL_SCAN_HTML;
    } else {
	logg("HTML support disabled.\n");
    }

    if((cpt = cfgopt(copt, "SelfCheck"))) {
	selfchk = cpt->numarg;
    } else {
	selfchk = CL_DEFAULT_SELFCHECK;
    }

    if(!selfchk) {
	logg("Self checking disabled.\n");
    } else {
	logg("Self checking every %d seconds.\n", selfchk);
    }

    if(cfgopt(copt, "ClamukoScanOnLine") || cfgopt(copt, "ClamukoScanOnAccess"))
#ifdef CLAMUKO
    {
	pthread_attr_init(&clamuko_attr);
	pthread_attr_setdetachstate(&clamuko_attr, PTHREAD_CREATE_JOINABLE);

	tharg = (struct thrarg *) mmalloc(sizeof(struct thrarg));
	tharg->copt = copt;
	tharg->root = root;
	tharg->limits = &limits;
	tharg->options = options;

	pthread_create(&clamuko_pid, &clamuko_attr, clamukoth, tharg);
    }
#else
	logg("Clamuko is not available.\n");
#endif

    /* set up signal handling */
    sigfillset(&sigset);
    sigdelset(&sigset, SIGINT);
    sigdelset(&sigset, SIGTERM);
    sigdelset(&sigset, SIGSEGV);
    sigdelset(&sigset, SIGHUP);
    sigdelset(&sigset, SIGPIPE);
    sigdelset(&sigset, SIGUSR2);
    sigprocmask(SIG_SETMASK, &sigset, NULL);
 
    /* SIGINT, SIGTERM, SIGSEGV */
    sigact.sa_handler = sighandler_th;
    sigemptyset(&sigact.sa_mask);
    sigaddset(&sigact.sa_mask, SIGINT);
    sigaddset(&sigact.sa_mask, SIGTERM);
    sigaddset(&sigact.sa_mask, SIGHUP);
    sigaddset(&sigact.sa_mask, SIGPIPE);
    sigaddset(&sigact.sa_mask, SIGUSR2);
    sigaction(SIGINT, &sigact, NULL);
    sigaction(SIGTERM, &sigact, NULL);
    sigaction(SIGHUP, &sigact, NULL);
    sigaction(SIGPIPE, &sigact, NULL);
    sigaction(SIGUSR2, &sigact, NULL);

    if(!debug_mode) {
	sigaddset(&sigact.sa_mask, SIGHUP);
	sigaction(SIGSEGV, &sigact, NULL);
    }

    pthread_mutex_init(&exit_mutex, NULL);
    pthread_mutex_init(&reload_mutex, NULL);


    if((cpt = cfgopt(copt, "IdleTimeout")))
	idletimeout = cpt->numarg;
    else
	idletimeout = CL_DEFAULT_IDLETIMEOUT;

    if((thr_pool=thrmgr_new(max_threads, idletimeout, scanner_thread)) == NULL) {
	logg("!thrmgr_new failed\n");
	exit(-1);
    }

    time(&start_time);

    for(;;) {				
	new_sd = accept(socketd, NULL, NULL);
	if((new_sd == -1) && (errno != EINTR)) {
	    /* very bad - need to exit or restart */
#ifdef HAVE_STRERROR_R
	    logg("!accept() failed: %s\n", strerror_r(errno, buff, BUFFSIZE));
#else
	    logg("!accept() failed\n");
#endif
	    continue;
	}

	if (sighup) {
		logg("SIGHUP caught: re-opening log file.\n");
		logg_close();
		sighup = 0;
		if(!logg_file && (cpt = cfgopt(copt, "LogFile")))
		    logg_file = cpt->strarg;
	}

	if (!progexit && new_sd >= 0) {
		client_conn = (client_conn_t *) mmalloc(sizeof(struct client_conn_tag));
		client_conn->sd = new_sd;
		client_conn->options = options;
		client_conn->copt = copt;
		client_conn->root = cl_dup(root);
		client_conn->root_timestamp = reloaded_time;
		client_conn->limits = &limits;
		client_conn->mainpid = mainpid;
		if (!thrmgr_dispatch(thr_pool, client_conn)) {
		    close(client_conn->sd);
		    free(client_conn);
		    logg("!thread dispatch failed\n");
		}
	}

	pthread_mutex_lock(&exit_mutex);
	if(progexit) {
	    if (new_sd >= 0) {
		close(new_sd);
	    }
	    pthread_mutex_unlock(&exit_mutex);
	    break;
	}
	pthread_mutex_unlock(&exit_mutex);

	if(selfchk) {
	    time(&current_time);
	    if((current_time - start_time) > (time_t)selfchk) {
		if(reload_db(root, copt, TRUE)) {
		    pthread_mutex_lock(&reload_mutex);
		    reload = 1;
		    pthread_mutex_unlock(&reload_mutex);
		}
		time(&start_time);
	    }
	}

	pthread_mutex_lock(&reload_mutex);
	if(reload) {
	    pthread_mutex_unlock(&reload_mutex);
	    root = reload_db(root, copt, FALSE);
	    pthread_mutex_lock(&reload_mutex);
	    reload = 0;
	    time(&reloaded_time);
	    pthread_mutex_unlock(&reload_mutex);
#ifdef CLAMUKO
	    if(cfgopt(copt, "ClamukoScanOnLine") || cfgopt(copt, "ClamukoScanOnAccess")) {
		logg("Stopping and restarting Clamuko.\n");
		pthread_kill(clamuko_pid, SIGUSR1);
		pthread_join(clamuko_pid, NULL);
		tharg->root = root;
		pthread_create(&clamuko_pid, &clamuko_attr, clamukoth, tharg);
	    }
#endif
	} else {
	    pthread_mutex_unlock(&reload_mutex);
	}
    }

    /* Destroy the thread manager.
     * This waits for all current tasks to end
     */
    thrmgr_destroy(thr_pool);
#ifdef CLAMUKO
    if(cfgopt(copt, "ClamukoScanOnLine") || cfgopt(copt, "ClamukoScanOnAccess")) {
	logg("Stopping Clamuko.\n");
	pthread_kill(clamuko_pid, SIGUSR1);
	pthread_join(clamuko_pid, NULL);
    }
#endif
    cl_free(root);
    logg("*Shutting down the main socket.\n");
    shutdown(socketd, 2);
    logg("*Closing the main socket.\n");
    close(socketd);

#ifndef C_OS2
    if((cpt = cfgopt(copt, "LocalSocket"))) {
	if(unlink(cpt->strarg) == -1)
	    logg("!Can't unlink the socket file %s\n", cpt->strarg);
	else
	     logg("Socket file removed.\n");
    }
#endif

    if((cpt = cfgopt(copt, "PidFile"))) {
	if(unlink(cpt->strarg) == -1)
	    logg("!Can't unlink the pid file %s\n", cpt->strarg);
	else
	    logg("Pid file removed.\n");
    }

    logg("Exiting (clean)\n");
    time(&current_time);
    logg("--- Stopped at %s", ctime(&current_time));

    return 0;
}
Example #10
0
int main(int argc, char **argv)
{
	const struct optstruct *opt;
#ifndef	C_WINDOWS
        struct passwd *user = NULL;
#endif
	time_t currtime;
	const char *dbdir, *cfgfile;
	char *pua_cats = NULL, *pt;
	int ret, tcpsock = 0, localsock = 0, i, min_port, max_port;
	unsigned int sigs = 0;
	int lsockets[2], nlsockets = 0;
	unsigned int dboptions = 0;
#ifdef C_LINUX
	struct stat sb;
#endif

#ifdef C_WINDOWS
    if(!pthread_win32_process_attach_np()) {
	mprintf("!Can't start the win32 pthreads layer\n");
        return 1;
    }
#endif

    if((opts = optparse(NULL, argc, argv, 1, OPT_CLAMD, 0, NULL)) == NULL) {
	mprintf("!Can't parse command line options\n");
	return 1;
    }

    if(optget(opts, "help")->enabled) {
    	help();
	optfree(opts);
	return 0;
    }

    if(optget(opts, "debug")->enabled) {
#if defined(C_LINUX)
	    /* [email protected]: create a dump if needed */
	    struct rlimit rlim;

	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
	if(setrlimit(RLIMIT_CORE, &rlim) < 0)
	    perror("setrlimit");
#endif
	debug_mode = 1;
    }

    /* parse the config file */
    cfgfile = optget(opts, "config-file")->strarg;
    pt = strdup(cfgfile);
    if((opts = optparse(cfgfile, 0, NULL, 1, OPT_CLAMD, 0, opts)) == NULL) {
	fprintf(stderr, "ERROR: Can't open/parse the config file %s\n", pt);
	free(pt);
	return 1;
    }
    free(pt);

    if(optget(opts, "version")->enabled) {
	print_version(optget(opts, "DatabaseDirectory")->strarg);
	optfree(opts);
	return 0;
    }

    umask(0);

    /* drop privileges */
#if (!defined(C_OS2)) && (!defined(C_WINDOWS))
    if(geteuid() == 0 && (opt = optget(opts, "User"))->enabled) {
	if((user = getpwnam(opt->strarg)) == NULL) {
	    fprintf(stderr, "ERROR: Can't get information about user %s.\n", opt->strarg);
	    optfree(opts);
	    return 1;
	}

	if(optget(opts, "AllowSupplementaryGroups")->enabled) {
#ifdef HAVE_INITGROUPS
	    if(initgroups(opt->strarg, user->pw_gid)) {
		fprintf(stderr, "ERROR: initgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#else
	    mprintf("!AllowSupplementaryGroups: initgroups() is not available, please disable AllowSupplementaryGroups in %s\n", cfgfile);
	    optfree(opts);
	    return 1;
#endif
	} else {
#ifdef HAVE_SETGROUPS
	    if(setgroups(1, &user->pw_gid)) {
		fprintf(stderr, "ERROR: setgroups() failed.\n");
		optfree(opts);
		return 1;
	    }
#endif
	}

	if(setgid(user->pw_gid)) {
	    fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int) user->pw_gid);
	    optfree(opts);
	    return 1;
	}

	if(setuid(user->pw_uid)) {
	    fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int) user->pw_uid);
	    optfree(opts);
	    return 1;
	}
    }
#endif

    /* initialize logger */
    logg_lock = !optget(opts, "LogFileUnlock")->enabled;
    logg_time = optget(opts, "LogTime")->enabled;
    logok = optget(opts, "LogClean")->enabled;
    logg_size = optget(opts, "LogFileMaxSize")->numarg;
    logg_verbose = mprintf_verbose = optget(opts, "LogVerbose")->enabled;
    mprintf_send_timeout = optget(opts, "SendBufTimeout")->numarg;

    do { /* logger initialized */

    if((opt = optget(opts, "LogFile"))->enabled) {
	char timestr[32];
	logg_file = opt->strarg;
	if(strlen(logg_file) < 2 || (logg_file[0] != '/' && logg_file[0] != '\\' && logg_file[1] != ':')) {
	    fprintf(stderr, "ERROR: LogFile requires full path.\n");
	    ret = 1;
	    break;
	}
	time(&currtime);
	if(logg("#+++ Started at %s", cli_ctime(&currtime, timestr, sizeof(timestr)))) {
	    fprintf(stderr, "ERROR: Can't initialize the internal logger\n");
	    ret = 1;
	    break;
	}
    } else
	logg_file = NULL;

    if((ret = cl_init(CL_INIT_DEFAULT))) {
	logg("!Can't initialize libclamav: %s\n", cl_strerror(ret));
	ret = 1;
	break;
    }

    if(optget(opts, "Debug")->enabled) /* enable debug messages in libclamav */ {
	cl_debug();
	logg_verbose = 2;
    }

#if defined(USE_SYSLOG) && !defined(C_AIX)
    if(optget(opts, "LogSyslog")->enabled) {
	    int fac = LOG_LOCAL6;

	opt = optget(opts, "LogFacility");
	if((fac = logg_facility(opt->strarg)) == -1) {
	    logg("!LogFacility: %s: No such facility.\n", opt->strarg);
	    ret = 1;
	    break;
	}

	openlog("clamd", LOG_PID, fac);
	logg_syslog = 1;
    }
#endif

#ifdef C_LINUX
    procdev = 0;
    if(stat("/proc", &sb) != -1 && !sb.st_size)
	procdev = sb.st_dev;
#endif

    /* check socket type */

    if(optget(opts, "TCPSocket")->enabled)
	tcpsock = 1;

    if(optget(opts, "LocalSocket")->enabled)
	localsock = 1;

    if(!tcpsock && !localsock) {
	logg("!Please define server type (local and/or TCP).\n");
	ret = 1;
	break;
    }

    logg("#clamd daemon %s (OS: "TARGET_OS_TYPE", ARCH: "TARGET_ARCH_TYPE", CPU: "TARGET_CPU_TYPE")\n", get_version());

#ifndef C_WINDOWS
    if(user)
	logg("#Running as user %s (UID %u, GID %u)\n", user->pw_name, user->pw_uid, user->pw_gid);
#endif

    if(logg_size)
	logg("#Log file size limited to %d bytes.\n", logg_size);
    else
	logg("#Log file size limit disabled.\n");

    min_port = optget(opts, "StreamMinPort")->numarg;
    max_port = optget(opts, "StreamMaxPort")->numarg;
    if (min_port < 1024 || min_port > max_port || max_port > 65535) {
	logg("!Invalid StreamMinPort/StreamMaxPort: %d, %d\n", min_port, max_port);
	ret = 1;
	break;
    }

    if(!(engine = cl_engine_new())) {
	logg("!Can't initialize antivirus engine\n");
	ret = 1;
	break;
    }

    /* load the database(s) */
    dbdir = optget(opts, "DatabaseDirectory")->strarg;
    logg("#Reading databases from %s\n", dbdir);

    if(optget(opts, "DetectPUA")->enabled) {
	dboptions |= CL_DB_PUA;

	if((opt = optget(opts, "ExcludePUA"))->enabled) {
	    dboptions |= CL_DB_PUA_EXCLUDE;
	    i = 0;
	    logg("#Excluded PUA categories:");
	    while(opt) {
		if(!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
		    logg("!Can't allocate memory for pua_cats\n");
		    cl_engine_free(engine);
		    ret = 1;
		    break;
		}
		logg("# %s", opt->strarg);
		sprintf(pua_cats + i, ".%s", opt->strarg);
		i += strlen(opt->strarg) + 1;
		pua_cats[i] = 0;
		opt = opt->nextarg;
	    }
	    if (ret)
		break;
	    logg("#\n");
	    pua_cats[i] = '.';
	    pua_cats[i + 1] = 0;
	}

	if((opt = optget(opts, "IncludePUA"))->enabled) {
	    if(pua_cats) {
		logg("!ExcludePUA and IncludePUA cannot be used at the same time\n");
		free(pua_cats);
		ret = 1;
		break;
	    }
	    dboptions |= CL_DB_PUA_INCLUDE;
	    i = 0;
	    logg("#Included PUA categories:");
	    while(opt) {
		if(!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
		    logg("!Can't allocate memory for pua_cats\n");
		    ret = 1;
		    break;
		}
		logg("# %s", opt->strarg);
		sprintf(pua_cats + i, ".%s", opt->strarg);
		i += strlen(opt->strarg) + 1;
		pua_cats[i] = 0;
		opt = opt->nextarg;
	    }
	    if (ret)
		break;
	    logg("#\n");
	    pua_cats[i] = '.';
	    pua_cats[i + 1] = 0;
	}

	if(pua_cats) {
	    if((ret = cl_engine_set_str(engine, CL_ENGINE_PUA_CATEGORIES, pua_cats))) {
		logg("!cli_engine_set_str(CL_ENGINE_PUA_CATEGORIES) failed: %s\n", cl_strerror(ret));
		free(pua_cats);
		ret = 1;
		break;
	    }
	    free(pua_cats);
	}
    } else {
	logg("#Not loading PUA signatures.\n");
    }

    /* set the temporary dir */
    if((opt = optget(opts, "TemporaryDirectory"))->enabled) {
	if((ret = cl_engine_set_str(engine, CL_ENGINE_TMPDIR, opt->strarg))) {
	    logg("!cli_engine_set_str(CL_ENGINE_TMPDIR) failed: %s\n", cl_strerror(ret));
	    ret = 1;
	    break;
	}
    }

    if(optget(opts, "LeaveTemporaryFiles")->enabled)
	cl_engine_set_num(engine, CL_ENGINE_KEEPTMP, 1);

    if(optget(opts, "PhishingSignatures")->enabled)
	dboptions |= CL_DB_PHISHING;
    else
	logg("#Not loading phishing signatures.\n");

    if(optget(opts,"PhishingScanURLs")->enabled)
	dboptions |= CL_DB_PHISHING_URLS;
    else
	logg("#Disabling URL based phishing detection.\n");

    if(optget(opts,"DevACOnly")->enabled) {
	logg("#Only using the A-C matcher.\n");
	cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
    }

    if((opt = optget(opts, "DevACDepth"))->enabled) {
        cl_engine_set_num(engine, CL_ENGINE_AC_MAXDEPTH, opt->numarg);
	logg("#Max A-C depth set to %u\n", (unsigned int) opt->numarg);
    }

    if((ret = cl_load(dbdir, engine, &sigs, dboptions))) {
	logg("!%s\n", cl_strerror(ret));
	ret = 1;
	break;
    }

    logg("#Loaded %u signatures.\n", sigs);
    if((ret = cl_engine_compile(engine)) != 0) {
	logg("!Database initialization error: %s\n", cl_strerror(ret));
	ret = 1;
	break;
    }

    if(tcpsock) {
#ifdef C_WINDOWS
	    WSADATA wsaData;

	if(WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR) {
	    logg("!Error at WSAStartup(): %d\n", WSAGetLastError());
	    ret = 1;
	    break;
	}
#endif
	if ((lsockets[nlsockets] = tcpserver(opts)) == -1) {
	    ret = 1;
	    break;
	}
	nlsockets++;
    }

    if(localsock) {
	if ((lsockets[nlsockets] = localserver(opts)) == -1) {
	    ret = 1;
	    break;
	}
	nlsockets++;
    }

    /* fork into background */
    if(!optget(opts, "Foreground")->enabled) {
#ifdef C_BSD	    
	/* workaround for OpenBSD bug, see https://wwws.clamav.net/bugzilla/show_bug.cgi?id=885 */
	for(ret=0;ret<nlsockets;ret++) {
		fcntl(lsockets[ret], F_SETFL, fcntl(lsockets[ret], F_GETFL) | O_NONBLOCK);
	}
#endif
	if(daemonize() == -1) {
	    logg("!daemonize() failed\n");
	    ret = 1;
	    break;
	}
#ifdef C_BSD
	for(ret=0;ret<nlsockets;ret++) {
		fcntl(lsockets[ret], F_SETFL, fcntl(lsockets[ret], F_GETFL) & ~O_NONBLOCK);
	}
#endif
	if(!debug_mode)
	    if(chdir("/") == -1)
		logg("^Can't change current working directory to root\n");

    } else
        foreground = 1;

    ret = recvloop_th(lsockets, nlsockets, engine, dboptions, opts);

    } while (0);

    logg("*Closing the main socket%s.\n", (nlsockets > 1) ? "s" : "");

    for (i = 0; i < nlsockets; i++) {
	closesocket(lsockets[i]);
    }

#ifndef C_OS2
    if(nlsockets && localsock) {
	opt = optget(opts, "LocalSocket");
	if(unlink(opt->strarg) == -1)
	    logg("!Can't unlink the socket file %s\n", opt->strarg);
	else
	    logg("Socket file removed.\n");
    }
#endif

#ifdef C_WINDOWS
    if(tcpsock)
	WSACleanup();

    if(!pthread_win32_process_detach_np()) {
	logg("!Can't stop the win32 pthreads layer\n");
	logg_close();
	optfree(opts);
	return 1;
    }
#endif

    logg_close();
    optfree(opts);

    return ret;
}