dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / vscan / vscand / vs_svc.c
blob4985d4af62fc8fb96af586d2c5915479bbf8cd0f
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Implementation of the "scan file" interface
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <syslog.h>
36 #include <sys/types.h>
37 #include <fcntl.h>
38 #include <bsm/adt.h>
39 #include <bsm/adt_event.h>
40 #include <pthread.h>
42 #include "vs_incl.h"
45 * vs_svc_nodes - table of scan requests and their thread id and
46 * scan engine context.
47 * The table is sized by the value passed to vs_svc_init. This
48 * value is obtained from the kernel and represents the maximum
49 * request idx that the kernel will request vscand to process.
50 * The table is indexed by the vsr_idx value passed in
51 * the scan request - always non-zero. This value is also the index
52 * into the kernel scan request table and identifies the instance of
53 * the driver being used to access file data for the scan. Although
54 * this is of no consequence here, it is useful information for debug.
56 * When a scan request is received a response is sent indicating
57 * one of the following:
58 * VS_STATUS_ERROR - an error occurred
59 * VS_STATUS_NO_SCAN - no scan is required
60 * VS_STATUS_SCANNING - request has been queued for async processing
62 * If the scan is required (VS_STATUS_SCANNING) a thread is created
63 * to perform the scan. It's tid is saved in vs_svc_nodes.
65 * In the case of SHUTDOWN, vs_terminate requests that all scan
66 * engine connections be closed, thus termintaing any in-progress
67 * scans, then awaits completion of all scanning threads as identified
68 * in vs_svc_nodes.
71 typedef struct vs_svc_node {
72 pthread_t vsn_tid;
73 vs_scan_req_t vsn_req;
74 vs_eng_ctx_t vsn_eng;
75 } vs_svc_node_t;
77 static vs_svc_node_t *vs_svc_nodes;
78 static uint32_t vs_svc_max_node; /* max idx into vs_svc_nodes */
79 static pthread_mutex_t vs_svc_mutex = PTHREAD_MUTEX_INITIALIZER;
82 /* local functions */
83 static void *vs_svc_async_scan(void *);
84 static int vs_svc_scan_file(vs_svc_node_t *, vs_scanstamp_t *);
85 static void vs_svc_vlog(char *, vs_result_t *);
86 static void vs_svc_audit(char *, vs_result_t *);
90 * vs_svc_init, vs_svc_fini
92 * Invoked on daemon load and unload
94 int
95 vs_svc_init(uint32_t max_req)
97 vs_svc_max_node = max_req;
98 vs_svc_nodes = (vs_svc_node_t *)
99 calloc(max_req + 1, sizeof (vs_svc_node_t));
101 return (vs_svc_nodes == NULL ? -1 : 0);
104 void
105 vs_svc_fini()
107 free(vs_svc_nodes);
112 * vs_svc_terminate
114 * Close all scan engine connections to terminate in-progress scan
115 * requests, and wait for all threads in vs_svc_nodes to complete
117 void
118 vs_svc_terminate()
120 int i;
121 pthread_t tid;
123 /* close connections to abort requests */
124 vs_eng_close_connections();
126 /* wait for threads */
127 for (i = 1; i <= vs_svc_max_node; i++) {
129 (void) pthread_mutex_lock(&vs_svc_mutex);
130 tid = vs_svc_nodes[i].vsn_tid;
131 (void) pthread_mutex_unlock(&vs_svc_mutex);
133 if (tid != 0)
134 (void) pthread_join(tid, NULL);
140 * vs_svc_queue_scan_req
142 * Determine if the file needs to be scanned - either it has
143 * been modified or its scanstamp is not current.
144 * Initiate a thread to process the request, saving the tid
145 * in vs_svc_nodes[idx].vsn_tid, where idx is the vsr_idx passed in
146 * the scan request.
148 * Returns: VS_STATUS_ERROR - error
149 * VS_STATUS_NO_SCAN - no scan required
150 * VS_STATUS_SCANNING - async scan initiated
153 vs_svc_queue_scan_req(vs_scan_req_t *req)
155 pthread_t tid;
156 vs_svc_node_t *node;
158 /* No scan if file quarantined */
159 if (req->vsr_quarantined)
160 return (VS_STATUS_NO_SCAN);
162 /* No scan if file not modified AND scanstamp is current */
163 if ((req->vsr_modified == 0) &&
164 vs_eng_scanstamp_current(req->vsr_scanstamp)) {
165 return (VS_STATUS_NO_SCAN);
168 /* scan required */
169 node = &(vs_svc_nodes[req->vsr_idx]);
171 (void) pthread_mutex_lock(&vs_svc_mutex);
172 if ((node->vsn_tid != 0) || (req->vsr_idx > vs_svc_max_node)) {
173 (void) pthread_mutex_unlock(&vs_svc_mutex);
174 return (VS_STATUS_ERROR);
177 node->vsn_req = *req;
179 if (pthread_create(&tid, NULL, vs_svc_async_scan, (void *)node) != 0) {
180 (void) pthread_mutex_unlock(&vs_svc_mutex);
181 return (VS_STATUS_ERROR);
184 node->vsn_tid = tid;
185 (void) pthread_mutex_unlock(&vs_svc_mutex);
187 return (VS_STATUS_SCANNING);
192 * vs_svc_async_scan
194 * Initialize response structure, invoke vs_svc_scan_file to
195 * perform the scan, then send the result to the kernel.
197 static void *
198 vs_svc_async_scan(void *arg)
200 vs_svc_node_t *node = (vs_svc_node_t *)arg;
201 vs_scan_req_t *scan_req = &(node->vsn_req);
202 vs_scan_rsp_t scan_rsp;
204 scan_rsp.vsr_idx = scan_req->vsr_idx;
205 scan_rsp.vsr_seqnum = scan_req->vsr_seqnum;
206 scan_rsp.vsr_result = vs_svc_scan_file(node, &scan_rsp.vsr_scanstamp);
208 /* clear node and send async response to kernel */
209 (void) pthread_mutex_lock(&vs_svc_mutex);
210 (void) memset(node, 0, sizeof (vs_svc_node_t));
211 (void) pthread_mutex_unlock(&vs_svc_mutex);
213 (void) vscand_kernel_result(&scan_rsp);
215 return (NULL);
220 * vs_svc_scan_file
222 * vs_svc_scan_file is responsible for:
223 * - obtaining & releasing a scan engine connection
224 * - invoking the scan engine interface code to do the scan
225 * - retrying a failed scan (up to VS_MAX_RETRY times)
226 * - updating scan statistics
227 * - logging virus information
230 * Returns:
231 * VS_STATUS_NO_SCAN - scan not reqd; daemon shutting down
232 * VS_STATUS_CLEAN - scan success. File clean.
233 * new scanstamp returned in scanstamp param.
234 * VS_STATUS_INFECTED - scan success. File infected.
235 * VS_STATUS_ERROR - scan failure either in vscand or scan engine.
237 static int
238 vs_svc_scan_file(vs_svc_node_t *node, vs_scanstamp_t *scanstamp)
240 char devname[MAXPATHLEN];
241 int flags = 0;
242 int retries;
243 vs_result_t result;
244 vs_scan_req_t *req = &(node->vsn_req);
245 vs_eng_ctx_t *eng = &(node->vsn_eng);
247 (void) snprintf(devname, MAXPATHLEN, "%s%d", VS_DRV_PATH, req->vsr_idx);
249 /* initialize response scanstamp to current scanstamp value */
250 (void) strlcpy(*scanstamp, req->vsr_scanstamp, sizeof (vs_scanstamp_t));
252 (void) memset(&result, 0, sizeof (vs_result_t));
253 result.vsr_rc = VS_RESULT_UNDEFINED;
255 for (retries = 0; retries <= VS_MAX_RETRY; retries++) {
256 /* get engine connection */
257 if (vs_eng_get(eng, (retries != 0)) != 0) {
258 result.vsr_rc = VS_RESULT_ERROR;
259 continue;
262 /* shutdown could occur while waiting for engine connection */
263 if (vscand_get_state() == VS_STATE_SHUTDOWN) {
264 vs_eng_release(eng);
265 return (VS_STATUS_NO_SCAN);
268 /* scan file */
269 (void) vs_icap_scan_file(eng, devname, req->vsr_path,
270 req->vsr_size, flags, &result);
272 /* if no error, clear error state on engine and break */
273 if ((result.vsr_rc != VS_RESULT_SE_ERROR) &&
274 (result.vsr_rc != VS_RESULT_ERROR)) {
275 vs_eng_set_error(eng, 0);
276 vs_eng_release(eng);
277 break;
280 /* treat error on shutdown as scan not required */
281 if (vscand_get_state() == VS_STATE_SHUTDOWN) {
282 vs_eng_release(eng);
283 return (VS_STATUS_NO_SCAN);
286 /* set engine's error state and update engine stats */
287 if (result.vsr_rc == VS_RESULT_SE_ERROR)
288 vs_eng_set_error(eng, 1);
290 vs_eng_release(eng);
293 vs_stats_set(result.vsr_rc);
296 * VS_RESULT_CLEANED - file infected, cleaned data available
297 * VS_RESULT_FORBIDDEN - file infected, no cleaned data
298 * Log virus, write audit record and return INFECTED status
300 if (result.vsr_rc == VS_RESULT_CLEANED ||
301 result.vsr_rc == VS_RESULT_FORBIDDEN) {
302 vs_svc_vlog(req->vsr_path, &result);
303 vs_svc_audit(req->vsr_path, &result);
304 return (VS_STATUS_INFECTED);
307 /* VS_RESULT_CLEAN - Set the scanstamp and return CLEAN status */
308 if (result.vsr_rc == VS_RESULT_CLEAN) {
309 (void) strlcpy(*scanstamp, result.vsr_scanstamp,
310 sizeof (vs_scanstamp_t));
311 return (VS_STATUS_CLEAN);
314 return (VS_STATUS_ERROR);
319 * vs_svc_vlog
321 * log details of infections detected in syslig
322 * If virus log is configured log details there too
324 static void
325 vs_svc_vlog(char *filepath, vs_result_t *result)
327 FILE *fp = NULL;
328 time_t sec;
329 struct tm *timestamp;
330 char timebuf[18]; /* MM/DD/YY hh:mm:ss */
331 int i;
332 char *log;
334 /* syslog */
335 if (result->vsr_nviolations == 0) {
336 syslog(LOG_NOTICE, "quarantine %s\n", filepath);
337 } else {
338 for (i = 0; i < result->vsr_nviolations; i++) {
339 syslog(LOG_NOTICE, "quarantine %s %d - %s\n",
340 filepath,
341 result->vsr_vrec[i].vr_id,
342 result->vsr_vrec[i].vr_desc);
346 /* log file */
347 if (((log = vscand_viruslog()) == NULL) ||
348 ((fp = fopen(log, "a")) == NULL)) {
349 return;
352 (void) time(&sec);
353 timestamp = localtime(&sec);
354 (void) strftime(timebuf, sizeof (timebuf), "%D %T", timestamp);
356 if (result->vsr_nviolations == 0) {
357 (void) fprintf(fp, "%s quarantine %d[%s]\n",
358 timebuf, strlen(filepath), filepath);
359 } else {
360 for (i = 0; i < result->vsr_nviolations; i++) {
361 (void) fprintf(fp, "%s quarantine %d[%s] %d - %d[%s]\n",
362 timebuf, strlen(filepath), filepath,
363 result->vsr_vrec[i].vr_id,
364 strlen(result->vsr_vrec[i].vr_desc),
365 result->vsr_vrec[i].vr_desc);
369 (void) fclose(fp);
374 * vs_svc_audit
376 * Generate AUE_vscan_quarantine audit record containing name
377 * of infected file, and violation details if available.
379 static void
380 vs_svc_audit(char *filepath, vs_result_t *result)
382 int i;
383 char *violations[VS_MAX_VIOLATIONS];
384 char data[VS_MAX_VIOLATIONS][VS_DESCRIPTION_MAX];
385 adt_session_data_t *ah;
386 adt_termid_t *p_tid;
387 adt_event_data_t *event;
389 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) {
390 syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m");
391 return;
394 if (adt_load_ttyname("/dev/console", &p_tid) != 0) {
395 syslog(LOG_AUTH | LOG_ALERT,
396 "adt_load_ttyname(/dev/console): %m");
397 return;
400 if (adt_set_user(ah, ADT_NO_ATTRIB, ADT_NO_ATTRIB, ADT_NO_ATTRIB,
401 ADT_NO_ATTRIB, p_tid, ADT_NEW) != 0) {
402 syslog(LOG_AUTH | LOG_ALERT, "adt_set_user(ADT_NO_ATTRIB): %m");
403 (void) adt_end_session(ah);
404 return;
407 if ((event = adt_alloc_event(ah, ADT_vscan_quarantine)) == NULL) {
408 syslog(LOG_AUTH | LOG_ALERT,
409 "adt_alloc_event(ADT_vscan_quarantine)): %m");
410 (void) adt_end_session(ah);
411 return;
414 /* populate vscan audit event */
415 event->adt_vscan_quarantine.file = filepath;
416 for (i = 0; i < result->vsr_nviolations; i++) {
417 (void) snprintf(data[i], VS_DESCRIPTION_MAX, "%d - %s",
418 result->vsr_vrec[i].vr_id, result->vsr_vrec[i].vr_desc);
419 violations[i] = data[i];
422 event->adt_vscan_quarantine.violations = (char **)violations;
423 event->adt_vscan_quarantine.nviolations = result->vsr_nviolations;
425 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS))
426 syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m");
428 adt_free_event(event);
429 (void) adt_end_session(ah);