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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * vscand Daemon Program
32 #include <sys/filio.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/ioctl.h>
36 #include <sys/param.h>
38 #include <tsol/label.h>
50 #include <netinet/in.h>
51 #include <arpa/inet.h>
58 #include <priv_utils.h>
62 #define VS_FILE_DESCRIPTORS 512
64 static int vscand_fg
= 0; /* daemon by default */
65 static vs_daemon_state_t vscand_state
= VS_STATE_INIT
;
66 static volatile uint_t vscand_sigval
= 0;
67 static volatile uint_t vscand_n_refresh
= 0;
68 static int vscand_kdrv_fd
= -1;
69 static pthread_mutex_t vscand_cfg_mutex
= PTHREAD_MUTEX_INITIALIZER
;
70 static pthread_cond_t vscand_cfg_cv
;
71 static pthread_t vscand_cfg_tid
= 0;
74 static char vscand_vlog
[MAXPATHLEN
];
76 /* user and group ids - default to 0 */
77 static uid_t root_uid
= 0, daemon_uid
= 0;
78 static gid_t sys_gid
= 0;
81 /* local function prototypes */
82 static void vscand_sig_handler(int);
83 static int vscand_parse_args(int, char **);
84 static void vscand_get_uid_gid();
85 static int vscand_init_file(char *, uid_t
, gid_t
, mode_t
);
86 static void vscand_usage(char *);
87 static int vscand_daemonize_init(void);
88 static void vscand_daemonize_fini(int, int);
89 static int vscand_init(void);
90 static void vscand_fini(void);
91 static int vscand_cfg_init(void);
92 static void vscand_cfg_fini(void);
93 static void *vscand_cfg_handler(void *);
94 static int vscand_configure(void);
95 static void vscand_dtrace_cfg(vs_props_all_t
*);
96 static int vscand_kernel_bind(void);
97 static void vscand_kernel_unbind(void);
98 static int vscand_kernel_enable(int);
99 static void vscand_kernel_disable(void);
100 static int vscand_kernel_config(vs_config_t
*);
101 static int vscand_kernel_max_req(uint32_t *);
102 static void vscand_error(const char *);
103 static int vscand_get_viruslog(void);
104 static int vscand_set_resource_limits(void);
108 * Enable libumem debugging by default on DEBUG builds.
112 _umem_debug_init(void)
114 return ("default,verbose"); /* $UMEM_DEBUG setting */
118 _umem_logging_init(void)
120 return ("fail,contents"); /* $UMEM_LOGGING setting */
129 vscand_sig_handler(int sig
)
131 if (vscand_sigval
== 0)
132 (void) atomic_swap_uint(&vscand_sigval
, sig
);
135 atomic_inc_uint(&vscand_n_refresh
);
142 * main must return SMF return code (see smf_method (5)) if vscand
143 * is invoked directly by smf (see manifest: vscan.xml)
144 * Exit codes: SMF_EXIT_ERR_CONFIG - error
145 * SMF_EXIT_ERR_FATAL - fatal error
146 * SMF_EXIT_OK - success
149 main(int argc
, char **argv
)
151 int err_stat
= 0, pfd
= -1;
153 struct sigaction act
;
156 mode_t log_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
;
157 mode_t door_mode
= S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
159 (void) setlocale(LC_ALL
, "");
160 openlog("vscand", 0, LOG_DAEMON
);
162 /* check if running in global zone; other zones not supported */
163 if (getzoneid() != GLOBAL_ZONEID
) {
164 vscand_error(gettext("non-global zone not supported"));
165 exit(SMF_EXIT_ERR_FATAL
);
168 /* check for a Trusted Solaris environment; not supported */
169 if (is_system_labeled()) {
170 vscand_error(gettext("Trusted Extensions not supported"));
171 exit(SMF_EXIT_ERR_FATAL
);
174 /* Parse arguments */
175 if (vscand_parse_args(argc
, argv
) != 0)
176 exit(SMF_EXIT_ERR_CONFIG
);
178 vscand_get_uid_gid();
181 * Initializetion of virus log and statistic door file
182 * MUST be done BEFORE vscand_daemonize_init resets uid/gid.
183 * Only root can create the files in /var/log and /var/run.
185 if ((vscand_get_viruslog() != 0) ||
186 (vscand_vlog
[0] == '\0') ||
187 (vscand_init_file(vscand_vlog
, root_uid
, sys_gid
, log_mode
) != 0)) {
191 (void) vscand_init_file(VS_STATS_DOOR_NAME
,
192 daemon_uid
, sys_gid
, door_mode
);
195 * Once we're done setting our global state up, set up signal handlers
196 * for ensuring orderly termination on SIGTERM.
198 (void) sigfillset(&set
);
199 (void) sigdelset(&set
, SIGABRT
); /* always unblocked for ASSERT() */
201 (void) sigfillset(&act
.sa_mask
);
202 act
.sa_handler
= vscand_sig_handler
;
205 (void) sigaction(SIGTERM
, &act
, NULL
);
206 (void) sigaction(SIGHUP
, &act
, NULL
); /* Refresh config */
207 (void) sigaction(SIGINT
, &act
, NULL
);
208 (void) sigaction(SIGPIPE
, &act
, NULL
);
209 (void) sigdelset(&set
, SIGTERM
);
210 (void) sigdelset(&set
, SIGHUP
);
211 (void) sigdelset(&set
, SIGINT
);
212 (void) sigdelset(&set
, SIGPIPE
);
215 (void) sigdelset(&set
, SIGTSTP
);
216 (void) sigdelset(&set
, SIGTTIN
);
217 (void) sigdelset(&set
, SIGTTOU
);
219 if (vscand_init() != 0) {
220 vscand_error(gettext("failed to initialize service"));
221 exit(SMF_EXIT_ERR_CONFIG
);
225 * "pfd" is a pipe descriptor -- any fatal errors
226 * during subsequent initialization of the child
227 * process should be written to this pipe and the
228 * parent will report this error as the exit status.
230 pfd
= vscand_daemonize_init();
232 if (vscand_init() != 0) {
233 vscand_error(gettext("failed to initialize service"));
234 exit(SMF_EXIT_ERR_CONFIG
);
237 vscand_daemonize_fini(pfd
, err_stat
);
240 vscand_state
= VS_STATE_RUNNING
;
242 /* Wait here until shutdown */
243 while (vscand_state
== VS_STATE_RUNNING
) {
244 if (vscand_sigval
== 0 && vscand_n_refresh
== 0)
245 (void) sigsuspend(&set
);
247 sigval
= atomic_swap_uint(&vscand_sigval
, 0);
255 vscand_state
= VS_STATE_SHUTDOWN
;
259 if (atomic_swap_uint(&vscand_n_refresh
, 0) != 0)
260 (void) pthread_cond_signal(&vscand_cfg_cv
);
264 return (SMF_EXIT_OK
);
270 * Routine to parse the arguments to the daemon program
271 * 'f' argument runs process in the foreground instead of as a daemon
274 vscand_parse_args(int argc
, char **argv
)
278 while ((optchar
= getopt(argc
, argv
, "f?")) != EOF
) {
284 vscand_usage(argv
[0]);
296 vscand_usage(char *progname
)
300 (void) snprintf(buf
, sizeof (buf
), "%s %s [-f]",
301 gettext("Usage"), progname
);
304 (void) snprintf(buf
, sizeof (buf
), "\t-f %s\n",
305 gettext("run program in foreground"));
313 * failure to access a uid/gid results in the default (0) being used.
321 if ((pwd
= getpwnam("root")) != NULL
)
322 root_uid
= pwd
->pw_uid
;
324 if ((pwd
= getpwnam("daemon")) != NULL
)
325 daemon_uid
= pwd
->pw_uid
;
327 if ((grp
= getgrnam("sys")) != NULL
)
328 sys_gid
= grp
->gr_gid
;
333 * vscand_daemonize_init
335 * This function will fork off a child process, from which
336 * only the child will return.
339 vscand_daemonize_init(void)
346 * Reset process owner/group to daemon/sys. Root ownership is only
347 * required to initialize virus log file in /var/log
349 if (__init_daemon_priv(PU_RESETGROUPS
| PU_LIMITPRIVS
,
351 PRIV_PROC_AUDIT
, PRIV_FILE_DAC_SEARCH
, PRIV_FILE_DAC_READ
,
352 PRIV_FILE_FLAG_SET
, NULL
) != 0) {
353 vscand_error(gettext("failed to initialize privileges"));
354 _exit(SMF_EXIT_ERR_FATAL
);
358 * Block all signals prior to the fork and leave them blocked in the
359 * parent so we don't get in a situation where the parent gets SIGINT
360 * and returns non-zero exit status and the child is actually running.
361 * In the child, restore the signal mask once we've done our setsid().
363 (void) sigfillset(&set
);
364 (void) sigdelset(&set
, SIGABRT
);
365 (void) sigprocmask(SIG_BLOCK
, &set
, &oset
);
367 if (pipe(pfds
) == -1) {
368 vscand_error(gettext("failed to create pipe for daemonize"));
369 _exit(SMF_EXIT_ERR_FATAL
);
372 if ((pid
= fork()) == -1) {
373 vscand_error(gettext("failed to fork for daemonize"));
374 _exit(SMF_EXIT_ERR_FATAL
);
378 * If we're the parent process, wait for either the child to send us
379 * the appropriate exit status over the pipe or for the read to fail
380 * (presumably with 0 for EOF if our child terminated abnormally).
381 * If the read fails, exit with either the child's exit status if it
382 * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal.
385 (void) close(pfds
[1]);
387 if (read(pfds
[0], &status
, sizeof (status
)) == sizeof (status
))
390 if (waitpid(pid
, &status
, 0) == pid
&& WIFEXITED(status
))
391 _exit(WEXITSTATUS(status
));
393 vscand_error(gettext("failed to daemonize"));
394 _exit(SMF_EXIT_ERR_FATAL
);
399 (void) sigprocmask(SIG_SETMASK
, &oset
, NULL
);
402 (void) close(pfds
[0]);
409 * vscand_daemonize_fini
410 * Now that we're running, if a pipe fd was specified, write an exit
411 * status to it to indicate that our parent process can safely detach.
414 vscand_daemonize_fini(int fd
, int err_status
)
417 (void) write(fd
, &err_status
, sizeof (err_status
));
421 /* Restore standard file descriptors */
422 if ((fd
= open("/dev/null", O_RDWR
)) >= 0) {
423 (void) fcntl(fd
, F_DUP2FD
, STDIN_FILENO
);
424 (void) fcntl(fd
, F_DUP2FD
, STDOUT_FILENO
);
425 (void) fcntl(fd
, F_DUP2FD
, STDERR_FILENO
);
429 /* clear basic privileges not required by vscand */
430 __fini_daemon_priv(PRIV_PROC_FORK
, PRIV_PROC_EXEC
, PRIV_PROC_SESSION
,
431 PRIV_FILE_LINK_ANY
, PRIV_PROC_INFO
, (char *)NULL
);
438 * create specified file and set its uid, gid and mode
441 vscand_init_file(char *filepath
, uid_t uid
, gid_t gid
, mode_t access_mode
)
444 struct stat stat_buf
;
445 char buf
[MAXPATHLEN
];
447 if ((fd
= open(filepath
, O_RDONLY
| O_CREAT
, access_mode
)) == -1) {
450 if (fstat(fd
, &stat_buf
) != 0) {
453 if ((stat_buf
.st_mode
& S_IAMB
) != access_mode
) {
454 if (fchmod(fd
, access_mode
) != 0)
458 if ((stat_buf
.st_uid
!= uid
) ||
459 (stat_buf
.st_gid
!= gid
)) {
460 if (fchown(fd
, uid
, gid
) != 0)
469 (void) snprintf(buf
, MAXPATHLEN
, "%s %s",
470 gettext("Failed to initialize"), filepath
);
481 * There are some requirements on the order in which the daemon
482 * initialization functions are called.
484 * - vscand_kernel_bind - bind to kernel module
485 * - vs_eng_init populates vs_icap data and thus vs_icap_init MUST be
486 * called before vs_eng_init
487 * - vscand_configure - load the configuration
488 * - vs_door_init - start vscan door server
489 * - vscand_kernel_enable - enable scan requests from kernel
497 if (vscand_kernel_bind() < 0)
500 if (vscand_kernel_max_req(&max_req
) == -1)
503 if (vs_svc_init(max_req
) != 0)
506 if (vs_stats_init() != 0)
508 gettext("failed to initialize statistics interface"));
513 /* initialize configuration and handler thread */
514 if (vscand_cfg_init() != 0) {
515 vscand_error(gettext("failed to initialize configuration"));
520 (void) vscand_set_resource_limits();
522 if (((door_fd
= vs_door_init()) < 0) ||
523 (vscand_kernel_enable(door_fd
) < 0)) {
535 * vscand_kernel_disable - should be called first to ensure that no
536 * more scan requests are initiated from the kernel module
537 * vs_svc_terminate - terminate requests and wait for thread completion
538 * vs_xxx_fini - module cleanup routines
539 * vscand_kernel_unbind - should be called last to tell the kernel module
540 * that vscand is shutdown.
545 vscand_kernel_disable();
547 /* terminate reconfiguration handler thread */
550 /* terminate requests and wait for completion */
560 vscand_kernel_unbind();
567 * initialize configuration and reconfiguration handler thread
570 vscand_cfg_init(void)
574 (void) pthread_cond_init(&vscand_cfg_cv
, NULL
);
576 (void) pthread_mutex_lock(&vscand_cfg_mutex
);
577 rc
= vscand_configure();
578 (void) pthread_mutex_unlock(&vscand_cfg_mutex
);
583 if (pthread_create(&vscand_cfg_tid
, NULL
, vscand_cfg_handler
, 0) != 0) {
595 * terminate reconfiguration handler thread
600 if (vscand_cfg_tid
!= 0) {
601 (void) pthread_cond_signal(&vscand_cfg_cv
);
602 (void) pthread_join(vscand_cfg_tid
, NULL
);
605 (void) pthread_cond_destroy(&vscand_cfg_cv
);
611 * wait for reconfiguration event and reload configuration
612 * exit on VS_STATE_SHUTDOWN
616 vscand_cfg_handler(void *arg
)
618 (void) pthread_mutex_lock(&vscand_cfg_mutex
);
620 while (pthread_cond_wait(&vscand_cfg_cv
, &vscand_cfg_mutex
) == 0) {
621 if (vscand_state
== VS_STATE_SHUTDOWN
)
624 (void) vscand_configure();
627 (void) pthread_mutex_unlock(&vscand_cfg_mutex
);
637 vscand_configure(void)
641 vs_props_all_t config
;
643 (void) memset(&config
, 0, sizeof (vs_props_all_t
));
644 if (vs_props_get_all(&config
) != VS_ERR_NONE
) {
645 vscand_error(gettext("configuration data error"));
649 (void) memset(&kconfig
, 0, sizeof (vs_config_t
));
650 len
= sizeof (kconfig
.vsc_types
);
651 if (vs_parse_types(config
.va_props
.vp_types
,
652 kconfig
.vsc_types
, &len
) != 0) {
653 vscand_error(gettext("configuration data error - types"));
656 kconfig
.vsc_types_len
= len
;
658 /* Convert the maxfsize string from the configuration into bytes */
659 if (vs_strtonum(config
.va_props
.vp_maxsize
,
660 &kconfig
.vsc_max_size
) != 0) {
661 vscand_error(gettext("configuration data error - max-size"));
664 kconfig
.vsc_allow
= config
.va_props
.vp_maxsize_action
? 1LL : 0LL;
666 /* Send configuration update to kernel */
667 if (vscand_kernel_config(&kconfig
) != 0) {
671 /* dtrace the configuration data */
672 vscand_dtrace_cfg(&config
);
674 /* propagate configuration changes */
675 vs_eng_config(&config
);
676 vs_stats_config(&config
);
686 vscand_get_state(void)
688 return (vscand_state
);
693 * vscand_get_viruslog
696 vscand_get_viruslog()
702 propids
= VS_PROPID_VLOG
;
703 if ((rc
= vs_props_get(&props
, propids
)) != VS_ERR_NONE
) {
704 vscand_error(vs_strerror(rc
));
708 (void) strlcpy(vscand_vlog
, props
.vp_vlog
, sizeof (vscand_vlog
));
717 vscand_viruslog(void)
719 if (vscand_vlog
[0] == '\0')
722 return (vscand_vlog
);
730 vscand_kernel_bind(void)
732 char devname
[MAXPATHLEN
];
735 (void) snprintf(devname
, MAXPATHLEN
, "%s%d", VS_DRV_PATH
, inst
);
737 if ((vscand_kdrv_fd
= open(devname
, O_RDONLY
)) < 0) {
738 vscand_error(gettext("failed to bind to kernel"));
747 * vscand_kernel_unbind
750 vscand_kernel_unbind(void)
752 if (vscand_kdrv_fd
>= 0)
753 (void) close(vscand_kdrv_fd
);
758 * vscand_kernel_enable
761 vscand_kernel_enable(int door_fd
)
763 if (ioctl(vscand_kdrv_fd
, VS_IOCTL_ENABLE
, door_fd
) < 0) {
764 vscand_error(gettext("failed to bind to kernel"));
765 (void) close(vscand_kdrv_fd
);
774 * vscand_kernel_disable
777 vscand_kernel_disable()
779 if (vscand_kdrv_fd
>= 0)
780 (void) ioctl(vscand_kdrv_fd
, VS_IOCTL_DISABLE
);
785 * vscand_kernel_config
788 vscand_kernel_config(vs_config_t
*conf
)
790 if ((vscand_kdrv_fd
< 0) ||
791 (ioctl(vscand_kdrv_fd
, VS_IOCTL_CONFIG
, conf
) < 0)) {
792 vscand_error(gettext("failed to send config to kernel"));
801 * vscand_kernel_result
804 vscand_kernel_result(vs_scan_rsp_t
*scan_rsp
)
806 if ((vscand_kdrv_fd
< 0) ||
807 (ioctl(vscand_kdrv_fd
, VS_IOCTL_RESULT
, scan_rsp
) < 0)) {
808 vscand_error(gettext("failed to send result to kernel"));
817 * vscand_kernel_max_req
820 vscand_kernel_max_req(uint32_t *max_req
)
822 if ((vscand_kdrv_fd
< 0) ||
823 (ioctl(vscand_kdrv_fd
, VS_IOCTL_MAX_REQ
, max_req
) < 0)) {
824 vscand_error(gettext("failed to get config data from kernel"));
833 * vscand_set_resource_limits
835 * If the process's max file descriptor limit is less than
836 * VS_FILE_DESCRIPTORS, increae it to VS_FILE_DESCRIPTORS.
839 vscand_set_resource_limits(void)
843 char *limit
= "process.max-file-descriptor";
845 rblk
= (rctlblk_t
*)malloc(rctlblk_size());
848 rc
= getrctl(limit
, NULL
, rblk
, 0);
850 (rctlblk_get_value(rblk
) < VS_FILE_DESCRIPTORS
)) {
851 rctlblk_set_value(rblk
, VS_FILE_DESCRIPTORS
);
852 rc
= setrctl(limit
, NULL
, rblk
, 0);
865 vscand_error(const char *errmsg
)
867 (void) fprintf(stderr
, "vscand: %s", errmsg
);
868 syslog(LOG_ERR
, "%s\n", errmsg
);
877 * Support for dtracing vscand configuration when processing
878 * a reconfiguration event (SIGHUP)
882 vscand_dtrace_eng(char *id
, boolean_t enable
, char *host
, int port
, int conn
)
887 vscand_dtrace_gen(char *size
, boolean_t action
, char *types
, char *log
)
891 vscand_dtrace_cfg(vs_props_all_t
*config
)
895 vscand_dtrace_gen(config
->va_props
.vp_maxsize
,
896 config
->va_props
.vp_maxsize_action
,
897 config
->va_props
.vp_types
,
898 config
->va_props
.vp_vlog
);
900 for (i
= 0; i
< VS_SE_MAX
; i
++) {
901 if (config
->va_se
[i
].vep_engid
[0] != 0)
902 vscand_dtrace_eng(config
->va_se
[i
].vep_engid
,
903 config
->va_se
[i
].vep_enable
,
904 config
->va_se
[i
].vep_host
,
905 config
->va_se
[i
].vep_port
,
906 config
->va_se
[i
].vep_maxconn
);