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 2008 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 * Implementation of the "scan file" interface 30 */ 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <errno.h> 37 #include <syslog.h> 38 #include <sys/types.h> 39 #include <fcntl.h> 40 #include <bsm/adt.h> 41 #include <bsm/adt_event.h> 42 43 #include "vs_incl.h" 44 45 /* local functions */ 46 static void vs_svc_vlog(char *, vs_result_t *); 47 static void vs_svc_audit(char *, vs_result_t *); 48 49 /* 50 * vs_svc_init, vs_svc_fini 51 * 52 * Invoked on daemon load and unload 53 */ 54 void 55 vs_svc_init() 56 { 57 } 58 59 void 60 vs_svc_fini() 61 { 62 } 63 64 65 /* 66 * vs_svc_scan_file 67 * 68 * vs_svc_scan_file is responsible for: 69 * - determining if a scan is required 70 * - obtaining & releasing a scan engine connection 71 * - invoking the scan engine interface code to do the scan 72 * - retrying a failed scan (up to VS_MAX_RETRY times) 73 * - updating scan statistics 74 * - logging virus information 75 * 76 * 77 * Returns: 78 * VS_STATUS_NO_SCAN - scan not reqd, or daemon shutting down 79 * VS_STATUS_CLEAN - scan success. File clean. 80 * new scanstamp returned in scanstamp param. 81 * VS_STATUS_INFECTED - scan success. File infected. 82 * VS_STATUS_ERROR - scan failure either in vscand or scan engine. 83 */ 84 int 85 vs_svc_scan_file(char *devname, char *fname, vs_attr_t *fattr, int flags, 86 vs_scanstamp_t *scanstamp) 87 { 88 vs_eng_conn_t conn; 89 int retries; 90 vs_result_t result; 91 92 /* initialize response scanstamp to current scanstamp value */ 93 (void) strlcpy(*scanstamp, fattr->vsa_scanstamp, 94 sizeof (vs_scanstamp_t)); 95 96 97 /* No scan if file quarantined */ 98 if (fattr->vsa_quarantined) 99 return (VS_STATUS_NO_SCAN); 100 101 /* No scan if file not modified AND scanstamp is current */ 102 if ((fattr->vsa_modified == 0) && 103 vs_eng_scanstamp_current(fattr->vsa_scanstamp)) { 104 return (VS_STATUS_NO_SCAN); 105 } 106 107 (void) memset(&result, 0, sizeof (vs_result_t)); 108 result.vsr_rc = VS_RESULT_UNDEFINED; 109 110 for (retries = 0; retries <= VS_MAX_RETRY; retries++) { 111 /* identify available engine connection */ 112 if (vs_eng_get(&conn, retries) != 0) { 113 result.vsr_rc = VS_RESULT_ERROR; 114 continue; 115 } 116 117 /* connect to engine and scan file */ 118 if (vs_eng_connect(&conn) != 0) { 119 result.vsr_rc = VS_RESULT_SE_ERROR; 120 } else { 121 if (vscand_get_state() == VS_STATE_SHUTDOWN) { 122 vs_eng_release(&conn); 123 return (VS_STATUS_NO_SCAN); 124 } 125 126 (void) vs_icap_scan_file(&conn, devname, fname, 127 fattr->vsa_size, flags, &result); 128 } 129 130 /* if no error, clear error state on engine and break */ 131 if ((result.vsr_rc != VS_RESULT_SE_ERROR) && 132 (result.vsr_rc != VS_RESULT_ERROR)) { 133 vs_eng_set_error(&conn, 0); 134 vs_eng_release(&conn); 135 break; 136 } 137 138 /* treat error on shutdown as scan not required */ 139 if (vscand_get_state() == VS_STATE_SHUTDOWN) { 140 vs_eng_release(&conn); 141 return (VS_STATUS_NO_SCAN); 142 } 143 144 /* set engine's error state and update engine stats */ 145 if (result.vsr_rc == VS_RESULT_SE_ERROR) { 146 vs_eng_set_error(&conn, 1); 147 vs_stats_eng_err(conn.vsc_engid); 148 } 149 vs_eng_release(&conn); 150 } 151 152 vs_stats_set(result.vsr_rc); 153 154 /* 155 * VS_RESULT_CLEANED - file infected, cleaned data available 156 * VS_RESULT_FORBIDDEN - file infected, no cleaned data 157 * Log virus, write audit record and return INFECTED status 158 */ 159 if (result.vsr_rc == VS_RESULT_CLEANED || 160 result.vsr_rc == VS_RESULT_FORBIDDEN) { 161 vs_svc_vlog(fname, &result); 162 vs_svc_audit(fname, &result); 163 return (VS_STATUS_INFECTED); 164 } 165 166 /* VS_RESULT_CLEAN - Set the scanstamp and return CLEAN status */ 167 if (result.vsr_rc == VS_RESULT_CLEAN) { 168 (void) strlcpy(*scanstamp, result.vsr_scanstamp, 169 sizeof (vs_scanstamp_t)); 170 return (VS_STATUS_CLEAN); 171 } 172 173 return (VS_STATUS_ERROR); 174 } 175 176 177 /* 178 * vs_svc_vlog 179 * 180 * log details of infections detected in file 181 * If virus log is not configured or cannot be opened, use syslog. 182 */ 183 static void 184 vs_svc_vlog(char *filepath, vs_result_t *result) 185 { 186 FILE *fp = NULL; 187 time_t sec; 188 struct tm *timestamp; 189 char timebuf[18]; /* MM/DD/YY hh:mm:ss */ 190 int i; 191 char *log; 192 193 if ((log = vscand_viruslog()) != NULL) 194 fp = fopen(log, "a"); 195 196 if (fp) { 197 (void) time(&sec); 198 timestamp = localtime(&sec); 199 (void) strftime(timebuf, sizeof (timebuf), "%D %T", timestamp); 200 } 201 202 if (result->vsr_nviolations == 0) { 203 if (fp) { 204 (void) fprintf(fp, "%s quarantine %s", 205 timebuf, filepath); 206 } else { 207 syslog(LOG_WARNING, "quarantine %s\n", filepath); 208 } 209 } else { 210 for (i = 0; i < result->vsr_nviolations; i++) { 211 if (fp) { 212 (void) fprintf(fp, "%s quarantine %s %d - %s\n", 213 timebuf, filepath, 214 result->vsr_vrec[i].vr_id, 215 result->vsr_vrec[i].vr_desc); 216 } else { 217 syslog(LOG_WARNING, "quarantine %s %d - %s\n", 218 filepath, 219 result->vsr_vrec[i].vr_id, 220 result->vsr_vrec[i].vr_desc); 221 } 222 } 223 } 224 225 if (fp) 226 (void) fclose(fp); 227 } 228 229 230 /* 231 * vs_svc_audit 232 * 233 * Generate AUE_vscan_quarantine audit record containing name 234 * of infected file, and violation details if available. 235 */ 236 static void 237 vs_svc_audit(char *filepath, vs_result_t *result) 238 { 239 int i; 240 char *violations[VS_MAX_VIOLATIONS]; 241 char data[VS_MAX_VIOLATIONS][VS_DESCRIPTION_MAX]; 242 adt_session_data_t *ah; 243 adt_termid_t *p_tid; 244 adt_event_data_t *event; 245 246 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) { 247 syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m"); 248 return; 249 } 250 251 if (adt_load_ttyname("/dev/console", &p_tid) != 0) { 252 syslog(LOG_AUTH | LOG_ALERT, 253 "adt_load_ttyname(/dev/console): %m"); 254 return; 255 } 256 257 if (adt_set_user(ah, ADT_NO_ATTRIB, ADT_NO_ATTRIB, ADT_NO_ATTRIB, 258 ADT_NO_ATTRIB, p_tid, ADT_NEW) != 0) { 259 syslog(LOG_AUTH | LOG_ALERT, "adt_set_user(ADT_NO_ATTRIB): %m"); 260 (void) adt_end_session(ah); 261 return; 262 } 263 264 if ((event = adt_alloc_event(ah, ADT_vscan_quarantine)) == NULL) { 265 syslog(LOG_AUTH | LOG_ALERT, 266 "adt_alloc_event(ADT_vscan_quarantine)): %m"); 267 (void) adt_end_session(ah); 268 return; 269 } 270 271 /* populate vscan audit event */ 272 event->adt_vscan_quarantine.file = filepath; 273 for (i = 0; i < result->vsr_nviolations; i++) { 274 (void) snprintf(data[i], VS_DESCRIPTION_MAX, "%d - %s", 275 result->vsr_vrec[i].vr_id, result->vsr_vrec[i].vr_desc); 276 violations[i] = data[i]; 277 } 278 279 event->adt_vscan_quarantine.violations = (char **)violations; 280 event->adt_vscan_quarantine.nviolations = result->vsr_nviolations; 281 282 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS)) 283 syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m"); 284 285 adt_free_event(event); 286 (void) adt_end_session(ah); 287 } 288