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 * Implementation of the "scan file" interface
36 #include <sys/types.h>
39 #include <bsm/adt_event.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
71 typedef struct vs_svc_node
{
73 vs_scan_req_t vsn_req
;
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
;
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
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);
114 * Close all scan engine connections to terminate in-progress scan
115 * requests, and wait for all threads in vs_svc_nodes to complete
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
);
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
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
)
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
);
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
);
185 (void) pthread_mutex_unlock(&vs_svc_mutex
);
187 return (VS_STATUS_SCANNING
);
194 * Initialize response structure, invoke vs_svc_scan_file to
195 * perform the scan, then send the result to the kernel.
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
);
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
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.
238 vs_svc_scan_file(vs_svc_node_t
*node
, vs_scanstamp_t
*scanstamp
)
240 char devname
[MAXPATHLEN
];
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
;
262 /* shutdown could occur while waiting for engine connection */
263 if (vscand_get_state() == VS_STATE_SHUTDOWN
) {
265 return (VS_STATUS_NO_SCAN
);
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);
280 /* treat error on shutdown as scan not required */
281 if (vscand_get_state() == VS_STATE_SHUTDOWN
) {
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);
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
);
321 * log details of infections detected in syslig
322 * If virus log is configured log details there too
325 vs_svc_vlog(char *filepath
, vs_result_t
*result
)
329 struct tm
*timestamp
;
330 char timebuf
[18]; /* MM/DD/YY hh:mm:ss */
335 if (result
->vsr_nviolations
== 0) {
336 syslog(LOG_NOTICE
, "quarantine %s\n", filepath
);
338 for (i
= 0; i
< result
->vsr_nviolations
; i
++) {
339 syslog(LOG_NOTICE
, "quarantine %s %d - %s\n",
341 result
->vsr_vrec
[i
].vr_id
,
342 result
->vsr_vrec
[i
].vr_desc
);
347 if (((log
= vscand_viruslog()) == NULL
) ||
348 ((fp
= fopen(log
, "a")) == NULL
)) {
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
);
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
);
376 * Generate AUE_vscan_quarantine audit record containing name
377 * of infected file, and violation details if available.
380 vs_svc_audit(char *filepath
, vs_result_t
*result
)
383 char *violations
[VS_MAX_VIOLATIONS
];
384 char data
[VS_MAX_VIOLATIONS
][VS_DESCRIPTION_MAX
];
385 adt_session_data_t
*ah
;
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");
394 if (adt_load_ttyname("/dev/console", &p_tid
) != 0) {
395 syslog(LOG_AUTH
| LOG_ALERT
,
396 "adt_load_ttyname(/dev/console): %m");
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
);
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
);
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
);