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