15440Sjm199354 /*
25440Sjm199354 * CDDL HEADER START
35440Sjm199354 *
45440Sjm199354 * The contents of this file are subject to the terms of the
55440Sjm199354 * Common Development and Distribution License (the "License").
65440Sjm199354 * You may not use this file except in compliance with the License.
75440Sjm199354 *
85440Sjm199354 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95440Sjm199354 * or http://www.opensolaris.org/os/licensing.
105440Sjm199354 * See the License for the specific language governing permissions
115440Sjm199354 * and limitations under the License.
125440Sjm199354 *
135440Sjm199354 * When distributing Covered Code, include this CDDL HEADER in each
145440Sjm199354 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155440Sjm199354 * If applicable, add the following below this CDDL HEADER, with the
165440Sjm199354 * fields enclosed by brackets "[]" replaced with your own identifying
175440Sjm199354 * information: Portions Copyright [yyyy] [name of copyright owner]
185440Sjm199354 *
195440Sjm199354 * CDDL HEADER END
205440Sjm199354 */
215440Sjm199354 /*
225931Sjm199354 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
235440Sjm199354 * Use is subject to license terms.
245440Sjm199354 */
255440Sjm199354
265440Sjm199354 #pragma ident "%Z%%M% %I% %E% SMI"
275440Sjm199354
285440Sjm199354 /*
295440Sjm199354 * Implementation of the "scan file" interface
305440Sjm199354 */
315440Sjm199354
325440Sjm199354 #include <stdio.h>
335440Sjm199354 #include <stdlib.h>
345440Sjm199354 #include <unistd.h>
355440Sjm199354 #include <string.h>
365440Sjm199354 #include <errno.h>
375440Sjm199354 #include <syslog.h>
385440Sjm199354 #include <sys/types.h>
395440Sjm199354 #include <fcntl.h>
405440Sjm199354 #include <bsm/adt.h>
415440Sjm199354 #include <bsm/adt_event.h>
426407Sjm199354 #include <pthread.h>
435440Sjm199354
445440Sjm199354 #include "vs_incl.h"
455440Sjm199354
466407Sjm199354 /*
476407Sjm199354 * vs_svc_nodes - table of scan requests and their thread id and
486407Sjm199354 * scan engine context.
496407Sjm199354 * The table is sized by the value passed to vs_svc_init. This
506407Sjm199354 * value is obtained from the kernel and represents the maximum
516407Sjm199354 * request idx that the kernel will request vscand to process.
526407Sjm199354 * The table is indexed by the vsr_idx value passed in
536407Sjm199354 * the scan request - always non-zero. This value is also the index
546407Sjm199354 * into the kernel scan request table and identifies the instance of
556407Sjm199354 * the driver being used to access file data for the scan. Although
566407Sjm199354 * this is of no consequence here, it is useful information for debug.
576407Sjm199354 *
586407Sjm199354 * When a scan request is received a response is sent indicating
596407Sjm199354 * one of the following:
606407Sjm199354 * VS_STATUS_ERROR - an error occurred
616407Sjm199354 * VS_STATUS_NO_SCAN - no scan is required
626407Sjm199354 * VS_STATUS_SCANNING - request has been queued for async processing
636407Sjm199354 *
646407Sjm199354 * If the scan is required (VS_STATUS_SCANNING) a thread is created
656407Sjm199354 * to perform the scan. It's tid is saved in vs_svc_nodes.
666407Sjm199354 *
676407Sjm199354 * In the case of SHUTDOWN, vs_terminate requests that all scan
686407Sjm199354 * engine connections be closed, thus termintaing any in-progress
696407Sjm199354 * scans, then awaits completion of all scanning threads as identified
706407Sjm199354 * in vs_svc_nodes.
716407Sjm199354 */
726407Sjm199354
736407Sjm199354 typedef struct vs_svc_node {
746407Sjm199354 pthread_t vsn_tid;
756407Sjm199354 vs_scan_req_t vsn_req;
766407Sjm199354 vs_eng_ctx_t vsn_eng;
776407Sjm199354 } vs_svc_node_t;
786407Sjm199354
796407Sjm199354 static vs_svc_node_t *vs_svc_nodes;
806407Sjm199354 static uint32_t vs_svc_max_node; /* max idx into vs_svc_nodes */
816407Sjm199354 static pthread_mutex_t vs_svc_mutex = PTHREAD_MUTEX_INITIALIZER;
826407Sjm199354
836407Sjm199354
845440Sjm199354 /* local functions */
856407Sjm199354 static void *vs_svc_async_scan(void *);
866407Sjm199354 static int vs_svc_scan_file(vs_svc_node_t *, vs_scanstamp_t *);
875440Sjm199354 static void vs_svc_vlog(char *, vs_result_t *);
885440Sjm199354 static void vs_svc_audit(char *, vs_result_t *);
895440Sjm199354
906407Sjm199354
915440Sjm199354 /*
925440Sjm199354 * vs_svc_init, vs_svc_fini
935440Sjm199354 *
945440Sjm199354 * Invoked on daemon load and unload
955440Sjm199354 */
966407Sjm199354 int
vs_svc_init(uint32_t max_req)976407Sjm199354 vs_svc_init(uint32_t max_req)
985440Sjm199354 {
996407Sjm199354 vs_svc_max_node = max_req;
1006407Sjm199354 vs_svc_nodes = (vs_svc_node_t *)
1016407Sjm199354 calloc(max_req + 1, sizeof (vs_svc_node_t));
1026407Sjm199354
1036407Sjm199354 return (vs_svc_nodes == NULL ? -1 : 0);
1045440Sjm199354 }
1055440Sjm199354
1065440Sjm199354 void
vs_svc_fini()1075440Sjm199354 vs_svc_fini()
1085440Sjm199354 {
1096407Sjm199354 if (vs_svc_nodes)
1106407Sjm199354 free(vs_svc_nodes);
1116407Sjm199354 }
1126407Sjm199354
1136407Sjm199354
1146407Sjm199354 /*
1156407Sjm199354 * vs_svc_terminate
1166407Sjm199354 *
1176407Sjm199354 * Close all scan engine connections to terminate in-progress scan
1186407Sjm199354 * requests, and wait for all threads in vs_svc_nodes to complete
1196407Sjm199354 */
1206407Sjm199354 void
vs_svc_terminate()1216407Sjm199354 vs_svc_terminate()
1226407Sjm199354 {
1236407Sjm199354 int i;
1246407Sjm199354 pthread_t tid;
1256407Sjm199354
1266407Sjm199354 /* close connections to abort requests */
1276407Sjm199354 vs_eng_close_connections();
1286407Sjm199354
1296407Sjm199354 /* wait for threads */
1306407Sjm199354 for (i = 1; i <= vs_svc_max_node; i++) {
1316407Sjm199354
1326407Sjm199354 (void) pthread_mutex_lock(&vs_svc_mutex);
1336407Sjm199354 tid = vs_svc_nodes[i].vsn_tid;
1346407Sjm199354 (void) pthread_mutex_unlock(&vs_svc_mutex);
1356407Sjm199354
1366407Sjm199354 if (tid != 0)
1376407Sjm199354 (void) pthread_join(tid, NULL);
1386407Sjm199354 }
1396407Sjm199354 }
1406407Sjm199354
1416407Sjm199354
1426407Sjm199354 /*
1436407Sjm199354 * vs_svc_queue_scan_req
1446407Sjm199354 *
1456407Sjm199354 * Determine if the file needs to be scanned - either it has
1466407Sjm199354 * been modified or its scanstamp is not current.
1476407Sjm199354 * Initiate a thread to process the request, saving the tid
1486407Sjm199354 * in vs_svc_nodes[idx].vsn_tid, where idx is the vsr_idx passed in
1496407Sjm199354 * the scan request.
1506407Sjm199354 *
1516407Sjm199354 * Returns: VS_STATUS_ERROR - error
1526407Sjm199354 * VS_STATUS_NO_SCAN - no scan required
1536407Sjm199354 * VS_STATUS_SCANNING - async scan initiated
1546407Sjm199354 */
1556407Sjm199354 int
vs_svc_queue_scan_req(vs_scan_req_t * req)1566407Sjm199354 vs_svc_queue_scan_req(vs_scan_req_t *req)
1576407Sjm199354 {
1586407Sjm199354 pthread_t tid;
1596407Sjm199354 vs_svc_node_t *node;
1606407Sjm199354
1616407Sjm199354 /* No scan if file quarantined */
1626407Sjm199354 if (req->vsr_quarantined)
1636407Sjm199354 return (VS_STATUS_NO_SCAN);
1646407Sjm199354
1656407Sjm199354 /* No scan if file not modified AND scanstamp is current */
1666407Sjm199354 if ((req->vsr_modified == 0) &&
1676407Sjm199354 vs_eng_scanstamp_current(req->vsr_scanstamp)) {
1686407Sjm199354 return (VS_STATUS_NO_SCAN);
1696407Sjm199354 }
1706407Sjm199354
1716407Sjm199354 /* scan required */
1726407Sjm199354 node = &(vs_svc_nodes[req->vsr_idx]);
1736407Sjm199354
1746407Sjm199354 (void) pthread_mutex_lock(&vs_svc_mutex);
1756407Sjm199354 if ((node->vsn_tid != 0) || (req->vsr_idx > vs_svc_max_node)) {
1766407Sjm199354 (void) pthread_mutex_unlock(&vs_svc_mutex);
1776407Sjm199354 return (VS_STATUS_ERROR);
1786407Sjm199354 }
1796407Sjm199354
1806407Sjm199354 node->vsn_req = *req;
1816407Sjm199354
1826407Sjm199354 if (pthread_create(&tid, NULL, vs_svc_async_scan, (void *)node) != 0) {
1836407Sjm199354 (void) pthread_mutex_unlock(&vs_svc_mutex);
1846407Sjm199354 return (VS_STATUS_ERROR);
1856407Sjm199354 }
1866407Sjm199354
1876407Sjm199354 node->vsn_tid = tid;
1886407Sjm199354 (void) pthread_mutex_unlock(&vs_svc_mutex);
1896407Sjm199354
1906407Sjm199354 return (VS_STATUS_SCANNING);
1916407Sjm199354 }
1926407Sjm199354
1936407Sjm199354
1946407Sjm199354 /*
1956407Sjm199354 * vs_svc_async_scan
1966407Sjm199354 *
1976407Sjm199354 * Initialize response structure, invoke vs_svc_scan_file to
1986407Sjm199354 * perform the scan, then send the result to the kernel.
1996407Sjm199354 */
2006407Sjm199354 static void *
vs_svc_async_scan(void * arg)2016407Sjm199354 vs_svc_async_scan(void *arg)
2026407Sjm199354 {
2036407Sjm199354 vs_svc_node_t *node = (vs_svc_node_t *)arg;
2046407Sjm199354 vs_scan_req_t *scan_req = &(node->vsn_req);
2056407Sjm199354 vs_scan_rsp_t scan_rsp;
2066407Sjm199354
2076407Sjm199354 scan_rsp.vsr_idx = scan_req->vsr_idx;
2086407Sjm199354 scan_rsp.vsr_seqnum = scan_req->vsr_seqnum;
2096407Sjm199354 scan_rsp.vsr_result = vs_svc_scan_file(node, &scan_rsp.vsr_scanstamp);
2106407Sjm199354
2116407Sjm199354 /* clear node and send async response to kernel */
2126407Sjm199354 (void) pthread_mutex_lock(&vs_svc_mutex);
2136407Sjm199354 (void) memset(node, 0, sizeof (vs_svc_node_t));
2146407Sjm199354 (void) pthread_mutex_unlock(&vs_svc_mutex);
2156407Sjm199354
2166407Sjm199354 (void) vscand_kernel_result(&scan_rsp);
2176407Sjm199354
2186407Sjm199354 return (NULL);
2195440Sjm199354 }
2205440Sjm199354
2215440Sjm199354
2225440Sjm199354 /*
2235440Sjm199354 * vs_svc_scan_file
2245440Sjm199354 *
2255440Sjm199354 * vs_svc_scan_file is responsible for:
2265440Sjm199354 * - obtaining & releasing a scan engine connection
2275440Sjm199354 * - invoking the scan engine interface code to do the scan
2285440Sjm199354 * - retrying a failed scan (up to VS_MAX_RETRY times)
2295440Sjm199354 * - updating scan statistics
2305440Sjm199354 * - logging virus information
2315440Sjm199354 *
2325931Sjm199354 *
2335440Sjm199354 * Returns:
2346407Sjm199354 * VS_STATUS_NO_SCAN - scan not reqd; daemon shutting down
2355931Sjm199354 * VS_STATUS_CLEAN - scan success. File clean.
2365931Sjm199354 * new scanstamp returned in scanstamp param.
2375931Sjm199354 * VS_STATUS_INFECTED - scan success. File infected.
2385931Sjm199354 * VS_STATUS_ERROR - scan failure either in vscand or scan engine.
2395440Sjm199354 */
2406407Sjm199354 static int
vs_svc_scan_file(vs_svc_node_t * node,vs_scanstamp_t * scanstamp)2416407Sjm199354 vs_svc_scan_file(vs_svc_node_t *node, vs_scanstamp_t *scanstamp)
2425440Sjm199354 {
2436407Sjm199354 char devname[MAXPATHLEN];
2446407Sjm199354 int flags = 0;
2455931Sjm199354 int retries;
2465440Sjm199354 vs_result_t result;
2476407Sjm199354 vs_scan_req_t *req = &(node->vsn_req);
2486407Sjm199354 vs_eng_ctx_t *eng = &(node->vsn_eng);
2496407Sjm199354
2506407Sjm199354 (void) snprintf(devname, MAXPATHLEN, "%s%d", VS_DRV_PATH, req->vsr_idx);
2515440Sjm199354
2525931Sjm199354 /* initialize response scanstamp to current scanstamp value */
2536407Sjm199354 (void) strlcpy(*scanstamp, req->vsr_scanstamp, sizeof (vs_scanstamp_t));
2545440Sjm199354
2555440Sjm199354 (void) memset(&result, 0, sizeof (vs_result_t));
2565440Sjm199354 result.vsr_rc = VS_RESULT_UNDEFINED;
2575440Sjm199354
2585440Sjm199354 for (retries = 0; retries <= VS_MAX_RETRY; retries++) {
2596407Sjm199354 /* get engine connection */
2606407Sjm199354 if (vs_eng_get(eng, (retries != 0)) != 0) {
2615931Sjm199354 result.vsr_rc = VS_RESULT_ERROR;
2625440Sjm199354 continue;
2635440Sjm199354 }
2645440Sjm199354
2656407Sjm199354 /* shutdown could occur while waiting for engine connection */
2666407Sjm199354 if (vscand_get_state() == VS_STATE_SHUTDOWN) {
2676407Sjm199354 vs_eng_release(eng);
2686407Sjm199354 return (VS_STATUS_NO_SCAN);
2696407Sjm199354 }
2705440Sjm199354
2716407Sjm199354 /* scan file */
2726407Sjm199354 (void) vs_icap_scan_file(eng, devname, req->vsr_path,
2736407Sjm199354 req->vsr_size, flags, &result);
2745440Sjm199354
2755440Sjm199354 /* if no error, clear error state on engine and break */
2765931Sjm199354 if ((result.vsr_rc != VS_RESULT_SE_ERROR) &&
2775931Sjm199354 (result.vsr_rc != VS_RESULT_ERROR)) {
2786407Sjm199354 vs_eng_set_error(eng, 0);
2796407Sjm199354 vs_eng_release(eng);
2805440Sjm199354 break;
2815440Sjm199354 }
2825440Sjm199354
2835931Sjm199354 /* treat error on shutdown as scan not required */
2845440Sjm199354 if (vscand_get_state() == VS_STATE_SHUTDOWN) {
2856407Sjm199354 vs_eng_release(eng);
2865931Sjm199354 return (VS_STATUS_NO_SCAN);
2875440Sjm199354 }
2885440Sjm199354
2895440Sjm199354 /* set engine's error state and update engine stats */
2906407Sjm199354 if (result.vsr_rc == VS_RESULT_SE_ERROR)
2916407Sjm199354 vs_eng_set_error(eng, 1);
2926407Sjm199354
2936407Sjm199354 vs_eng_release(eng);
2945440Sjm199354 }
2955440Sjm199354
2965931Sjm199354 vs_stats_set(result.vsr_rc);
2975440Sjm199354
2985931Sjm199354 /*
2995931Sjm199354 * VS_RESULT_CLEANED - file infected, cleaned data available
3005931Sjm199354 * VS_RESULT_FORBIDDEN - file infected, no cleaned data
3015931Sjm199354 * Log virus, write audit record and return INFECTED status
3025931Sjm199354 */
3035440Sjm199354 if (result.vsr_rc == VS_RESULT_CLEANED ||
3045440Sjm199354 result.vsr_rc == VS_RESULT_FORBIDDEN) {
3056407Sjm199354 vs_svc_vlog(req->vsr_path, &result);
3066407Sjm199354 vs_svc_audit(req->vsr_path, &result);
3075931Sjm199354 return (VS_STATUS_INFECTED);
3085440Sjm199354 }
3095440Sjm199354
3105931Sjm199354 /* VS_RESULT_CLEAN - Set the scanstamp and return CLEAN status */
3115931Sjm199354 if (result.vsr_rc == VS_RESULT_CLEAN) {
3125931Sjm199354 (void) strlcpy(*scanstamp, result.vsr_scanstamp,
3135440Sjm199354 sizeof (vs_scanstamp_t));
3145931Sjm199354 return (VS_STATUS_CLEAN);
3155440Sjm199354 }
3165440Sjm199354
3175931Sjm199354 return (VS_STATUS_ERROR);
3185440Sjm199354 }
3195440Sjm199354
3205440Sjm199354
3215440Sjm199354 /*
3225440Sjm199354 * vs_svc_vlog
3235440Sjm199354 *
3246407Sjm199354 * log details of infections detected in syslig
3256407Sjm199354 * If virus log is configured log details there too
3265440Sjm199354 */
3275440Sjm199354 static void
vs_svc_vlog(char * filepath,vs_result_t * result)3285440Sjm199354 vs_svc_vlog(char *filepath, vs_result_t *result)
3295440Sjm199354 {
3305440Sjm199354 FILE *fp = NULL;
3315440Sjm199354 time_t sec;
3325440Sjm199354 struct tm *timestamp;
3335440Sjm199354 char timebuf[18]; /* MM/DD/YY hh:mm:ss */
3345440Sjm199354 int i;
3355440Sjm199354 char *log;
3365440Sjm199354
3376407Sjm199354 /* syslog */
3385440Sjm199354 if (result->vsr_nviolations == 0) {
339*7228Sjm199354 syslog(LOG_NOTICE, "quarantine %s\n", filepath);
3405440Sjm199354 } else {
3415440Sjm199354 for (i = 0; i < result->vsr_nviolations; i++) {
342*7228Sjm199354 syslog(LOG_NOTICE, "quarantine %s %d - %s\n",
3436407Sjm199354 filepath,
3446407Sjm199354 result->vsr_vrec[i].vr_id,
3456407Sjm199354 result->vsr_vrec[i].vr_desc);
3465440Sjm199354 }
3475440Sjm199354 }
3485440Sjm199354
3496407Sjm199354 /* log file */
3506407Sjm199354 if (((log = vscand_viruslog()) == NULL) ||
3516407Sjm199354 ((fp = fopen(log, "a")) == NULL)) {
3526407Sjm199354 return;
3536407Sjm199354 }
3546407Sjm199354
3556407Sjm199354 (void) time(&sec);
3566407Sjm199354 timestamp = localtime(&sec);
3576407Sjm199354 (void) strftime(timebuf, sizeof (timebuf), "%D %T", timestamp);
3586407Sjm199354
3596407Sjm199354 if (result->vsr_nviolations == 0) {
3606407Sjm199354 (void) fprintf(fp, "%s quarantine %d[%s]\n",
3616407Sjm199354 timebuf, strlen(filepath), filepath);
3626407Sjm199354 } else {
3636407Sjm199354 for (i = 0; i < result->vsr_nviolations; i++) {
3646407Sjm199354 (void) fprintf(fp, "%s quarantine %d[%s] %d - %d[%s]\n",
3656407Sjm199354 timebuf, strlen(filepath), filepath,
3666407Sjm199354 result->vsr_vrec[i].vr_id,
3676407Sjm199354 strlen(result->vsr_vrec[i].vr_desc),
3686407Sjm199354 result->vsr_vrec[i].vr_desc);
3696407Sjm199354 }
3706407Sjm199354 }
3716407Sjm199354
3726407Sjm199354 (void) fclose(fp);
3735440Sjm199354 }
3745440Sjm199354
3755440Sjm199354
3765440Sjm199354 /*
3775440Sjm199354 * vs_svc_audit
3785440Sjm199354 *
3795440Sjm199354 * Generate AUE_vscan_quarantine audit record containing name
3805440Sjm199354 * of infected file, and violation details if available.
3815440Sjm199354 */
3825440Sjm199354 static void
vs_svc_audit(char * filepath,vs_result_t * result)3835440Sjm199354 vs_svc_audit(char *filepath, vs_result_t *result)
3845440Sjm199354 {
3855440Sjm199354 int i;
3865440Sjm199354 char *violations[VS_MAX_VIOLATIONS];
3875440Sjm199354 char data[VS_MAX_VIOLATIONS][VS_DESCRIPTION_MAX];
3885440Sjm199354 adt_session_data_t *ah;
3895440Sjm199354 adt_termid_t *p_tid;
3905440Sjm199354 adt_event_data_t *event;
3915440Sjm199354
3925440Sjm199354 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) {
3935440Sjm199354 syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m");
3945440Sjm199354 return;
3955440Sjm199354 }
3965440Sjm199354
3975440Sjm199354 if (adt_load_ttyname("/dev/console", &p_tid) != 0) {
3985440Sjm199354 syslog(LOG_AUTH | LOG_ALERT,
3995440Sjm199354 "adt_load_ttyname(/dev/console): %m");
4005440Sjm199354 return;
4015440Sjm199354 }
4025440Sjm199354
4035440Sjm199354 if (adt_set_user(ah, ADT_NO_ATTRIB, ADT_NO_ATTRIB, ADT_NO_ATTRIB,
4045440Sjm199354 ADT_NO_ATTRIB, p_tid, ADT_NEW) != 0) {
4055440Sjm199354 syslog(LOG_AUTH | LOG_ALERT, "adt_set_user(ADT_NO_ATTRIB): %m");
4065440Sjm199354 (void) adt_end_session(ah);
4075440Sjm199354 return;
4085440Sjm199354 }
4095440Sjm199354
4105440Sjm199354 if ((event = adt_alloc_event(ah, ADT_vscan_quarantine)) == NULL) {
4115440Sjm199354 syslog(LOG_AUTH | LOG_ALERT,
4125440Sjm199354 "adt_alloc_event(ADT_vscan_quarantine)): %m");
4135440Sjm199354 (void) adt_end_session(ah);
4145440Sjm199354 return;
4155440Sjm199354 }
4165440Sjm199354
4175440Sjm199354 /* populate vscan audit event */
4185440Sjm199354 event->adt_vscan_quarantine.file = filepath;
4195440Sjm199354 for (i = 0; i < result->vsr_nviolations; i++) {
4205440Sjm199354 (void) snprintf(data[i], VS_DESCRIPTION_MAX, "%d - %s",
4215440Sjm199354 result->vsr_vrec[i].vr_id, result->vsr_vrec[i].vr_desc);
4225440Sjm199354 violations[i] = data[i];
4235440Sjm199354 }
4245440Sjm199354
4255440Sjm199354 event->adt_vscan_quarantine.violations = (char **)violations;
4265440Sjm199354 event->adt_vscan_quarantine.nviolations = result->vsr_nviolations;
4275440Sjm199354
4285440Sjm199354 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS))
4295440Sjm199354 syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m");
4305440Sjm199354
4315440Sjm199354 adt_free_event(event);
4325440Sjm199354 (void) adt_end_session(ah);
4335440Sjm199354 }
434