1 /*
2 * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
3 * Copyright (C) 2007-2013 Sourcefire, Inc.
4 *
5 * Authors: Tomasz Kojm
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #if HAVE_CONFIG_H
23 #include "clamav-config.h"
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #ifndef _WIN32
33 #include <sys/time.h>
34 #include <sys/resource.h>
35 #endif
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <time.h>
40 #ifdef HAVE_PWD_H
41 #include <pwd.h>
42 #endif
43 #ifdef HAVE_GRP_H
44 #include <grp.h>
45 #endif
46 #include <signal.h>
47 #include <errno.h>
48 #include <locale.h>
49
50 #if defined(USE_SYSLOG) && !defined(C_AIX)
51 #include <syslog.h>
52 #endif
53
54 #ifdef C_LINUX
55 #include <sys/resource.h>
56 #endif
57
58 #include "target.h"
59
60 // libclamav
61 #include "clamav.h"
62 #include "others.h"
63 #include "matcher-ac.h"
64 #include "readdb.h"
65
66 // common
67 #include "output.h"
68 #include "optparser.h"
69 #include "misc.h"
70
71 #include "server.h"
72 #include "tcpserver.h"
73 #include "localserver.h"
74 #include "clamd_others.h"
75 #include "shared.h"
76 #include "scanner.h"
77
78 #ifdef _WIN32
79 #include "service.h"
80 #endif
81
82 #include <sys/types.h>
83 #ifndef WIN32
84 #include <sys/wait.h>
85 #endif
86
87 short debug_mode = 0, logok = 0;
88 short foreground = -1;
89
help(void)90 static void help(void)
91 {
92 printf("\n");
93 printf(" Clam AntiVirus: Daemon %s\n", get_version());
94 printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n");
95 printf(" (C) 2022 Cisco Systems, Inc.\n");
96 printf("\n");
97 printf(" clamd [options]\n");
98 printf("\n");
99 printf(" --help -h Show this help\n");
100 printf(" --version -V Show version number\n");
101 #ifdef _WIN32
102 printf(" --install-service Install Windows Service\n");
103 printf(" --uninstall-service Uninstall Windows Service\n");
104 #endif
105 printf(" --foreground -F Run in foreground; do not daemonize\n");
106 printf(" --debug Enable debug mode\n");
107 printf(" --log=FILE -l FILE Log into FILE\n");
108 printf(" --config-file=FILE -c FILE Read configuration from FILE\n");
109 printf("\n");
110 printf("Pass in - as the filename for stdin.\n");
111 printf("\n");
112 }
113
114 static struct optstruct *opts;
115
116 /* When running under valgrind and daemonizing, valgrind incorrectly reports
117 * leaks from the engine, because it can't see that all the memory is still
118 * reachable (some pointers are stored mangled in the JIT).
119 * So free the engine on exit from the parent too (during daemonize)
120 */
121 static struct cl_engine *gengine = NULL;
free_engine(void)122 static void free_engine(void)
123 {
124 if (gengine) {
125 cl_engine_free(gengine);
126 gengine = NULL;
127 }
128 }
129
main(int argc,char ** argv)130 int main(int argc, char **argv)
131 {
132 static struct cl_engine *engine = NULL;
133 const struct optstruct *opt;
134 #ifndef _WIN32
135 struct passwd *user = NULL;
136 struct sigaction sa;
137 int dropPrivRet = 0;
138 #endif
139 #if defined(C_LINUX) || (defined(RLIMIT_DATA) && defined(C_BSD))
140 struct rlimit rlim;
141 #endif
142 time_t currtime;
143 const char *dbdir, *cfgfile;
144 char *pua_cats = NULL, *pt;
145 int ret, tcpsock = 0, localsock = 0, min_port, max_port;
146 unsigned int sigs = 0;
147 int *lsockets = NULL;
148 unsigned int nlsockets = 0;
149 unsigned int dboptions = 0;
150 unsigned int i;
151 int j;
152 int num_fd;
153 pid_t parentPid = getpid();
154 #ifdef C_LINUX
155 STATBUF sb;
156 #endif
157 pid_t mainpid = 0;
158 mode_t old_umask = 0;
159 const char *user_name = NULL;
160
161 if (check_flevel())
162 exit(1);
163
164 #ifndef _WIN32
165 memset(&sa, 0, sizeof(sa));
166 sa.sa_handler = SIG_IGN;
167 sigaction(SIGHUP, &sa, NULL);
168 sigaction(SIGUSR2, &sa, NULL);
169 if (!setlocale(LC_CTYPE, "")) {
170 mprintf("^Failed to set locale\n");
171 }
172 #endif
173
174 if ((opts = optparse(NULL, argc, argv, 1, OPT_CLAMD, 0, NULL)) == NULL) {
175 mprintf("!Can't parse command line options\n");
176 return 1;
177 }
178
179 if (optget(opts, "help")->enabled) {
180 help();
181 optfree(opts);
182 return 0;
183 }
184
185 if (optget(opts, "debug")->enabled) {
186 #if defined(C_LINUX)
187 /* njh@bandsman.co.uk: create a dump if needed */
188 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
189 if (setrlimit(RLIMIT_CORE, &rlim) < 0)
190 perror("setrlimit");
191 #endif
192 debug_mode = 1;
193 }
194
195 /* check foreground option from command line to override config file */
196 for (j = 0; j < argc; j += 1) {
197 if ((memcmp(argv[j], "--foreground", 12) == 0) || (memcmp(argv[j], "-F", 2) == 0)) {
198 /* found */
199 break;
200 }
201 }
202
203 if (j < argc) {
204 if (optget(opts, "Foreground")->enabled) {
205 foreground = 1;
206 } else {
207 foreground = 0;
208 }
209 }
210
211 num_fd = sd_listen_fds(0);
212
213 /* parse the config file */
214 cfgfile = optget(opts, "config-file")->strarg;
215 pt = strdup(cfgfile);
216 if (pt == NULL) {
217 fprintf(stderr, "ERROR: Unable to allocate memory for config file\n");
218 return 1;
219 }
220 if ((opts = optparse(cfgfile, 0, NULL, 1, OPT_CLAMD, 0, opts)) == NULL) {
221 fprintf(stderr, "ERROR: Can't open/parse the config file %s\n", pt);
222 free(pt);
223 return 1;
224 }
225 free(pt);
226
227 if ((opt = optget(opts, "User"))->enabled) {
228 user_name = opt->strarg;
229 }
230
231 if (optget(opts, "version")->enabled) {
232 print_version(optget(opts, "DatabaseDirectory")->strarg);
233 optfree(opts);
234 return 0;
235 }
236
237 /* initialize logger */
238 logg_lock = !optget(opts, "LogFileUnlock")->enabled;
239 logg_time = optget(opts, "LogTime")->enabled;
240 logok = optget(opts, "LogClean")->enabled;
241 logg_size = optget(opts, "LogFileMaxSize")->numarg;
242 logg_verbose = mprintf_verbose = optget(opts, "LogVerbose")->enabled;
243 if (logg_size)
244 logg_rotate = optget(opts, "LogRotate")->enabled;
245 mprintf_send_timeout = optget(opts, "SendBufTimeout")->numarg;
246
247 if ((opt = optget(opts, "LogFile"))->enabled) {
248 char timestr[32];
249 logg_file = opt->strarg;
250 if (!cli_is_abspath(logg_file)) {
251 fprintf(stderr, "ERROR: LogFile requires full path.\n");
252 ret = 1;
253 return ret;
254 }
255 time(&currtime);
256 if (logg("#+++ Started at %s", cli_ctime(&currtime, timestr, sizeof(timestr)))) {
257 fprintf(stderr, "ERROR: Can't initialize the internal logger\n");
258 ret = 1;
259 return ret;
260 }
261 } else {
262 logg_file = NULL;
263 }
264
265 #ifndef WIN32
266 /* fork into background */
267 if (foreground == -1) {
268 if (optget(opts, "Foreground")->enabled) {
269 foreground = 1;
270 } else {
271 foreground = 0;
272 }
273 }
274 if (foreground == 0) {
275 int daemonizeRet = 0;
276 #ifdef C_BSD
277 /* workaround for OpenBSD bug, see https://wwws.clamav.net/bugzilla/show_bug.cgi?id=885 */
278 for (ret = 0; (unsigned int)ret < nlsockets; ret++) {
279 if (fcntl(lsockets[ret], F_SETFL, fcntl(lsockets[ret], F_GETFL) | O_NONBLOCK) == -1) {
280 logg("!fcntl for lsockets[] failed\n");
281 close(lsockets[ret]);
282 ret = 1;
283 break;
284 }
285 }
286 #endif
287 gengine = engine;
288 atexit(free_engine);
289 daemonizeRet = daemonize_parent_wait(user_name, logg_file);
290 if (daemonizeRet < 0) {
291 logg("!daemonize() failed: %s\n", strerror(errno));
292 return 1;
293 }
294 gengine = NULL;
295 #ifdef C_BSD
296 for (ret = 0; (unsigned int)ret < nlsockets; ret++) {
297 if (fcntl(lsockets[ret], F_SETFL, fcntl(lsockets[ret], F_GETFL) & ~O_NONBLOCK) == -1) {
298 logg("!fcntl for lsockets[] failed\n");
299 close(lsockets[ret]);
300 ret = 1;
301 break;
302 }
303 }
304 #endif
305 }
306
307 #endif
308
309 /* save the PID */
310 mainpid = getpid();
311 if ((opt = optget(opts, "PidFile"))->enabled) {
312 FILE *fd;
313 old_umask = umask(0022);
314 if ((fd = fopen(opt->strarg, "w")) == NULL) {
315 //logg("!Can't save PID in file %s\n", opt->strarg);
316 logg("!Can't save PID to file %s: %s\n", opt->strarg, strerror(errno));
317 exit(2);
318 } else {
319 if (fprintf(fd, "%u\n", (unsigned int)mainpid) < 0) {
320 logg("!Can't save PID to file %s: %s\n", opt->strarg, strerror(errno));
321 //logg("!Can't save PID in file %s\n", opt->strarg);
322 fclose(fd);
323 exit(2);
324 }
325 fclose(fd);
326 }
327 umask(old_umask);
328
329 #ifndef _WIN32
330 /*If the file has already been created by a different user, it will just be
331 * rewritten by us, but not change the ownership, so do that explicitly.
332 */
333 if (0 == geteuid()) {
334 struct passwd *pw = getpwuid(0);
335 int ret = lchown(opt->strarg, pw->pw_uid, pw->pw_gid);
336 if (ret) {
337 logg("!Can't change ownership of PID file %s '%s'\n", opt->strarg, strerror(errno));
338 exit(2);
339 }
340 }
341 #endif /* _WIN32 */
342 }
343
344 #ifdef _WIN32
345
346 if (optget(opts, "install-service")->enabled) {
347 svc_install("clamd", "ClamAV ClamD",
348 "Provides virus scanning facilities for ClamAV");
349 optfree(opts);
350 return 0;
351 }
352
353 if (optget(opts, "uninstall-service")->enabled) {
354 svc_uninstall("clamd", 1);
355 optfree(opts);
356 return 0;
357 }
358 #endif
359
360 /* drop privileges */
361 #ifndef _WIN32
362 dropPrivRet = drop_privileges(user_name, logg_file);
363 if (dropPrivRet) {
364 optfree(opts);
365 return dropPrivRet;
366 }
367 #endif /* _WIN32 */
368
369 do { /* logger initialized */
370
371 if (optget(opts, "DevLiblog")->enabled)
372 cl_set_clcb_msg(msg_callback);
373
374 if ((ret = cl_init(CL_INIT_DEFAULT))) {
375 logg("!Can't initialize libclamav: %s\n", cl_strerror(ret));
376 ret = 1;
377 break;
378 }
379
380 if (optget(opts, "Debug")->enabled) {
381 /* enable debug messages in libclamav */
382 cl_debug();
383 logg_verbose = 2;
384 }
385
386 #if defined(USE_SYSLOG) && !defined(C_AIX)
387 if (optget(opts, "LogSyslog")->enabled) {
388 int fac = LOG_LOCAL6;
389
390 opt = optget(opts, "LogFacility");
391 if ((fac = logg_facility(opt->strarg)) == -1) {
392 logg("!LogFacility: %s: No such facility.\n", opt->strarg);
393 ret = 1;
394 break;
395 }
396
397 openlog("clamd", LOG_PID, fac);
398 logg_syslog = 1;
399 }
400 #endif
401
402 #ifdef C_LINUX
403 procdev = 0;
404 if (CLAMSTAT("/proc", &sb) != -1 && !sb.st_size)
405 procdev = sb.st_dev;
406 #endif
407
408 /* check socket type */
409
410 if (optget(opts, "TCPSocket")->enabled)
411 tcpsock = 1;
412
413 if (optget(opts, "LocalSocket")->enabled)
414 localsock = 1;
415
416 logg("#Received %d file descriptor(s) from systemd.\n", num_fd);
417
418 if (!tcpsock && !localsock && num_fd == 0) {
419 logg("!Please define server type (local and/or TCP).\n");
420 ret = 1;
421 break;
422 }
423
424 logg("#clamd daemon %s (OS: " TARGET_OS_TYPE ", ARCH: " TARGET_ARCH_TYPE ", CPU: " TARGET_CPU_TYPE ")\n", get_version());
425
426 #ifndef _WIN32
427 if (user)
428 logg("#Running as user %s (UID %u, GID %u)\n", user->pw_name, user->pw_uid, user->pw_gid);
429 #endif
430
431 #if defined(RLIMIT_DATA) && defined(C_BSD)
432 if (getrlimit(RLIMIT_DATA, &rlim) == 0) {
433 /* bb #1941.
434 * On 32-bit FreeBSD if you set ulimit -d to >2GB then mmap() will fail
435 * too soon (after ~120 MB).
436 * Set limit lower than 2G if on 32-bit */
437 uint64_t lim = rlim.rlim_cur;
438 if (sizeof(void *) == 4 &&
439 lim > (1ULL << 31)) {
440 rlim.rlim_cur = 1ULL << 31;
441 if (setrlimit(RLIMIT_DATA, &rlim) < 0)
442 logg("!setrlimit(RLIMIT_DATA) failed: %s\n", strerror(errno));
443 else
444 logg("Running on 32-bit system, and RLIMIT_DATA > 2GB, lowering to 2GB!\n");
445 }
446 }
447 #endif
448
449 if (logg_size)
450 logg("#Log file size limited to %lld bytes.\n", (long long int)logg_size);
451 else
452 logg("#Log file size limit disabled.\n");
453
454 min_port = optget(opts, "StreamMinPort")->numarg;
455 max_port = optget(opts, "StreamMaxPort")->numarg;
456 if (min_port < 1024 || min_port > max_port || max_port > 65535) {
457 logg("!Invalid StreamMinPort/StreamMaxPort: %d, %d\n", min_port, max_port);
458 ret = 1;
459 break;
460 }
461
462 if (!(engine = cl_engine_new())) {
463 logg("!Can't initialize antivirus engine\n");
464 ret = 1;
465 break;
466 }
467
468 if (optget(opts, "disable-cache")->enabled)
469 cl_engine_set_num(engine, CL_ENGINE_DISABLE_CACHE, 1);
470
471 /* load the database(s) */
472 dbdir = optget(opts, "DatabaseDirectory")->strarg;
473 logg("#Reading databases from %s\n", dbdir);
474
475 if (optget(opts, "DetectPUA")->enabled) {
476 dboptions |= CL_DB_PUA;
477
478 if ((opt = optget(opts, "ExcludePUA"))->enabled) {
479 dboptions |= CL_DB_PUA_EXCLUDE;
480 i = 0;
481 logg("#Excluded PUA categories:");
482
483 while (opt) {
484 if (!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
485 logg("!Can't allocate memory for pua_cats\n");
486 cl_engine_free(engine);
487 ret = 1;
488 break;
489 }
490
491 logg("# %s", opt->strarg);
492
493 sprintf(pua_cats + i, ".%s", opt->strarg);
494 i += strlen(opt->strarg) + 1;
495 pua_cats[i] = 0;
496 opt = opt->nextarg;
497 }
498
499 if (ret)
500 break;
501
502 logg("#\n");
503 pua_cats[i] = '.';
504 pua_cats[i + 1] = 0;
505 }
506
507 if ((opt = optget(opts, "IncludePUA"))->enabled) {
508 if (pua_cats) {
509 logg("!ExcludePUA and IncludePUA cannot be used at the same time\n");
510 free(pua_cats);
511 ret = 1;
512 break;
513 }
514
515 dboptions |= CL_DB_PUA_INCLUDE;
516 i = 0;
517 logg("#Included PUA categories:");
518 while (opt) {
519 if (!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
520 logg("!Can't allocate memory for pua_cats\n");
521 ret = 1;
522 break;
523 }
524
525 logg("# %s", opt->strarg);
526
527 sprintf(pua_cats + i, ".%s", opt->strarg);
528 i += strlen(opt->strarg) + 1;
529 pua_cats[i] = 0;
530 opt = opt->nextarg;
531 }
532
533 if (ret)
534 break;
535
536 logg("#\n");
537 pua_cats[i] = '.';
538 pua_cats[i + 1] = 0;
539 }
540
541 if (pua_cats) {
542 if ((ret = cl_engine_set_str(engine, CL_ENGINE_PUA_CATEGORIES, pua_cats))) {
543 logg("!cli_engine_set_str(CL_ENGINE_PUA_CATEGORIES) failed: %s\n", cl_strerror(ret));
544 free(pua_cats);
545 ret = 1;
546 break;
547 }
548 free(pua_cats);
549 }
550 } else {
551 logg("#Not loading PUA signatures.\n");
552 }
553
554 if (optget(opts, "OfficialDatabaseOnly")->enabled) {
555 dboptions |= CL_DB_OFFICIAL_ONLY;
556 logg("#Only loading official signatures.\n");
557 }
558
559 /* set the temporary dir */
560 if ((opt = optget(opts, "TemporaryDirectory"))->enabled) {
561 if ((ret = cl_engine_set_str(engine, CL_ENGINE_TMPDIR, opt->strarg))) {
562 logg("!cli_engine_set_str(CL_ENGINE_TMPDIR) failed: %s\n", cl_strerror(ret));
563 ret = 1;
564 break;
565 }
566 }
567
568 cl_engine_set_clcb_hash(engine, hash_callback);
569
570 cl_engine_set_clcb_virus_found(engine, clamd_virus_found_cb);
571
572 if (optget(opts, "LeaveTemporaryFiles")->enabled)
573 cl_engine_set_num(engine, CL_ENGINE_KEEPTMP, 1);
574
575 if (optget(opts, "ForceToDisk")->enabled)
576 cl_engine_set_num(engine, CL_ENGINE_FORCETODISK, 1);
577
578 if (optget(opts, "PhishingSignatures")->enabled)
579 dboptions |= CL_DB_PHISHING;
580 else
581 logg("#Not loading phishing signatures.\n");
582
583 if (optget(opts, "Bytecode")->enabled) {
584 dboptions |= CL_DB_BYTECODE;
585 if ((opt = optget(opts, "BytecodeSecurity"))->enabled) {
586 enum bytecode_security s;
587
588 if (!strcmp(opt->strarg, "TrustSigned")) {
589 s = CL_BYTECODE_TRUST_SIGNED;
590 logg("#Bytecode: Security mode set to \"TrustSigned\".\n");
591 } else if (!strcmp(opt->strarg, "Paranoid")) {
592 s = CL_BYTECODE_TRUST_NOTHING;
593 logg("#Bytecode: Security mode set to \"Paranoid\".\n");
594 } else {
595 logg("!Unable to parse bytecode security setting:%s\n",
596 opt->strarg);
597 ret = 1;
598 break;
599 }
600
601 if ((ret = cl_engine_set_num(engine, CL_ENGINE_BYTECODE_SECURITY, s))) {
602 logg("^Invalid bytecode security setting %s: %s\n", opt->strarg, cl_strerror(ret));
603 ret = 1;
604 break;
605 }
606 }
607 if ((opt = optget(opts, "BytecodeUnsigned"))->enabled) {
608 dboptions |= CL_DB_BYTECODE_UNSIGNED;
609 logg("#Bytecode: Enabled support for unsigned bytecode.\n");
610 }
611
612 if ((opt = optget(opts, "BytecodeMode"))->enabled) {
613 enum bytecode_mode mode;
614
615 if (!strcmp(opt->strarg, "ForceJIT"))
616 mode = CL_BYTECODE_MODE_JIT;
617 else if (!strcmp(opt->strarg, "ForceInterpreter"))
618 mode = CL_BYTECODE_MODE_INTERPRETER;
619 else if (!strcmp(opt->strarg, "Test"))
620 mode = CL_BYTECODE_MODE_TEST;
621 else
622 mode = CL_BYTECODE_MODE_AUTO;
623 cl_engine_set_num(engine, CL_ENGINE_BYTECODE_MODE, mode);
624 }
625
626 if ((opt = optget(opts, "BytecodeTimeout"))->enabled) {
627 cl_engine_set_num(engine, CL_ENGINE_BYTECODE_TIMEOUT, opt->numarg);
628 }
629 } else {
630 logg("#Bytecode support disabled.\n");
631 }
632
633 if (optget(opts, "PhishingScanURLs")->enabled)
634 dboptions |= CL_DB_PHISHING_URLS;
635 else
636 logg("#Disabling URL based phishing detection.\n");
637
638 if (optget(opts, "DevACOnly")->enabled) {
639 logg("#Only using the A-C matcher.\n");
640 cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
641 }
642
643 if ((opt = optget(opts, "DevACDepth"))->enabled) {
644 cl_engine_set_num(engine, CL_ENGINE_AC_MAXDEPTH, opt->numarg);
645 logg("#Max A-C depth set to %u\n", (unsigned int)opt->numarg);
646 }
647
648 #ifdef _WIN32
649 if (optget(opts, "daemon")->enabled) {
650 cl_engine_set_clcb_sigload(engine, svc_checkpoint, NULL);
651 svc_register("clamd");
652 }
653 #endif
654
655 if ((ret = cl_load(dbdir, engine, &sigs, dboptions))) {
656 logg("!%s\n", cl_strerror(ret));
657 ret = 1;
658 break;
659 }
660
661 if ((ret = statinidir(dbdir))) {
662 logg("!%s\n", cl_strerror(ret));
663 ret = 1;
664 break;
665 }
666
667 if (optget(opts, "DisableCertCheck")->enabled)
668 cl_engine_set_num(engine, CL_ENGINE_DISABLE_PE_CERTS, 1);
669
670 logg("#Loaded %u signatures.\n", sigs);
671
672 /* pcre engine limits - required for cl_engine_compile */
673 if ((opt = optget(opts, "PCREMatchLimit"))->active) {
674 if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MATCH_LIMIT, opt->numarg))) {
675 logg("!cli_engine_set_num(PCREMatchLimit) failed: %s\n", cl_strerror(ret));
676 cl_engine_free(engine);
677 return 1;
678 }
679 }
680
681 if ((opt = optget(opts, "PCRERecMatchLimit"))->active) {
682 if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_RECMATCH_LIMIT, opt->numarg))) {
683 logg("!cli_engine_set_num(PCRERecMatchLimit) failed: %s\n", cl_strerror(ret));
684 cl_engine_free(engine);
685 return 1;
686 }
687 }
688
689 if ((ret = cl_engine_compile(engine)) != 0) {
690 logg("!Database initialization error: %s\n", cl_strerror(ret));
691 ret = 1;
692 break;
693 }
694
695 if (tcpsock || num_fd > 0) {
696 opt = optget(opts, "TCPAddr");
697 if (opt->enabled) {
698 int breakout = 0;
699
700 while (opt && opt->strarg) {
701 char *ipaddr = (!strcmp(opt->strarg, "all") ? NULL : opt->strarg);
702
703 if (tcpserver(&lsockets, &nlsockets, ipaddr, opts) == -1) {
704 ret = 1;
705 breakout = 1;
706 break;
707 }
708
709 opt = opt->nextarg;
710 }
711
712 if (breakout)
713 break;
714 } else {
715 if (tcpserver(&lsockets, &nlsockets, NULL, opts) == -1) {
716 ret = 1;
717 break;
718 }
719 }
720 }
721 #ifndef _WIN32
722 if (localsock && num_fd == 0) {
723 int *t;
724 mode_t sock_mode, umsk = umask(0777); /* socket is created with 000 to avoid races */
725
726 t = realloc(lsockets, sizeof(int) * (nlsockets + 1));
727 if (!(t)) {
728 ret = 1;
729 break;
730 }
731 lsockets = t;
732
733 if ((lsockets[nlsockets] = localserver(opts)) == -1) {
734 ret = 1;
735 umask(umsk);
736 break;
737 }
738 umask(umsk); /* restore umask */
739
740 if (optget(opts, "LocalSocketGroup")->enabled) {
741 char *gname = optget(opts, "LocalSocketGroup")->strarg, *end;
742 gid_t sock_gid = strtol(gname, &end, 10);
743
744 if (*end) {
745 struct group *pgrp = getgrnam(gname);
746
747 if (!pgrp) {
748 logg("!Unknown group %s\n", gname);
749 ret = 1;
750 break;
751 }
752
753 sock_gid = pgrp->gr_gid;
754 }
755 if (chown(optget(opts, "LocalSocket")->strarg, -1, sock_gid)) {
756 logg("!Failed to change socket ownership to group %s\n", gname);
757 ret = 1;
758 break;
759 }
760 }
761 if (optget(opts, "LocalSocketMode")->enabled) {
762 char *end;
763
764 sock_mode = strtol(optget(opts, "LocalSocketMode")->strarg, &end, 8);
765
766 if (*end) {
767 logg("!Invalid LocalSocketMode %s\n", optget(opts, "LocalSocketMode")->strarg);
768 ret = 1;
769 break;
770 }
771 } else {
772 sock_mode = 0777 /* & ~umsk*/; /* conservative default: umask was 0 in clamd < 0.96 */
773 }
774
775 if (chmod(optget(opts, "LocalSocket")->strarg, sock_mode & 0666)) {
776 logg("!Cannot set socket permission for %s to %3o\n", optget(opts, "LocalSocket")->strarg, sock_mode & 0666);
777 ret = 1;
778 break;
779 }
780
781 nlsockets++;
782 }
783
784 /* check for local sockets passed by systemd */
785 if (num_fd > 0) {
786 int *t;
787 t = realloc(lsockets, sizeof(int) * (nlsockets + 1));
788 if (!(t)) {
789 ret = 1;
790 break;
791 }
792 lsockets = t;
793
794 lsockets[nlsockets] = localserver(opts);
795 if (lsockets[nlsockets] == -1) {
796 ret = 1;
797 break;
798 } else if (lsockets[nlsockets] > 0) {
799 nlsockets++;
800 }
801 }
802
803 if (0 == foreground) {
804 if (!debug_mode) {
805 if (chdir("/") == -1) {
806 logg("^Can't change current working directory to root\n");
807 }
808 }
809
810 #ifndef _WIN32
811
812 /*Since some of the logging is written to stderr, and some of it
813 * is written to a log file, close stdin, stderr, and stdout
814 * now, since everything is initialized.*/
815
816 /*signal the parent process.*/
817 if (parentPid != getpid()) {
818 daemonize_signal_parent(parentPid);
819 }
820 #endif
821 }
822
823 #elif defined(_WIN32)
824 if (optget(opts, "service-mode")->enabled) {
825 cl_engine_set_clcb_sigload(engine, NULL, NULL);
826 svc_ready();
827 }
828 #endif
829
830 if (nlsockets == 0) {
831 logg("!Not listening on any interfaces\n");
832 ret = 1;
833 break;
834 }
835
836 ret = recvloop(lsockets, nlsockets, engine, dboptions, opts);
837
838 } while (0);
839
840 if (num_fd == 0) {
841 logg("*Closing the main socket%s.\n", (nlsockets > 1) ? "s" : "");
842
843 for (i = 0; i < nlsockets; i++) {
844 closesocket(lsockets[i]);
845 }
846 #ifndef _WIN32
847 if (nlsockets && localsock) {
848 opt = optget(opts, "LocalSocket");
849
850 if (unlink(opt->strarg) == -1)
851 logg("!Can't unlink the socket file %s\n", opt->strarg);
852 else
853 logg("Socket file removed.\n");
854 }
855 #endif
856 }
857
858 free(lsockets);
859
860 logg_close();
861 optfree(opts);
862
863 return ret;
864 }
865