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 2007 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 int vs_svc_process_scan_result(vs_attr_t *, vs_result_t *); 47 static void vs_svc_vlog(char *, vs_result_t *); 48 static void vs_svc_audit(char *, vs_result_t *); 49 50 /* 51 * vs_svc_init, vs_svc_fini 52 * 53 * Invoked on daemon load and unload 54 */ 55 void 56 vs_svc_init() 57 { 58 } 59 60 void 61 vs_svc_fini() 62 { 63 } 64 65 66 /* 67 * vs_svc_scan_file 68 * 69 * vs_svc_scan_file is responsible for: 70 * - determining if a scan is required 71 * - obtaining & releasing a scan engine connection 72 * - invoking the scan engine interface code to do the scan 73 * - retrying a failed scan (up to VS_MAX_RETRY times) 74 * - updating scan statistics 75 * - logging virus information 76 * 77 * Returns: 78 * VS_ACCESS_ALLOW, VS_ACCESS_DENY 79 */ 80 int 81 vs_svc_scan_file(char *devname, char *fname, vs_attr_t *fattr, int flags) 82 { 83 vs_eng_conn_t conn; 84 int access = VS_ACCESS_UNDEFINED; 85 int rc, retries; 86 vs_result_t result; 87 88 /* deny access to quarantined files */ 89 if (fattr->vsa_quarantined) 90 return (VS_ACCESS_DENY); 91 92 /* allow access if not modified & scanstamp current */ 93 if ((fattr->vsa_modified == 0) && 94 vs_eng_scanstamp_current(fattr->vsa_scanstamp)) { 95 return (VS_ACCESS_ALLOW); 96 } 97 98 (void) memset(&result, 0, sizeof (vs_result_t)); 99 result.vsr_rc = VS_RESULT_UNDEFINED; 100 101 for (retries = 0; retries <= VS_MAX_RETRY; retries++) { 102 /* identify available engine connection */ 103 if (vs_eng_get(&conn, retries) != 0) { 104 rc = VS_RESULT_ERROR; 105 continue; 106 } 107 108 /* connect to engine and scan file */ 109 if (vs_eng_connect(&conn) != 0) 110 rc = VS_RESULT_SE_ERROR; 111 else { 112 if (vscand_get_state() == VS_STATE_SHUTDOWN) { 113 vs_eng_release(&conn); 114 return (VS_ACCESS_ALLOW); 115 } 116 117 rc = vs_icap_scan_file(&conn, devname, fname, 118 fattr->vsa_size, flags, &result); 119 } 120 121 /* if no error, clear error state on engine and break */ 122 if ((rc != VS_RESULT_SE_ERROR) && (rc != VS_RESULT_ERROR)) { 123 vs_eng_set_error(&conn, 0); 124 vs_eng_release(&conn); 125 break; 126 } 127 128 /* if scan failed due to shutdown, allow access */ 129 if (vscand_get_state() == VS_STATE_SHUTDOWN) { 130 vs_eng_release(&conn); 131 return (VS_ACCESS_ALLOW); 132 } 133 134 /* set engine's error state and update engine stats */ 135 if (rc == VS_RESULT_SE_ERROR) { 136 vs_eng_set_error(&conn, 1); 137 vs_stats_eng_err(conn.vsc_engid); 138 } 139 vs_eng_release(&conn); 140 } 141 142 vs_stats_set(rc); 143 144 /* if file infected, update virus log and write audit record */ 145 if (result.vsr_rc == VS_RESULT_CLEANED || 146 result.vsr_rc == VS_RESULT_FORBIDDEN) { 147 vs_svc_vlog(fname, &result); 148 vs_svc_audit(fname, &result); 149 } 150 151 access = vs_svc_process_scan_result(fattr, &result); 152 153 return (access); 154 } 155 156 157 /* 158 * vs_svc_process_scan_result 159 * 160 * Translate the scan result into VS_ACCESS_ALLOW or VS_ACCESS_DENY. 161 * If the scan failed (VS_RESULT_ERROR) deny access if the 162 * scan was initiated because the file had been modified or 163 * had never been scanned. Otherwise allow access. 164 * 165 * If file has been modified or has never been scanned, it must 166 * be successfully scanned before access is allowed 167 * 168 * If the file has previously been scanned and has not been 169 * modified, don't deny access if scan fail, only if the file 170 * is found to be infected. 171 * 172 * If the file is still infected set quarantine attribute, 173 * otherwise clear modified attribute. 174 * 175 * Returns: VS_ACCESS_ALLOW, VS_ACCESS_DENY 176 */ 177 static int 178 vs_svc_process_scan_result(vs_attr_t *fattr, vs_result_t *result) 179 { 180 int access = VS_ACCESS_DENY; 181 182 switch (result->vsr_rc) { 183 case VS_RESULT_CLEANED: 184 case VS_RESULT_FORBIDDEN: 185 fattr->vsa_scanstamp[0] = '\0'; 186 fattr->vsa_quarantined = 1; 187 access = VS_ACCESS_DENY; 188 break; 189 case VS_RESULT_CLEAN: 190 (void) strlcpy(fattr->vsa_scanstamp, result->vsr_scanstamp, 191 sizeof (vs_scanstamp_t)); 192 fattr->vsa_modified = 0; 193 access = VS_ACCESS_ALLOW; 194 break; 195 case VS_RESULT_ERROR: 196 case VS_RESULT_SE_ERROR: 197 case VS_RESULT_UNDEFINED: 198 default: 199 if ((fattr->vsa_modified) || (fattr->vsa_scanstamp[0] == '\0')) 200 access = VS_ACCESS_DENY; 201 else 202 access = VS_ACCESS_ALLOW; 203 break; 204 } 205 206 return (access); 207 } 208 209 210 /* 211 * vs_svc_vlog 212 * 213 * log details of infections detected in file 214 * If virus log is not configured or cannot be opened, use syslog. 215 */ 216 static void 217 vs_svc_vlog(char *filepath, vs_result_t *result) 218 { 219 FILE *fp = NULL; 220 time_t sec; 221 struct tm *timestamp; 222 char timebuf[18]; /* MM/DD/YY hh:mm:ss */ 223 int i; 224 char *log; 225 226 if ((log = vscand_viruslog()) != NULL) 227 fp = fopen(log, "a"); 228 229 if (fp) { 230 (void) time(&sec); 231 timestamp = localtime(&sec); 232 (void) strftime(timebuf, sizeof (timebuf), "%D %T", timestamp); 233 } 234 235 if (result->vsr_nviolations == 0) { 236 if (fp) { 237 (void) fprintf(fp, "%s quarantine %s", 238 timebuf, filepath); 239 } else { 240 syslog(LOG_WARNING, "quarantine %s\n", filepath); 241 } 242 } else { 243 for (i = 0; i < result->vsr_nviolations; i++) { 244 if (fp) { 245 (void) fprintf(fp, "%s quarantine %s %d - %s\n", 246 timebuf, filepath, 247 result->vsr_vrec[i].vr_id, 248 result->vsr_vrec[i].vr_desc); 249 } else { 250 syslog(LOG_WARNING, "quarantine %s %d - %s\n", 251 filepath, 252 result->vsr_vrec[i].vr_id, 253 result->vsr_vrec[i].vr_desc); 254 } 255 } 256 } 257 258 if (fp) 259 (void) fclose(fp); 260 } 261 262 263 /* 264 * vs_svc_audit 265 * 266 * Generate AUE_vscan_quarantine audit record containing name 267 * of infected file, and violation details if available. 268 */ 269 static void 270 vs_svc_audit(char *filepath, vs_result_t *result) 271 { 272 int i; 273 char *violations[VS_MAX_VIOLATIONS]; 274 char data[VS_MAX_VIOLATIONS][VS_DESCRIPTION_MAX]; 275 adt_session_data_t *ah; 276 adt_termid_t *p_tid; 277 adt_event_data_t *event; 278 279 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA)) { 280 syslog(LOG_AUTH | LOG_ALERT, "adt_start_session: %m"); 281 return; 282 } 283 284 if (adt_load_ttyname("/dev/console", &p_tid) != 0) { 285 syslog(LOG_AUTH | LOG_ALERT, 286 "adt_load_ttyname(/dev/console): %m"); 287 return; 288 } 289 290 if (adt_set_user(ah, ADT_NO_ATTRIB, ADT_NO_ATTRIB, ADT_NO_ATTRIB, 291 ADT_NO_ATTRIB, p_tid, ADT_NEW) != 0) { 292 syslog(LOG_AUTH | LOG_ALERT, "adt_set_user(ADT_NO_ATTRIB): %m"); 293 (void) adt_end_session(ah); 294 return; 295 } 296 297 if ((event = adt_alloc_event(ah, ADT_vscan_quarantine)) == NULL) { 298 syslog(LOG_AUTH | LOG_ALERT, 299 "adt_alloc_event(ADT_vscan_quarantine)): %m"); 300 (void) adt_end_session(ah); 301 return; 302 } 303 304 /* populate vscan audit event */ 305 event->adt_vscan_quarantine.file = filepath; 306 for (i = 0; i < result->vsr_nviolations; i++) { 307 (void) snprintf(data[i], VS_DESCRIPTION_MAX, "%d - %s", 308 result->vsr_vrec[i].vr_id, result->vsr_vrec[i].vr_desc); 309 violations[i] = data[i]; 310 } 311 312 event->adt_vscan_quarantine.violations = (char **)violations; 313 event->adt_vscan_quarantine.nviolations = result->vsr_nviolations; 314 315 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS)) 316 syslog(LOG_AUTH | LOG_ALERT, "adt_put_event: %m"); 317 318 adt_free_event(event); 319 (void) adt_end_session(ah); 320 } 321