xref: /illumos-gate/usr/src/cmd/vscan/vscand/vs_main.c (revision bea83d026ee1bd1b2a2419e1d0232f107a5d7d9b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * vscand Daemon Program
30  */
31 
32 #include <stdio.h>
33 #include <sys/stat.h>
34 #include <sys/filio.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 #include <sys/param.h>
39 #include <zone.h>
40 #include <tsol/label.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <fcntl.h>
44 #include <wait.h>
45 #include <unistd.h>
46 #include <getopt.h>
47 #include <stdarg.h>
48 #include <libscf.h>
49 #include <signal.h>
50 #include <libintl.h>
51 #include <netinet/in.h>
52 #include <arpa/inet.h>
53 #include <ctype.h>
54 #include <pthread.h>
55 #include <syslog.h>
56 #include <locale.h>
57 #include <pwd.h>
58 #include <grp.h>
59 #include <priv_utils.h>
60 #include "vs_incl.h"
61 
62 static int vscand_fg = 0; /* daemon by default */
63 static vs_daemon_state_t vscand_state = VS_STATE_INIT;
64 static int vscand_sigval = 0;
65 static int vscand_kdrv_fd = -1;
66 static pthread_mutex_t vscand_cfg_mutex = PTHREAD_MUTEX_INITIALIZER;
67 
68 /* virus log path */
69 static char vscand_vlog[MAXPATHLEN];
70 
71 /* user and group ids - default to 0 */
72 static uid_t root_uid = 0, daemon_uid = 0;
73 static gid_t sys_gid = 0;
74 
75 
76 /* local function prototypes */
77 static void vscand_sig_handler(int);
78 static int vscand_parse_args(int, char **);
79 static void vscand_get_uid_gid();
80 static int vscand_init_file(char *, uid_t, gid_t, mode_t);
81 static void vscand_usage(char *);
82 static int vscand_daemonize_init(void);
83 static void vscand_daemonize_fini(int, int);
84 static int vscand_init(void);
85 static void vscand_fini(void);
86 static int vscand_configure(void);
87 static int vscand_kernel_bind(void);
88 static void vscand_kernel_unbind(void);
89 static int vscand_kernel_enable(int);
90 static void vscand_kernel_disable(void);
91 static int vscand_kernel_config(vs_config_t *);
92 static void vscand_error(const char *);
93 static int vscand_get_viruslog(void);
94 
95 
96 /*
97  * Enable libumem debugging by default on DEBUG builds.
98  */
99 #ifdef DEBUG
100 const char *
101 _umem_debug_init(void)
102 {
103 	return ("default,verbose"); /* $UMEM_DEBUG setting */
104 }
105 
106 const char *
107 _umem_logging_init(void)
108 {
109 	return ("fail,contents"); /* $UMEM_LOGGING setting */
110 }
111 #endif
112 
113 
114 /*
115  * vs_sig_handler
116  */
117 static void
118 vscand_sig_handler(int sig)
119 {
120 	if (vscand_sigval == 0)
121 		vscand_sigval = sig;
122 }
123 
124 
125 /*
126  * main
127  *
128  * main must return SMF return code (see smf_method (5)) if vscand
129  * is invoked directly by smf (see manifest: vscan.xml)
130  * Exit codes: SMF_EXIT_ERR_CONFIG - error
131  *             SMF_EXIT_ERR_FATAL - fatal error
132  *             SMF_EXIT_OK - success
133  */
134 int
135 main(int argc, char **argv)
136 {
137 	int err_stat = 0, pfd = -1;
138 	sigset_t set;
139 	struct sigaction act;
140 	mode_t log_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
141 	mode_t door_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
142 
143 	(void) setlocale(LC_ALL, "");
144 	openlog("vscand", 0, LOG_DAEMON);
145 
146 	/* check if running in global zone; other zones not supported */
147 	if (getzoneid() != GLOBAL_ZONEID) {
148 		vscand_error(gettext("non-global zone not supported"));
149 		exit(SMF_EXIT_ERR_FATAL);
150 	}
151 
152 	/* check for a Trusted Solaris environment; not supported */
153 	if (is_system_labeled()) {
154 		vscand_error(gettext("Trusted Extensions not supported"));
155 		exit(SMF_EXIT_ERR_FATAL);
156 	}
157 
158 	/* Parse arguments */
159 	if (vscand_parse_args(argc, argv) != 0)
160 		exit(SMF_EXIT_ERR_CONFIG);
161 
162 	vscand_get_uid_gid();
163 
164 	/*
165 	 * Initializetion of virus log and statistic door file
166 	 * MUST be done BEFORE vscand_daemonize_init resets uid/gid.
167 	 * Only root can create the files in /var/log and /var/run.
168 	 */
169 	if ((vscand_get_viruslog() != 0) ||
170 	    (vscand_vlog[0] == '\0') ||
171 	    (vscand_init_file(vscand_vlog, root_uid, sys_gid, log_mode) != 0)) {
172 		*vscand_vlog = 0;
173 	}
174 
175 	(void) unlink(VS_STATS_DOOR_NAME);
176 	(void) vscand_init_file(VS_STATS_DOOR_NAME,
177 	    daemon_uid, sys_gid, door_mode);
178 
179 	/*
180 	 * Once we're done setting our global state up, set up signal handlers
181 	 * for ensuring orderly termination on SIGTERM.  If we are starting in
182 	 * the foreground, we also use the same handler for SIGINT and SIGHUP.
183 	 */
184 	(void) sigfillset(&set);
185 	(void) sigdelset(&set, SIGABRT); /* always unblocked for ASSERT() */
186 
187 	(void) sigfillset(&act.sa_mask);
188 	act.sa_handler = vscand_sig_handler;
189 	act.sa_flags = 0;
190 
191 	(void) sigaction(SIGTERM, &act, NULL);
192 	(void) sigaction(SIGHUP, &act, NULL); /* Refresh config */
193 	(void) sigaction(SIGINT, &act, NULL);
194 	(void) sigaction(SIGUSR1, &act, NULL);
195 	(void) sigdelset(&set, SIGTERM);
196 	(void) sigdelset(&set, SIGHUP);
197 	(void) sigdelset(&set, SIGINT);
198 	(void) sigdelset(&set, SIGUSR1);
199 
200 	if (vscand_fg) {
201 		(void) sigdelset(&set, SIGTSTP);
202 		(void) sigdelset(&set, SIGTTIN);
203 		(void) sigdelset(&set, SIGTTOU);
204 
205 		if (vscand_init() != 0) {
206 			vscand_error(gettext("failed to initialize service"));
207 			exit(SMF_EXIT_ERR_CONFIG);
208 		}
209 	} else {
210 		/*
211 		 * "pfd" is a pipe descriptor -- any fatal errors
212 		 * during subsequent initialization of the child
213 		 * process should be written to this pipe and the
214 		 * parent will report this error as the exit status.
215 		 */
216 		pfd = vscand_daemonize_init();
217 
218 		if (vscand_init() != 0) {
219 			vscand_error(gettext("failed to initialize service"));
220 			exit(SMF_EXIT_ERR_CONFIG);
221 		}
222 
223 		vscand_daemonize_fini(pfd, err_stat);
224 	}
225 
226 	vscand_state = VS_STATE_RUNNING;
227 
228 	/* Wait here until shutdown */
229 	while (vscand_state == VS_STATE_RUNNING) {
230 
231 		(void) sigsuspend(&set);
232 
233 		switch (vscand_sigval) {
234 		case 0:
235 		case SIGUSR1:
236 			break;
237 		case SIGHUP:
238 			if (vscand_configure() != 0)
239 				vscand_state = VS_STATE_SHUTDOWN;
240 			break;
241 		default:
242 			vscand_state = VS_STATE_SHUTDOWN;
243 			break;
244 		}
245 
246 		vscand_sigval = 0;
247 	}
248 
249 	vscand_fini();
250 	return (SMF_EXIT_OK);
251 }
252 
253 
254 /*
255  * vscand_parse_args -
256  * Routine to parse the arguments to the daemon program
257  * 'f' argument runs process in the foreground instead of as a daemon
258  */
259 int
260 vscand_parse_args(int argc, char **argv)
261 {
262 	int	optchar;
263 
264 	while ((optchar = getopt(argc, argv, "f?")) != EOF) {
265 		switch (optchar) {
266 		case 'f':
267 			vscand_fg = 1;
268 			break;
269 		default:
270 			vscand_usage(argv[0]);
271 			return (-1);
272 		}
273 	}
274 	return (0);
275 }
276 
277 
278 /*
279  * vscand_usage
280  */
281 static void
282 vscand_usage(char *progname)
283 {
284 	char buf[128];
285 
286 	(void) snprintf(buf, sizeof (buf), "%s %s [-f]",
287 	    gettext("Usage"), progname);
288 	vscand_error(buf);
289 
290 	(void) snprintf(buf, sizeof (buf), "\t-f %s\n",
291 	    gettext("run program in foreground"));
292 	vscand_error(buf);
293 }
294 
295 
296 /*
297  * vscand_get_uid_gid
298  *
299  * failure to access a uid/gid results in the default (0) being used.
300  */
301 static void
302 vscand_get_uid_gid()
303 {
304 	struct passwd *pwd;
305 	struct group *grp;
306 
307 	if ((pwd = getpwnam("root")) != NULL)
308 		root_uid = pwd->pw_uid;
309 
310 	if ((pwd = getpwnam("daemon")) != NULL)
311 		daemon_uid = pwd->pw_uid;
312 
313 	if ((grp = getgrnam("sys")) != NULL)
314 		sys_gid = grp->gr_gid;
315 }
316 
317 
318 /*
319  * vscand_daemonize_init
320  *
321  * This function will fork off a child process, from which
322  * only the child will return.
323  */
324 static int
325 vscand_daemonize_init(void)
326 {
327 	int status, pfds[2];
328 	sigset_t set, oset;
329 	pid_t pid;
330 
331 	/*
332 	 * Reset process owner/group to daemon/sys. Root ownership is only
333 	 * required to initialize virus log file in /var/log
334 	 */
335 	if (__init_daemon_priv(PU_RESETGROUPS | PU_LIMITPRIVS,
336 	    daemon_uid, sys_gid,
337 	    PRIV_PROC_AUDIT, PRIV_FILE_DAC_SEARCH, PRIV_FILE_DAC_READ,
338 	    PRIV_FILE_FLAG_SET, NULL) != 0) {
339 		vscand_error(gettext("failed to initialize privileges"));
340 		_exit(SMF_EXIT_ERR_FATAL);
341 	}
342 
343 	/*
344 	 * Block all signals prior to the fork and leave them blocked in the
345 	 * parent so we don't get in a situation where the parent gets SIGINT
346 	 * and returns non-zero exit status and the child is actually running.
347 	 * In the child, restore the signal mask once we've done our setsid().
348 	 */
349 	(void) sigfillset(&set);
350 	(void) sigdelset(&set, SIGABRT);
351 	(void) sigprocmask(SIG_BLOCK, &set, &oset);
352 
353 	if (pipe(pfds) == -1) {
354 		vscand_error(gettext("failed to create pipe for daemonize"));
355 		_exit(SMF_EXIT_ERR_FATAL);
356 	}
357 
358 	if ((pid = fork()) == -1) {
359 		vscand_error(gettext("failed to fork for daemonize"));
360 		_exit(SMF_EXIT_ERR_FATAL);
361 	}
362 
363 	/*
364 	 * If we're the parent process, wait for either the child to send us
365 	 * the appropriate exit status over the pipe or for the read to fail
366 	 * (presumably with 0 for EOF if our child terminated abnormally).
367 	 * If the read fails, exit with either the child's exit status if it
368 	 * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal.
369 	 */
370 	if (pid != 0) {
371 		(void) close(pfds[1]);
372 
373 		if (read(pfds[0], &status, sizeof (status)) == sizeof (status))
374 			_exit(status);
375 
376 		if (waitpid(pid, &status, 0) == pid && WIFEXITED(status))
377 			_exit(WEXITSTATUS(status));
378 
379 		vscand_error(gettext("failed to daemonize"));
380 		_exit(SMF_EXIT_ERR_FATAL);
381 	}
382 
383 
384 	(void) setsid();
385 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
386 	(void) chdir("/");
387 	(void) umask(022);
388 	(void) close(pfds[0]);
389 
390 	return (pfds[1]);
391 }
392 
393 
394 /*
395  * vscand_daemonize_fini
396  * Now that we're running, if a pipe fd was specified, write an exit
397  * status to it to indicate that our parent process can safely detach.
398  */
399 static void
400 vscand_daemonize_fini(int fd, int err_status)
401 {
402 	if (fd >= 0)
403 		(void) write(fd, &err_status, sizeof (err_status));
404 
405 	(void) close(fd);
406 
407 	/* Restore standard file descriptors */
408 	if ((fd = open("/dev/null", O_RDWR)) >= 0) {
409 		(void) fcntl(fd, F_DUP2FD, STDIN_FILENO);
410 		(void) fcntl(fd, F_DUP2FD, STDOUT_FILENO);
411 		(void) fcntl(fd, F_DUP2FD, STDERR_FILENO);
412 		(void) close(fd);
413 	}
414 
415 	/* clear basic privileges not required by vscand */
416 	__fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
417 	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL);
418 }
419 
420 
421 /*
422  * vscand_init_file
423  *
424  * create specified file and set its uid, gid and mode
425  */
426 static int
427 vscand_init_file(char *filepath, uid_t uid, gid_t gid, mode_t mode)
428 {
429 	int fd, rc = 0;
430 	struct stat stat_buf;
431 	char buf[MAXPATHLEN];
432 
433 	if ((fd = open(filepath, O_RDONLY | O_CREAT, mode)) == -1)
434 		rc = -1;
435 	else {
436 		if (fstat(fd, &stat_buf) != 0)
437 			rc = -1;
438 		else {
439 			if (stat_buf.st_mode != mode) {
440 				if (fchmod(fd, mode) != 0)
441 					rc = -1;
442 			}
443 
444 			if ((stat_buf.st_uid != uid) ||
445 			    (stat_buf.st_gid != gid)) {
446 				if (fchown(fd, uid, gid) != 0)
447 					rc = -1;
448 			}
449 		}
450 
451 		(void) close(fd);
452 	}
453 
454 	if (rc == -1) {
455 		(void) snprintf(buf, MAXPATHLEN, "%s %s",
456 		    gettext("Failed to initialize"), filepath);
457 		vscand_error(buf);
458 	}
459 
460 	return (rc);
461 }
462 
463 
464 /*
465  * vscand_init
466  *
467  * There are some requirements on the order in which the daemon
468  * initialization functions are called.
469  *
470  * - vscand_kernel_bind - bind to kernel module
471  * - vs_eng_init populates vs_icap data and thus vs_icap_init MUST be
472  *   called before vs_eng_init
473  * - vscand_configure - load the configuration
474  * - vs_door_init - start vscan door server
475  * - vscand_kernel_enable - enable scan requests from kernel
476  */
477 static int
478 vscand_init(void)
479 {
480 	int door_fd = -1;
481 
482 	if (vscand_kernel_bind() < 0)
483 		return (-1);
484 
485 	if (vs_stats_init() != 0)
486 		vscand_error(
487 		    gettext("failed to initialize statistics interface"));
488 
489 	vs_svc_init();
490 	vs_icap_init();
491 	vs_eng_init();
492 
493 	if (vscand_configure() != 0) {
494 		vscand_error(gettext("failed to initialize configuration"));
495 		return (-1);
496 	}
497 
498 	if (((door_fd = vs_door_init()) < 0) ||
499 	    (vscand_kernel_enable(door_fd) < 0)) {
500 		vscand_fini();
501 		return (-1);
502 	}
503 
504 	return (0);
505 }
506 
507 
508 /*
509  * vscand_fini
510  *
511  * vscand_kernel_disable - should be called first to ensure that no
512  *	more scan requests are initiated from the kernel module
513  * vs_door_fini - shouldn't be called until after the in-progress
514  *	scans complete (vs_eng_fini waits for in progress scans)
515  * vscand_kernel_unbind - should be called last to tell the kernel module
516  *	that vscand is shutdown.
517  */
518 static void
519 vscand_fini(void)
520 {
521 	vscand_kernel_disable();
522 
523 	vs_svc_fini();
524 	vs_eng_fini();
525 	vs_icap_fini();
526 
527 	vs_door_fini();
528 	vs_stats_fini();
529 
530 	vscand_kernel_unbind();
531 }
532 
533 
534 /*
535  * vscand_configure
536  */
537 static int
538 vscand_configure(void)
539 {
540 	uint32_t len;
541 	vs_config_t kconfig;
542 	vs_props_all_t config;
543 
544 	(void) pthread_mutex_lock(&vscand_cfg_mutex);
545 
546 	(void) memset(&config, 0, sizeof (vs_props_all_t));
547 	if (vs_props_get_all(&config) != VS_ERR_NONE) {
548 		vscand_error(gettext("configuration data error"));
549 		(void) pthread_mutex_unlock(&vscand_cfg_mutex);
550 		return (-1);
551 	}
552 
553 	(void) memset(&kconfig, 0, sizeof (vs_config_t));
554 	len = sizeof (kconfig.vsc_types);
555 	if (vs_parse_types(config.va_props.vp_types,
556 	    kconfig.vsc_types, &len) != 0) {
557 		vscand_error(gettext("configuration data error - types"));
558 		(void) pthread_mutex_unlock(&vscand_cfg_mutex);
559 		return (-1);
560 	}
561 	kconfig.vsc_types_len = len;
562 
563 	/* Convert the maxfsize string from the configuration into bytes */
564 	if (vs_strtonum(config.va_props.vp_maxsize,
565 	    &kconfig.vsc_max_size) != 0) {
566 		vscand_error(gettext("configuration data error - max-size"));
567 		(void) pthread_mutex_unlock(&vscand_cfg_mutex);
568 		return (-1);
569 	}
570 	kconfig.vsc_allow = config.va_props.vp_maxsize_action ? 1LL : 0LL;
571 
572 	/* Send configuration update to kernel */
573 	if (vscand_kernel_config(&kconfig) != 0) {
574 		(void) pthread_mutex_unlock(&vscand_cfg_mutex);
575 		return (-1);
576 	}
577 
578 	/* Tell vs_eng things have changed. */
579 	vs_eng_config(&config);
580 
581 	/* Tell vs_stats things have changed */
582 	vs_stats_config(&config);
583 
584 	(void) pthread_mutex_unlock(&vscand_cfg_mutex);
585 	return (0);
586 }
587 
588 
589 /*
590  * vscand_get_state
591  */
592 vs_daemon_state_t
593 vscand_get_state(void)
594 {
595 	return (vscand_state);
596 }
597 
598 
599 /*
600  * vscand_get_viruslog
601  */
602 static int
603 vscand_get_viruslog()
604 {
605 	vs_props_t props;
606 	uint64_t propids;
607 	int rc;
608 
609 	propids = VS_PROPID_VLOG;
610 	if ((rc = vs_props_get(&props, propids)) != VS_ERR_NONE) {
611 		vscand_error(vs_strerror(rc));
612 		return (-1);
613 	}
614 
615 	(void) strlcpy(vscand_vlog, props.vp_vlog, sizeof (vscand_vlog));
616 	return (0);
617 }
618 
619 
620 /*
621  * vscand_viruslog
622  */
623 char *
624 vscand_viruslog(void)
625 {
626 	if (vscand_vlog[0] == '\0')
627 		return (NULL);
628 
629 	return (vscand_vlog);
630 }
631 
632 
633 /*
634  * vscand_kernel_bind
635  */
636 static int
637 vscand_kernel_bind(void)
638 {
639 	char devname[MAXPATHLEN];
640 	int inst = 0;
641 
642 	(void) snprintf(devname, MAXPATHLEN, "%s%d", VS_DRV_PATH, inst);
643 
644 	if ((vscand_kdrv_fd = open(devname, O_RDONLY)) < 0) {
645 		vscand_error(gettext("failed to bind to kernel"));
646 		return (-1);
647 	}
648 
649 	return (0);
650 }
651 
652 
653 /*
654  * vscand_kernel_unbind
655  */
656 static void
657 vscand_kernel_unbind(void)
658 {
659 	if (vscand_kdrv_fd >= 0)
660 		(void) close(vscand_kdrv_fd);
661 }
662 
663 
664 /*
665  * vscand_kernel_enable
666  */
667 static int
668 vscand_kernel_enable(int door_fd)
669 {
670 	if (ioctl(vscand_kdrv_fd, VS_DRV_IOCTL_ENABLE, door_fd) < 0) {
671 		vscand_error(gettext("failed to bind to kernel"));
672 		(void) close(vscand_kdrv_fd);
673 		vscand_kdrv_fd = -1;
674 		return (-1);
675 	}
676 	return (0);
677 }
678 
679 
680 /*
681  * vscand_kernel_disable
682  */
683 static void
684 vscand_kernel_disable()
685 {
686 	if (vscand_kdrv_fd >= 0)
687 		(void) ioctl(vscand_kdrv_fd, VS_DRV_IOCTL_DISABLE);
688 }
689 
690 
691 /*
692  * vscand_kernel_config
693  */
694 int
695 vscand_kernel_config(vs_config_t *conf)
696 {
697 	if (vscand_kdrv_fd < 0)
698 		return (-1);
699 
700 	if (ioctl(vscand_kdrv_fd, VS_DRV_IOCTL_CONFIG, conf) < 0) {
701 		vscand_error(gettext("failed to send config to kernel"));
702 		return (-1);
703 	}
704 
705 	return (0);
706 }
707 
708 
709 /*
710  * vscand_error
711  */
712 static void
713 vscand_error(const char *errmsg)
714 {
715 	(void) fprintf(stderr, "vscand: %s", errmsg);
716 	syslog(LOG_ERR, "%s\n", errmsg);
717 }
718