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.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * Implementation of the "scan file" interface
38 #include <sys/types.h>
41 #include <bsm/adt_event.h>
47 * vs_svc_nodes - table of scan requests and their thread id and
48 * scan engine context.
49 * The table is sized by the value passed to vs_svc_init. This
50 * value is obtained from the kernel and represents the maximum
51 * request idx that the kernel will request vscand to process.
52 * The table is indexed by the vsr_idx value passed in
53 * the scan request - always non-zero. This value is also the index
54 * into the kernel scan request table and identifies the instance of
55 * the driver being used to access file data for the scan. Although
56 * this is of no consequence here, it is useful information for debug.
58 * When a scan request is received a response is sent indicating
59 * one of the following:
60 * VS_STATUS_ERROR - an error occurred
61 * VS_STATUS_NO_SCAN - no scan is required
62 * VS_STATUS_SCANNING - request has been queued for async processing
64 * If the scan is required (VS_STATUS_SCANNING) a thread is created
65 * to perform the scan. It's tid is saved in vs_svc_nodes.
67 * In the case of SHUTDOWN, vs_terminate requests that all scan
68 * engine connections be closed, thus termintaing any in-progress
69 * scans, then awaits completion of all scanning threads as identified
73 typedef struct vs_svc_node
{
75 vs_scan_req_t vsn_req
;
79 static vs_svc_node_t
*vs_svc_nodes
;
80 static uint32_t vs_svc_max_node
; /* max idx into vs_svc_nodes */
81 static pthread_mutex_t vs_svc_mutex
= PTHREAD_MUTEX_INITIALIZER
;
85 static void *vs_svc_async_scan(void *);
86 static int vs_svc_scan_file(vs_svc_node_t
*, vs_scanstamp_t
*);
87 static void vs_svc_vlog(char *, vs_result_t
*);
88 static void vs_svc_audit(char *, vs_result_t
*);
92 * vs_svc_init, vs_svc_fini
94 * Invoked on daemon load and unload
97 vs_svc_init(uint32_t max_req
)
99 vs_svc_max_node
= max_req
;
100 vs_svc_nodes
= (vs_svc_node_t
*)
101 calloc(max_req
+ 1, sizeof (vs_svc_node_t
));
103 return (vs_svc_nodes
== NULL
? -1 : 0);
117 * Close all scan engine connections to terminate in-progress scan
118 * requests, and wait for all threads in vs_svc_nodes to complete
126 /* close connections to abort requests */
127 vs_eng_close_connections();
129 /* wait for threads */
130 for (i
= 1; i
<= vs_svc_max_node
; i
++) {
132 (void) pthread_mutex_lock(&vs_svc_mutex
);
133 tid
= vs_svc_nodes
[i
].vsn_tid
;
134 (void) pthread_mutex_unlock(&vs_svc_mutex
);
137 (void) pthread_join(tid
, NULL
);
143 * vs_svc_queue_scan_req
145 * Determine if the file needs to be scanned - either it has
146 * been modified or its scanstamp is not current.
147 * Initiate a thread to process the request, saving the tid
148 * in vs_svc_nodes[idx].vsn_tid, where idx is the vsr_idx passed in
151 * Returns: VS_STATUS_ERROR - error
152 * VS_STATUS_NO_SCAN - no scan required
153 * VS_STATUS_SCANNING - async scan initiated
156 vs_svc_queue_scan_req(vs_scan_req_t
*req
)
161 /* No scan if file quarantined */
162 if (req
->vsr_quarantined
)
163 return (VS_STATUS_NO_SCAN
);
165 /* No scan if file not modified AND scanstamp is current */
166 if ((req
->vsr_modified
== 0) &&
167 vs_eng_scanstamp_current(req
->vsr_scanstamp
)) {
168 return (VS_STATUS_NO_SCAN
);
172 node
= &(vs_svc_nodes
[req
->vsr_idx
]);
174 (void) pthread_mutex_lock(&vs_svc_mutex
);
175 if ((node
->vsn_tid
!= 0) || (req
->vsr_idx
> vs_svc_max_node
)) {
176 (void) pthread_mutex_unlock(&vs_svc_mutex
);
177 return (VS_STATUS_ERROR
);
180 node
->vsn_req
= *req
;
182 if (pthread_create(&tid
, NULL
, vs_svc_async_scan
, (void *)node
) != 0) {
183 (void) pthread_mutex_unlock(&vs_svc_mutex
);
184 return (VS_STATUS_ERROR
);
188 (void) pthread_mutex_unlock(&vs_svc_mutex
);
190 return (VS_STATUS_SCANNING
);
197 * Initialize response structure, invoke vs_svc_scan_file to
198 * perform the scan, then send the result to the kernel.
201 vs_svc_async_scan(void *arg
)
203 vs_svc_node_t
*node
= (vs_svc_node_t
*)arg
;
204 vs_scan_req_t
*scan_req
= &(node
->vsn_req
);
205 vs_scan_rsp_t scan_rsp
;
207 scan_rsp
.vsr_idx
= scan_req
->vsr_idx
;
208 scan_rsp
.vsr_seqnum
= scan_req
->vsr_seqnum
;
209 scan_rsp
.vsr_result
= vs_svc_scan_file(node
, &scan_rsp
.vsr_scanstamp
);
211 /* clear node and send async response to kernel */
212 (void) pthread_mutex_lock(&vs_svc_mutex
);
213 (void) memset(node
, 0, sizeof (vs_svc_node_t
));
214 (void) pthread_mutex_unlock(&vs_svc_mutex
);
216 (void) vscand_kernel_result(&scan_rsp
);
225 * vs_svc_scan_file is responsible for:
226 * - obtaining & releasing a scan engine connection
227 * - invoking the scan engine interface code to do the scan
228 * - retrying a failed scan (up to VS_MAX_RETRY times)
229 * - updating scan statistics
230 * - logging virus information
234 * VS_STATUS_NO_SCAN - scan not reqd; daemon shutting down
235 * VS_STATUS_CLEAN - scan success. File clean.
236 * new scanstamp returned in scanstamp param.
237 * VS_STATUS_INFECTED - scan success. File infected.
238 * VS_STATUS_ERROR - scan failure either in vscand or scan engine.
241 vs_svc_scan_file(vs_svc_node_t
*node
, vs_scanstamp_t
*scanstamp
)
243 char devname
[MAXPATHLEN
];
247 vs_scan_req_t
*req
= &(node
->vsn_req
);
248 vs_eng_ctx_t
*eng
= &(node
->vsn_eng
);
250 (void) snprintf(devname
, MAXPATHLEN
, "%s%d", VS_DRV_PATH
, req
->vsr_idx
);
252 /* initialize response scanstamp to current scanstamp value */
253 (void) strlcpy(*scanstamp
, req
->vsr_scanstamp
, sizeof (vs_scanstamp_t
));
255 (void) memset(&result
, 0, sizeof (vs_result_t
));
256 result
.vsr_rc
= VS_RESULT_UNDEFINED
;
258 for (retries
= 0; retries
<= VS_MAX_RETRY
; retries
++) {
259 /* get engine connection */
260 if (vs_eng_get(eng
, (retries
!= 0)) != 0) {
261 result
.vsr_rc
= VS_RESULT_ERROR
;
265 /* shutdown could occur while waiting for engine connection */
266 if (vscand_get_state() == VS_STATE_SHUTDOWN
) {
268 return (VS_STATUS_NO_SCAN
);
272 (void) vs_icap_scan_file(eng
, devname
, req
->vsr_path
,
273 req
->vsr_size
, flags
, &result
);
275 /* if no error, clear error state on engine and break */
276 if ((result
.vsr_rc
!= VS_RESULT_SE_ERROR
) &&
277 (result
.vsr_rc
!= VS_RESULT_ERROR
)) {
278 vs_eng_set_error(eng
, 0);
283 /* treat error on shutdown as scan not required */
284 if (vscand_get_state() == VS_STATE_SHUTDOWN
) {
286 return (VS_STATUS_NO_SCAN
);
289 /* set engine's error state and update engine stats */
290 if (result
.vsr_rc
== VS_RESULT_SE_ERROR
)
291 vs_eng_set_error(eng
, 1);
296 vs_stats_set(result
.vsr_rc
);
299 * VS_RESULT_CLEANED - file infected, cleaned data available
300 * VS_RESULT_FORBIDDEN - file infected, no cleaned data
301 * Log virus, write audit record and return INFECTED status
303 if (result
.vsr_rc
== VS_RESULT_CLEANED
||
304 result
.vsr_rc
== VS_RESULT_FORBIDDEN
) {
305 vs_svc_vlog(req
->vsr_path
, &result
);
306 vs_svc_audit(req
->vsr_path
, &result
);
307 return (VS_STATUS_INFECTED
);
310 /* VS_RESULT_CLEAN - Set the scanstamp and return CLEAN status */
311 if (result
.vsr_rc
== VS_RESULT_CLEAN
) {
312 (void) strlcpy(*scanstamp
, result
.vsr_scanstamp
,
313 sizeof (vs_scanstamp_t
));
314 return (VS_STATUS_CLEAN
);
317 return (VS_STATUS_ERROR
);
324 * log details of infections detected in syslig
325 * If virus log is configured log details there too
328 vs_svc_vlog(char *filepath
, vs_result_t
*result
)
332 struct tm
*timestamp
;
333 char timebuf
[18]; /* MM/DD/YY hh:mm:ss */
338 if (result
->vsr_nviolations
== 0) {
339 syslog(LOG_NOTICE
, "quarantine %s\n", filepath
);
341 for (i
= 0; i
< result
->vsr_nviolations
; i
++) {
342 syslog(LOG_NOTICE
, "quarantine %s %d - %s\n",
344 result
->vsr_vrec
[i
].vr_id
,
345 result
->vsr_vrec
[i
].vr_desc
);
350 if (((log
= vscand_viruslog()) == NULL
) ||
351 ((fp
= fopen(log
, "a")) == NULL
)) {
356 timestamp
= localtime(&sec
);
357 (void) strftime(timebuf
, sizeof (timebuf
), "%D %T", timestamp
);
359 if (result
->vsr_nviolations
== 0) {
360 (void) fprintf(fp
, "%s quarantine %d[%s]\n",
361 timebuf
, strlen(filepath
), filepath
);
363 for (i
= 0; i
< result
->vsr_nviolations
; i
++) {
364 (void) fprintf(fp
, "%s quarantine %d[%s] %d - %d[%s]\n",
365 timebuf
, strlen(filepath
), filepath
,
366 result
->vsr_vrec
[i
].vr_id
,
367 strlen(result
->vsr_vrec
[i
].vr_desc
),
368 result
->vsr_vrec
[i
].vr_desc
);
379 * Generate AUE_vscan_quarantine audit record containing name
380 * of infected file, and violation details if available.
383 vs_svc_audit(char *filepath
, vs_result_t
*result
)
386 char *violations
[VS_MAX_VIOLATIONS
];
387 char data
[VS_MAX_VIOLATIONS
][VS_DESCRIPTION_MAX
];
388 adt_session_data_t
*ah
;
390 adt_event_data_t
*event
;
392 if (adt_start_session(&ah
, NULL
, ADT_USE_PROC_DATA
)) {
393 syslog(LOG_AUTH
| LOG_ALERT
, "adt_start_session: %m");
397 if (adt_load_ttyname("/dev/console", &p_tid
) != 0) {
398 syslog(LOG_AUTH
| LOG_ALERT
,
399 "adt_load_ttyname(/dev/console): %m");
403 if (adt_set_user(ah
, ADT_NO_ATTRIB
, ADT_NO_ATTRIB
, ADT_NO_ATTRIB
,
404 ADT_NO_ATTRIB
, p_tid
, ADT_NEW
) != 0) {
405 syslog(LOG_AUTH
| LOG_ALERT
, "adt_set_user(ADT_NO_ATTRIB): %m");
406 (void) adt_end_session(ah
);
410 if ((event
= adt_alloc_event(ah
, ADT_vscan_quarantine
)) == NULL
) {
411 syslog(LOG_AUTH
| LOG_ALERT
,
412 "adt_alloc_event(ADT_vscan_quarantine)): %m");
413 (void) adt_end_session(ah
);
417 /* populate vscan audit event */
418 event
->adt_vscan_quarantine
.file
= filepath
;
419 for (i
= 0; i
< result
->vsr_nviolations
; i
++) {
420 (void) snprintf(data
[i
], VS_DESCRIPTION_MAX
, "%d - %s",
421 result
->vsr_vrec
[i
].vr_id
, result
->vsr_vrec
[i
].vr_desc
);
422 violations
[i
] = data
[i
];
425 event
->adt_vscan_quarantine
.violations
= (char **)violations
;
426 event
->adt_vscan_quarantine
.nviolations
= result
->vsr_nviolations
;
428 if (adt_put_event(event
, ADT_SUCCESS
, ADT_SUCCESS
))
429 syslog(LOG_AUTH
| LOG_ALERT
, "adt_put_event: %m");
431 adt_free_event(event
);
432 (void) adt_end_session(ah
);