1911106dfSjm199354 /* 2911106dfSjm199354 * CDDL HEADER START 3911106dfSjm199354 * 4911106dfSjm199354 * The contents of this file are subject to the terms of the 5911106dfSjm199354 * Common Development and Distribution License (the "License"). 6911106dfSjm199354 * You may not use this file except in compliance with the License. 7911106dfSjm199354 * 8911106dfSjm199354 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9911106dfSjm199354 * or http://www.opensolaris.org/os/licensing. 10911106dfSjm199354 * See the License for the specific language governing permissions 11911106dfSjm199354 * and limitations under the License. 12911106dfSjm199354 * 13911106dfSjm199354 * When distributing Covered Code, include this CDDL HEADER in each 14911106dfSjm199354 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15911106dfSjm199354 * If applicable, add the following below this CDDL HEADER, with the 16911106dfSjm199354 * fields enclosed by brackets "[]" replaced with your own identifying 17911106dfSjm199354 * information: Portions Copyright [yyyy] [name of copyright owner] 18911106dfSjm199354 * 19911106dfSjm199354 * CDDL HEADER END 20911106dfSjm199354 */ 21911106dfSjm199354 /* 22*db1a607eSjoyce mcintosh * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23911106dfSjm199354 * Use is subject to license terms. 24911106dfSjm199354 */ 25911106dfSjm199354 26911106dfSjm199354 /* 27911106dfSjm199354 * Description: Module contains supporting functions used by functions 28911106dfSjm199354 * defined in vs_svc.c. It also contains some internal(static) functions. 29911106dfSjm199354 */ 30911106dfSjm199354 31911106dfSjm199354 #include <stdarg.h> 32911106dfSjm199354 #include <stdio.h> 33911106dfSjm199354 #include <stdlib.h> 34911106dfSjm199354 #include <unistd.h> 35911106dfSjm199354 #include <errno.h> 36911106dfSjm199354 #include <time.h> 37911106dfSjm199354 #include <fcntl.h> 38911106dfSjm199354 #include <syslog.h> 39911106dfSjm199354 #include <ctype.h> 40911106dfSjm199354 #include <strings.h> 41911106dfSjm199354 #include <string.h> 42911106dfSjm199354 #include <limits.h> 43911106dfSjm199354 #include <pthread.h> 44911106dfSjm199354 #include <sys/types.h> 45911106dfSjm199354 #include <sys/socket.h> 46911106dfSjm199354 #include <sys/debug.h> 47911106dfSjm199354 #include <netinet/in.h> 48911106dfSjm199354 #include <arpa/inet.h> 49911106dfSjm199354 50911106dfSjm199354 #include "vs_incl.h" 51911106dfSjm199354 #include "vs_icap.h" 52911106dfSjm199354 53911106dfSjm199354 /* prototypes of local functions */ 54911106dfSjm199354 static int vs_icap_option_request(vs_scan_ctx_t *); 55911106dfSjm199354 static int vs_icap_send_option_req(vs_scan_ctx_t *); 56911106dfSjm199354 static int vs_icap_read_option_resp(vs_scan_ctx_t *); 57911106dfSjm199354 58911106dfSjm199354 static int vs_icap_respmod_request(vs_scan_ctx_t *); 59911106dfSjm199354 static int vs_icap_may_preview(vs_scan_ctx_t *); 60911106dfSjm199354 static char *vs_icap_find_ext(char *); 61911106dfSjm199354 static int vs_icap_send_preview(vs_scan_ctx_t *); 62911106dfSjm199354 static int vs_icap_send_respmod_hdr(vs_scan_ctx_t *, int); 63911106dfSjm199354 static int vs_icap_create_respmod_hdr(vs_scan_ctx_t *, int); 64911106dfSjm199354 static int vs_icap_uri_encode(char *, int, char *); 65911106dfSjm199354 static int vs_icap_uri_illegal_char(char); 66911106dfSjm199354 67911106dfSjm199354 static int vs_icap_read_respmod_resp(vs_scan_ctx_t *); 68911106dfSjm199354 static int vs_icap_read_resp_code(vs_scan_ctx_t *); 69911106dfSjm199354 static int vs_icap_read_hdr(vs_scan_ctx_t *, vs_hdr_t *, int); 70911106dfSjm199354 71911106dfSjm199354 static int vs_icap_set_scan_result(vs_scan_ctx_t *); 72911106dfSjm199354 static int vs_icap_read_encap_hdr(vs_scan_ctx_t *); 73911106dfSjm199354 static void vs_icap_read_encap_data(vs_scan_ctx_t *); 74911106dfSjm199354 static int vs_icap_create_repair_file(vs_scan_ctx_t *); 75911106dfSjm199354 static int vs_icap_read_resp_body(vs_scan_ctx_t *); 76911106dfSjm199354 static int vs_icap_read_body_chunk(vs_scan_ctx_t *); 77911106dfSjm199354 78911106dfSjm199354 static int vs_icap_send_chunk(vs_scan_ctx_t *, int); 79911106dfSjm199354 static int vs_icap_send_termination(vs_scan_ctx_t *); 80911106dfSjm199354 static int vs_icap_readline(vs_scan_ctx_t *, char *, int); 81911106dfSjm199354 82911106dfSjm199354 static int vs_icap_write(int, char *, int); 83911106dfSjm199354 static int vs_icap_read(int, char *, int); 84911106dfSjm199354 85911106dfSjm199354 /* process options and respmod headers */ 86911106dfSjm199354 static void vs_icap_parse_hdrs(char, char *, char **, char **); 87911106dfSjm199354 static int vs_icap_opt_value(vs_scan_ctx_t *, int, char *); 88911106dfSjm199354 static int vs_icap_opt_ext(vs_scan_ctx_t *, int, char *); 89911106dfSjm199354 static int vs_icap_resp_violations(vs_scan_ctx_t *, int, char *); 90911106dfSjm199354 static int vs_icap_resp_violation_rec(vs_scan_ctx_t *, int); 91911106dfSjm199354 static int vs_icap_resp_infection(vs_scan_ctx_t *, int, char *); 92911106dfSjm199354 static int vs_icap_resp_virus_id(vs_scan_ctx_t *, int, char *); 93911106dfSjm199354 static int vs_icap_resp_encap(vs_scan_ctx_t *, int, char *); 94911106dfSjm199354 static int vs_icap_resp_istag(vs_scan_ctx_t *, int, char *); 95911106dfSjm199354 static void vs_icap_istag_to_scanstamp(char *, vs_scanstamp_t); 96911106dfSjm199354 97911106dfSjm199354 /* Utility functions for handling OPTIONS data: vs_options_t */ 98911106dfSjm199354 static void vs_icap_free_options(vs_options_t *); 99911106dfSjm199354 static void vs_icap_copy_options(vs_options_t *, vs_options_t *); 100911106dfSjm199354 static void vs_icap_update_options(vs_scan_ctx_t *); 101911106dfSjm199354 static int vs_icap_compare_se(int, char *, int); 102911106dfSjm199354 103911106dfSjm199354 static iovec_t *vs_icap_make_strvec(char *, const char *); 104911106dfSjm199354 static iovec_t *vs_icap_copy_strvec(iovec_t *); 105911106dfSjm199354 static int vs_icap_check_ext(char *, iovec_t *); 106911106dfSjm199354 static void vs_icap_trimspace(char *); 107911106dfSjm199354 108911106dfSjm199354 /* icap response message */ 109911106dfSjm199354 static char *vs_icap_resp_str(int); 110911106dfSjm199354 111911106dfSjm199354 /* 112911106dfSjm199354 * local variables 113911106dfSjm199354 */ 114911106dfSjm199354 115911106dfSjm199354 /* option headers - and handler functions */ 116911106dfSjm199354 vs_hdr_t option_hdrs[] = { 117911106dfSjm199354 { VS_OPT_SERVICE, "Service", vs_icap_opt_value}, 118911106dfSjm199354 { VS_OPT_ISTAG, "ISTag", vs_icap_opt_value}, 119911106dfSjm199354 { VS_OPT_METHODS, "Methods", vs_icap_opt_value}, 120911106dfSjm199354 { VS_OPT_ALLOW, "Allow", vs_icap_opt_value}, 121911106dfSjm199354 { VS_OPT_PREVIEW, "Preview", vs_icap_opt_value}, 122911106dfSjm199354 { VS_OPT_XFER_PREVIEW, "Transfer-Preview", vs_icap_opt_ext}, 123911106dfSjm199354 { VS_OPT_XFER_COMPLETE, "Transfer-Complete", vs_icap_opt_ext}, 124911106dfSjm199354 { VS_OPT_MAX_CONNECTIONS, "Max-Connections", vs_icap_opt_value}, 125911106dfSjm199354 { VS_OPT_TTL, "Options-TTL", vs_icap_opt_value}, 126911106dfSjm199354 { VS_OPT_X_DEF_INFO, "X-Definition-Info", vs_icap_opt_value} 127911106dfSjm199354 }; 128911106dfSjm199354 129911106dfSjm199354 130911106dfSjm199354 /* resp hdrs - and handler functions */ 131911106dfSjm199354 vs_hdr_t resp_hdrs[] = { 132911106dfSjm199354 { VS_RESP_ENCAPSULATED, "Encapsulated", vs_icap_resp_encap}, 133911106dfSjm199354 { VS_RESP_ISTAG, "ISTag", vs_icap_resp_istag}, 134911106dfSjm199354 { VS_RESP_X_VIRUS_ID, "X-Virus-ID", vs_icap_resp_virus_id}, 135911106dfSjm199354 { VS_RESP_X_INFECTION, "X-Infection-Found", vs_icap_resp_infection}, 136911106dfSjm199354 { VS_RESP_X_VIOLATIONS, "X-Violations-Found", vs_icap_resp_violations} 137911106dfSjm199354 }; 138911106dfSjm199354 139911106dfSjm199354 /* ICAP response code to string mappings */ 140911106dfSjm199354 vs_resp_msg_t icap_resp[] = { 141911106dfSjm199354 { VS_RESP_CONTINUE, "Continue"}, 142911106dfSjm199354 { VS_RESP_OK, "OK"}, 143911106dfSjm199354 { VS_RESP_CREATED, "Virus Detected and Repaired"}, 144911106dfSjm199354 { VS_RESP_NO_CONT_NEEDED, "No Content Necessary"}, 145911106dfSjm199354 { VS_RESP_BAD_REQ, "Bad Request"}, 146911106dfSjm199354 { VS_RESP_FORBIDDEN, "File Infected and not repaired"}, 147911106dfSjm199354 { VS_RESP_NOT_FOUND, "URI not found"}, 148911106dfSjm199354 { VS_RESP_NOT_ALLOWED, "Method not allowed"}, 149911106dfSjm199354 { VS_RESP_TIMEOUT, "Request timedout"}, 150911106dfSjm199354 { VS_RESP_INTERNAL_ERR, "Internal server error"}, 151911106dfSjm199354 { VS_RESP_NOT_IMPL, "Method not implemented"}, 152911106dfSjm199354 { VS_RESP_SERV_UNAVAIL, "Service unavailable/overloaded"}, 153911106dfSjm199354 { VS_RESP_ICAP_VER_UNSUPP, "ICAP version not supported"}, 154911106dfSjm199354 { VS_RESP_SCAN_ERR, "Error scanning file"}, 155911106dfSjm199354 { VS_RESP_NO_LICENSE, "No AV License"}, 156911106dfSjm199354 { VS_RESP_RES_UNAVAIL, "Resource unavailable"}, 157911106dfSjm199354 { VS_RESP_UNKNOWN, "Unknown Error"}, 158911106dfSjm199354 }; 159911106dfSjm199354 160911106dfSjm199354 static const char *EXT_SEPARATOR = ","; 161911106dfSjm199354 static vs_options_t vs_options[VS_SE_MAX]; 162911106dfSjm199354 static pthread_mutex_t vs_opt_mutex = PTHREAD_MUTEX_INITIALIZER; 163911106dfSjm199354 164911106dfSjm199354 /* 165911106dfSjm199354 * vs_icap_init 166911106dfSjm199354 * initialization performed when daemon is loaded 167911106dfSjm199354 */ 168911106dfSjm199354 void 169911106dfSjm199354 vs_icap_init() 170911106dfSjm199354 { 171911106dfSjm199354 172911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex); 173911106dfSjm199354 (void) memset(vs_options, 0, sizeof (vs_options_t)); 174911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex); 175911106dfSjm199354 } 176911106dfSjm199354 177911106dfSjm199354 178911106dfSjm199354 /* 179911106dfSjm199354 * vs_icap_fini 180911106dfSjm199354 * cleanup performed when daemon is unloaded 181911106dfSjm199354 */ 182911106dfSjm199354 void 183911106dfSjm199354 vs_icap_fini() 184911106dfSjm199354 { 185911106dfSjm199354 int i; 186911106dfSjm199354 187911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex); 188911106dfSjm199354 189911106dfSjm199354 for (i = 0; i < VS_SE_MAX; i++) 190911106dfSjm199354 vs_icap_free_options(&vs_options[i]); 191911106dfSjm199354 192911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex); 193911106dfSjm199354 } 194911106dfSjm199354 195911106dfSjm199354 196911106dfSjm199354 /* 197911106dfSjm199354 * vs_icap_config 198911106dfSjm199354 * 19953c11029Sjm199354 * When a new VSCAN configuration is specified, this will be 200911106dfSjm199354 * called per scan engine. If the scan engine host or port has 201911106dfSjm199354 * changed delete the vs_options entry for that scan engine. 202911106dfSjm199354 */ 203911106dfSjm199354 void 204911106dfSjm199354 vs_icap_config(int idx, char *host, int port) 205911106dfSjm199354 { 206911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex); 207911106dfSjm199354 if (vs_icap_compare_se(idx, host, port) != 0) { 208911106dfSjm199354 vs_icap_free_options(&vs_options[idx]); 209911106dfSjm199354 (void) strlcpy(vs_options[idx].vso_host, host, 210911106dfSjm199354 sizeof (vs_options[idx].vso_host)); 211911106dfSjm199354 vs_options[idx].vso_port = port; 212911106dfSjm199354 } 213911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex); 214911106dfSjm199354 } 215911106dfSjm199354 216911106dfSjm199354 217911106dfSjm199354 /* 218911106dfSjm199354 * vs_icap_scan_file 219911106dfSjm199354 * 220911106dfSjm199354 * Create a context (vs_scan_ctx_t) for the scan operation and initialize 221911106dfSjm199354 * its options info. If the scan engine connection's IP or port is different 222911106dfSjm199354 * from that held in vs_options the vs_options info is old and should 223911106dfSjm199354 * be deleted (vs_icap_free_options). Otherwise, copy the vs_options info 224911106dfSjm199354 * into the context. 225911106dfSjm199354 * file name, size and decsriptor are also copied into the context 226911106dfSjm199354 * 227911106dfSjm199354 * Handle the ICAP protocol communication with the external Scan Engine to 228911106dfSjm199354 * perform the scan 229911106dfSjm199354 * - send an OPTIONS request if necessary 230911106dfSjm199354 * - send RESPMOD scan request 231911106dfSjm199354 * - process the response and save any cleaned data to file 232911106dfSjm199354 * 233911106dfSjm199354 * Returns: result->vsr_rc 234911106dfSjm199354 */ 235911106dfSjm199354 int 236bfc848c6Sjm199354 vs_icap_scan_file(vs_eng_ctx_t *eng, char *devname, char *fname, 237911106dfSjm199354 uint64_t fsize, int flags, vs_result_t *result) 238911106dfSjm199354 { 239911106dfSjm199354 vs_scan_ctx_t ctx; 240911106dfSjm199354 int fd; 241911106dfSjm199354 242bfc848c6Sjm199354 fd = open(devname, O_RDONLY); 243bfc848c6Sjm199354 244bfc848c6Sjm199354 /* retry once on ENOENT as /dev link may not be created yet */ 245bfc848c6Sjm199354 if ((fd == -1) && (errno == ENOENT)) { 246bfc848c6Sjm199354 (void) sleep(1); 247bfc848c6Sjm199354 fd = open(devname, O_RDONLY); 248bfc848c6Sjm199354 } 249bfc848c6Sjm199354 250bfc848c6Sjm199354 if (fd == -1) { 251bfc848c6Sjm199354 syslog(LOG_ERR, "Failed to open device %s - %s", 252bfc848c6Sjm199354 devname, strerror(errno)); 253911106dfSjm199354 result->vsr_rc = VS_RESULT_ERROR; 254911106dfSjm199354 return (result->vsr_rc); 255911106dfSjm199354 } 256911106dfSjm199354 257911106dfSjm199354 /* initialize context */ 258911106dfSjm199354 (void) memset(&ctx, 0, sizeof (vs_scan_ctx_t)); 259bfc848c6Sjm199354 ctx.vsc_idx = eng->vse_eidx; 260bfc848c6Sjm199354 (void) strlcpy(ctx.vsc_host, eng->vse_host, sizeof (ctx.vsc_host)); 261bfc848c6Sjm199354 ctx.vsc_port = eng->vse_port; 262bfc848c6Sjm199354 ctx.vsc_sockfd = eng->vse_sockfd; 263911106dfSjm199354 ctx.vsc_fd = fd; 264911106dfSjm199354 ctx.vsc_fname = fname; 265911106dfSjm199354 ctx.vsc_fsize = fsize; 266911106dfSjm199354 ctx.vsc_flags = flags; 267911106dfSjm199354 ctx.vsc_result = result; 268911106dfSjm199354 269911106dfSjm199354 /* Hooks for future saving of repaired data, not yet in use */ 270911106dfSjm199354 ctx.vsc_flags |= VS_NO_REPAIR; 271911106dfSjm199354 ctx.vsc_repair = 0; 272911106dfSjm199354 ctx.vsc_repair_fname = NULL; 273911106dfSjm199354 ctx.vsc_repair_fd = -1; 274911106dfSjm199354 275911106dfSjm199354 /* take a copy of vs_options[idx] if they match the SE specified */ 276911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex); 277911106dfSjm199354 if (vs_icap_compare_se(ctx.vsc_idx, ctx.vsc_host, ctx.vsc_port) == 0) { 278911106dfSjm199354 vs_icap_copy_options(&ctx.vsc_options, 279911106dfSjm199354 &vs_options[ctx.vsc_idx]); 280911106dfSjm199354 } 281911106dfSjm199354 282911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex); 283911106dfSjm199354 284911106dfSjm199354 /* 285911106dfSjm199354 * default the result to scan engine error. 286911106dfSjm199354 * Any non scan-engine errors will reset it to VS_RESULT_ERROR 287911106dfSjm199354 */ 288911106dfSjm199354 result->vsr_rc = VS_RESULT_SE_ERROR; 289911106dfSjm199354 290911106dfSjm199354 /* do the scan */ 291911106dfSjm199354 if (vs_icap_option_request(&ctx) == 0) 292911106dfSjm199354 (void) vs_icap_respmod_request(&ctx); 293911106dfSjm199354 294911106dfSjm199354 (void) close(fd); 295911106dfSjm199354 vs_icap_free_options(&ctx.vsc_options); 296911106dfSjm199354 return (result->vsr_rc); 297911106dfSjm199354 } 298911106dfSjm199354 299911106dfSjm199354 300911106dfSjm199354 /* ********************************************************************* */ 301911106dfSjm199354 /* Local Function definitions */ 302911106dfSjm199354 /* ********************************************************************* */ 303911106dfSjm199354 304911106dfSjm199354 /* 305911106dfSjm199354 * vs_icap_option_request 306911106dfSjm199354 * 307911106dfSjm199354 * Send ICAP options message and await/process the response. 308911106dfSjm199354 * 309911106dfSjm199354 * The ICAP options request needs to be sent when a connection 310911106dfSjm199354 * is first made with the scan engine. Unless the scan engine 311911106dfSjm199354 * determines that the options will never expire (which we save 312911106dfSjm199354 * as optione_req_time == -1) the request should be resent after 313911106dfSjm199354 * the expiry time specified by the icap server. 314911106dfSjm199354 * 315911106dfSjm199354 * Returns: 0 - success 316911106dfSjm199354 * -1 - error 317911106dfSjm199354 */ 318911106dfSjm199354 static int 319911106dfSjm199354 vs_icap_option_request(vs_scan_ctx_t *ctx) 320911106dfSjm199354 { 321911106dfSjm199354 if (ctx->vsc_options.vso_req_time != -1 && 322911106dfSjm199354 ((time(0) - ctx->vsc_options.vso_req_time) > 323911106dfSjm199354 ctx->vsc_options.vso_ttl)) { 324911106dfSjm199354 325911106dfSjm199354 if (vs_icap_send_option_req(ctx) < 0) 326911106dfSjm199354 return (-1); 327911106dfSjm199354 328911106dfSjm199354 if (vs_icap_read_option_resp(ctx) < 0) 329911106dfSjm199354 return (-1); 330911106dfSjm199354 331911106dfSjm199354 vs_icap_update_options(ctx); 332911106dfSjm199354 } 333911106dfSjm199354 334911106dfSjm199354 return (0); 335911106dfSjm199354 } 336911106dfSjm199354 337911106dfSjm199354 338911106dfSjm199354 /* 339911106dfSjm199354 * vs_icap_send_option_req 340911106dfSjm199354 * 341911106dfSjm199354 * Send an OPTIONS request to the scan engine 342911106dfSjm199354 * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME) 343911106dfSjm199354 * after the IP address, otherwise it closes the connection. 344911106dfSjm199354 * 345911106dfSjm199354 * Returns: 0 - success 346911106dfSjm199354 * -1 - error 347911106dfSjm199354 */ 348911106dfSjm199354 static int 349911106dfSjm199354 vs_icap_send_option_req(vs_scan_ctx_t *ctx) 350911106dfSjm199354 { 351911106dfSjm199354 char my_host_name[MAXHOSTNAMELEN]; 352911106dfSjm199354 int bufsp = VS_BUF_SZ; 353911106dfSjm199354 char *buf0 = ctx->vsc_info.vsi_send_buf; 354911106dfSjm199354 char *bufp = buf0; 355911106dfSjm199354 int tlen; 356911106dfSjm199354 357911106dfSjm199354 if (gethostname(my_host_name, sizeof (my_host_name)) != 0) { 358911106dfSjm199354 /* non SE error */ 359911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR; 360911106dfSjm199354 return (-1); 361911106dfSjm199354 } 362911106dfSjm199354 363911106dfSjm199354 (void) memset(ctx->vsc_info.vsi_send_buf, 0, 364911106dfSjm199354 sizeof (ctx->vsc_info.vsi_send_buf)); 365911106dfSjm199354 366911106dfSjm199354 tlen = snprintf(bufp, bufsp, "OPTIONS icap://%s:%d/%s %s\r\n", 367911106dfSjm199354 ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER); 368911106dfSjm199354 bufp += tlen; 369911106dfSjm199354 bufsp -= tlen; 370911106dfSjm199354 371911106dfSjm199354 tlen = snprintf(bufp, bufsp, "Host: %s\r\n\r\n", my_host_name); 372911106dfSjm199354 bufp += tlen; 373911106dfSjm199354 374911106dfSjm199354 if (vs_icap_write(ctx->vsc_sockfd, buf0, (bufp - buf0)) < 0) 375911106dfSjm199354 return (-1); 376911106dfSjm199354 377911106dfSjm199354 return (0); 378911106dfSjm199354 } 379911106dfSjm199354 380911106dfSjm199354 381911106dfSjm199354 /* 382911106dfSjm199354 * vs_icap_read_option_resp 383911106dfSjm199354 * 384911106dfSjm199354 * Returns: 0 - success 385911106dfSjm199354 * -1 - error 386911106dfSjm199354 */ 387911106dfSjm199354 static int 388911106dfSjm199354 vs_icap_read_option_resp(vs_scan_ctx_t *ctx) 389911106dfSjm199354 { 390911106dfSjm199354 if (vs_icap_read_resp_code(ctx) < 0) 391911106dfSjm199354 return (-1); 392911106dfSjm199354 393911106dfSjm199354 if (ctx->vsc_info.vsi_icap_rc != VS_RESP_OK) { 394911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error " 395911106dfSjm199354 "- unexpected option response: %s", 396911106dfSjm199354 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc)); 397911106dfSjm199354 return (-1); 398911106dfSjm199354 } 399911106dfSjm199354 400911106dfSjm199354 if (vs_icap_read_hdr(ctx, option_hdrs, VS_OPT_HDR_MAX) != 0) 401911106dfSjm199354 return (-1); 402911106dfSjm199354 403911106dfSjm199354 if ((ctx->vsc_options.vso_scanstamp[0] == 0) || 404911106dfSjm199354 (ctx->vsc_options.vso_respmod == 0) || 405911106dfSjm199354 (ctx->vsc_options.vso_req_time == 0)) { 406911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error " 407911106dfSjm199354 "- missing or invalid option response hdrs"); 408911106dfSjm199354 return (-1); 409911106dfSjm199354 } 410911106dfSjm199354 411911106dfSjm199354 return (0); 412911106dfSjm199354 } 413911106dfSjm199354 414911106dfSjm199354 415911106dfSjm199354 /* 416911106dfSjm199354 * vs_icap_respmod_request 417911106dfSjm199354 * 418911106dfSjm199354 * Send respmod request and receive and process ICAP response. 419911106dfSjm199354 * Preview: 420911106dfSjm199354 * ICAP allows for an optional "preview" request. In the option negotiation, 421911106dfSjm199354 * the server may ask for a list of types to be previewed, or to be sent 422911106dfSjm199354 * complete (no preview). 423911106dfSjm199354 * This is advisory. It is ok to skip the preview step, as done when the file 424911106dfSjm199354 * is smaller than the preview_len. 425911106dfSjm199354 * Process Response: 426911106dfSjm199354 * - read and parse the RESPMOD response headers 427911106dfSjm199354 * - populate the result structure 428911106dfSjm199354 * - read any encapsulated response headers 429911106dfSjm199354 * - read any encapsulated response body and, if it represents cleaned 430911106dfSjm199354 * file data, overwrite the file with it 431911106dfSjm199354 * 432911106dfSjm199354 * Returns: 0 - success 433911106dfSjm199354 * -1 - error 434911106dfSjm199354 */ 435911106dfSjm199354 static int 436911106dfSjm199354 vs_icap_respmod_request(vs_scan_ctx_t *ctx) 437911106dfSjm199354 { 438911106dfSjm199354 int rv; 439911106dfSjm199354 int bytes_sent, send_len; 440911106dfSjm199354 uint64_t resid = ctx->vsc_fsize; 441911106dfSjm199354 442911106dfSjm199354 if (vs_icap_may_preview(ctx)) { 443911106dfSjm199354 444911106dfSjm199354 if ((rv = vs_icap_send_preview(ctx)) < 0) 445911106dfSjm199354 return (-1); 446911106dfSjm199354 447911106dfSjm199354 if (vs_icap_read_respmod_resp(ctx) < 0) 448911106dfSjm199354 return (-1); 449911106dfSjm199354 450911106dfSjm199354 if (ctx->vsc_info.vsi_icap_rc != VS_RESP_CONTINUE) 451911106dfSjm199354 return (0); 452911106dfSjm199354 453911106dfSjm199354 bytes_sent = rv; 454911106dfSjm199354 455911106dfSjm199354 /* If > block (VS_BUF_SZ) remains, re-align to block boundary */ 456911106dfSjm199354 if ((ctx->vsc_fsize - (uint64_t)bytes_sent) > VS_BUF_SZ) { 457911106dfSjm199354 send_len = VS_BUF_SZ - bytes_sent; 458911106dfSjm199354 if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0) 459911106dfSjm199354 return (-1); 460911106dfSjm199354 bytes_sent += rv; 461911106dfSjm199354 } 462911106dfSjm199354 463911106dfSjm199354 resid -= (uint64_t)bytes_sent; 464911106dfSjm199354 465911106dfSjm199354 } else { 466911106dfSjm199354 467911106dfSjm199354 if (vs_icap_send_respmod_hdr(ctx, 0) < 0) 468911106dfSjm199354 return (-1); 469911106dfSjm199354 } 470911106dfSjm199354 471911106dfSjm199354 /* Send the remainder of the file... */ 472911106dfSjm199354 while (resid) { 473911106dfSjm199354 send_len = (resid > VS_BUF_SZ) ? VS_BUF_SZ : resid; 474911106dfSjm199354 475911106dfSjm199354 if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0) 476911106dfSjm199354 return (-1); 477911106dfSjm199354 478911106dfSjm199354 if (rv == 0) 479911106dfSjm199354 break; 480911106dfSjm199354 481911106dfSjm199354 resid -= (uint64_t)rv; 482911106dfSjm199354 } 483911106dfSjm199354 484911106dfSjm199354 if (vs_icap_send_termination(ctx) < 0) 485911106dfSjm199354 return (-1); 486911106dfSjm199354 487911106dfSjm199354 /* sending of ICAP request complete */ 488911106dfSjm199354 if (vs_icap_read_respmod_resp(ctx) < 0) 489911106dfSjm199354 return (-1); 490911106dfSjm199354 491911106dfSjm199354 return (0); 492911106dfSjm199354 } 493911106dfSjm199354 494911106dfSjm199354 495911106dfSjm199354 /* 496911106dfSjm199354 * vs_icap_may_preview 497911106dfSjm199354 * 498911106dfSjm199354 * Returns: 1 - preview 499911106dfSjm199354 * 0 - don't preview 500911106dfSjm199354 */ 501911106dfSjm199354 static int 502911106dfSjm199354 vs_icap_may_preview(vs_scan_ctx_t *ctx) 503911106dfSjm199354 { 504911106dfSjm199354 int in_list = 0; 505911106dfSjm199354 char *ext; 506911106dfSjm199354 vs_options_t *opts = &ctx->vsc_options; 507911106dfSjm199354 508911106dfSjm199354 if (opts->vso_xfer_how == VS_PREVIEW_NONE) 509911106dfSjm199354 return (0); 510911106dfSjm199354 511911106dfSjm199354 /* if the file is smaller than the preview size, don't preview */ 512911106dfSjm199354 if (ctx->vsc_fsize < (uint64_t)ctx->vsc_options.vso_preview_len) 513911106dfSjm199354 return (0); 514911106dfSjm199354 515911106dfSjm199354 switch (opts->vso_xfer_how) { 516911106dfSjm199354 case VS_PREVIEW_ALL: 517911106dfSjm199354 return (1); 518911106dfSjm199354 case VS_PREVIEW_EXCEPT: 519911106dfSjm199354 /* Preview everything except types in xfer_complete */ 520911106dfSjm199354 if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0) 521911106dfSjm199354 in_list = vs_icap_check_ext(ext, 522911106dfSjm199354 opts->vso_xfer_complete); 523911106dfSjm199354 return ((in_list) ? 0 : 1); 524911106dfSjm199354 case VS_PREVIEW_LIST: 525911106dfSjm199354 /* Preview only types in the the xfer_preview list */ 526911106dfSjm199354 if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0) 527911106dfSjm199354 in_list = vs_icap_check_ext(ext, 528911106dfSjm199354 opts->vso_xfer_preview); 529911106dfSjm199354 return ((in_list) ? 1 : 0); 530911106dfSjm199354 } 531911106dfSjm199354 532911106dfSjm199354 return (1); 533911106dfSjm199354 } 534911106dfSjm199354 535911106dfSjm199354 536911106dfSjm199354 /* 537911106dfSjm199354 * vs_icap_find_ext 538911106dfSjm199354 * 539911106dfSjm199354 * Returns: ptr to file's extension in fname 540911106dfSjm199354 * 0 if no extension 541911106dfSjm199354 */ 542911106dfSjm199354 static char * 543911106dfSjm199354 vs_icap_find_ext(char *fname) 544911106dfSjm199354 { 545911106dfSjm199354 char *last_comp, *ext_str = 0; 546911106dfSjm199354 547911106dfSjm199354 if ((last_comp = strrchr(fname, '/')) != 0) { 548911106dfSjm199354 last_comp++; 549911106dfSjm199354 } else { 550911106dfSjm199354 last_comp = fname; 551911106dfSjm199354 } 552911106dfSjm199354 553911106dfSjm199354 /* Get file extension */ 554911106dfSjm199354 if ((ext_str = strrchr(last_comp, '.')) != 0) { 555911106dfSjm199354 ext_str++; 556911106dfSjm199354 if (strlen(ext_str) == 0) 557911106dfSjm199354 ext_str = 0; 558911106dfSjm199354 } 559911106dfSjm199354 560911106dfSjm199354 return (ext_str); 561911106dfSjm199354 } 562911106dfSjm199354 563911106dfSjm199354 564911106dfSjm199354 /* 565911106dfSjm199354 * vs_icap_send_preview 566911106dfSjm199354 * 567911106dfSjm199354 * Returns: bytes sent (preview + alignment) 568911106dfSjm199354 * -1 - error 569911106dfSjm199354 */ 570911106dfSjm199354 static int 571911106dfSjm199354 vs_icap_send_preview(vs_scan_ctx_t *ctx) 572911106dfSjm199354 { 573911106dfSjm199354 int preview_len = ctx->vsc_options.vso_preview_len; 574911106dfSjm199354 int bytes_sent; 575911106dfSjm199354 576911106dfSjm199354 /* Send a RESPMOD request with "preview" mode. */ 577911106dfSjm199354 if (vs_icap_send_respmod_hdr(ctx, 'P') < 0) 578911106dfSjm199354 return (-1); 579911106dfSjm199354 580911106dfSjm199354 if ((bytes_sent = vs_icap_send_chunk(ctx, preview_len)) < 0) 581911106dfSjm199354 return (-1); 582911106dfSjm199354 583911106dfSjm199354 if (bytes_sent < preview_len) 584911106dfSjm199354 return (-1); 585911106dfSjm199354 586911106dfSjm199354 if (vs_icap_send_termination(ctx) < 0) 587911106dfSjm199354 return (-1); 588911106dfSjm199354 589911106dfSjm199354 return (bytes_sent); 590911106dfSjm199354 } 591911106dfSjm199354 592911106dfSjm199354 593911106dfSjm199354 /* 594911106dfSjm199354 * vs_icap_send_respmod_hdr 595911106dfSjm199354 * 596911106dfSjm199354 * Create and send the RESPMOD request headers to the scan engine. 597911106dfSjm199354 * 598911106dfSjm199354 * Returns: 0 success 599911106dfSjm199354 * < 0 error 600911106dfSjm199354 */ 601911106dfSjm199354 static int 602911106dfSjm199354 vs_icap_send_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview) 603911106dfSjm199354 { 604911106dfSjm199354 int len; 605911106dfSjm199354 606911106dfSjm199354 if ((len = vs_icap_create_respmod_hdr(ctx, ispreview)) == -1) { 607911106dfSjm199354 /* non SE error */ 608911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR; 609911106dfSjm199354 return (-1); 610911106dfSjm199354 } 611911106dfSjm199354 612911106dfSjm199354 /* send the headers */ 613911106dfSjm199354 if (vs_icap_write(ctx->vsc_sockfd, 614911106dfSjm199354 ctx->vsc_info.vsi_send_buf, len) < 0) { 615911106dfSjm199354 return (-1); 616911106dfSjm199354 } 617911106dfSjm199354 618911106dfSjm199354 return (0); 619911106dfSjm199354 } 620911106dfSjm199354 621911106dfSjm199354 622911106dfSjm199354 /* 623911106dfSjm199354 * vs_icap_create_respmod_hdr 624911106dfSjm199354 * 625911106dfSjm199354 * Create the RESPMOD request headers. 626911106dfSjm199354 * - RESPMOD, Host, Allow, [Preview], Encapsulated, encapsulated request hdr, 627911106dfSjm199354 * encapsulated response hdr 628911106dfSjm199354 * Encapsulated data is sent separately subsequent to vs_icap_send_respmod_hdr, 629911106dfSjm199354 * via calls to vs_icap_send_chunk. 630911106dfSjm199354 * 631911106dfSjm199354 * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME) 632911106dfSjm199354 * after the IP address, otherwise it closes the connection. 633911106dfSjm199354 * 634911106dfSjm199354 * Returns: -1 error 635911106dfSjm199354 * length of headers data 636911106dfSjm199354 */ 637911106dfSjm199354 static int 638911106dfSjm199354 vs_icap_create_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview) 639911106dfSjm199354 { 640911106dfSjm199354 char my_host_name[MAXHOSTNAMELEN]; 641911106dfSjm199354 int hbufsp = VS_BUF_SZ; 642911106dfSjm199354 char *hbuf0 = ctx->vsc_info.vsi_send_buf; 643911106dfSjm199354 char *hbufp = hbuf0; 644911106dfSjm199354 char *encap_hdr, *encap_off0, *req_hdr, *res_hdr, *res_body; 645911106dfSjm199354 int preview_len = ctx->vsc_options.vso_preview_len; 646911106dfSjm199354 int tlen; 647911106dfSjm199354 648911106dfSjm199354 if (gethostname(my_host_name, sizeof (my_host_name)) != 0) { 649911106dfSjm199354 /* non SE error */ 650911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR; 651911106dfSjm199354 return (-1); 652911106dfSjm199354 } 653911106dfSjm199354 654911106dfSjm199354 (void) memset(hbufp, 0, hbufsp); 655911106dfSjm199354 656911106dfSjm199354 /* First the ICAP "request" part. (at offset 0) */ 657911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "RESPMOD icap://%s:%d/%s %s\r\n", 658911106dfSjm199354 ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER); 659911106dfSjm199354 if (tlen >= hbufsp) 660911106dfSjm199354 return (-1); 661911106dfSjm199354 hbufp += tlen; hbufsp -= tlen; 662911106dfSjm199354 663911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "Host: %s\r\n", my_host_name); 664911106dfSjm199354 if (tlen >= hbufsp) 665911106dfSjm199354 return (-1); 666911106dfSjm199354 hbufp += tlen; hbufsp -= tlen; 667911106dfSjm199354 668911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "Allow: 204\r\n"); 669911106dfSjm199354 if (tlen >= hbufsp) 670911106dfSjm199354 return (-1); 671911106dfSjm199354 hbufp += tlen; hbufsp -= tlen; 672911106dfSjm199354 673911106dfSjm199354 if (ispreview) { 674911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "Preview: %d\r\n", preview_len); 675911106dfSjm199354 if (tlen >= hbufsp) 676911106dfSjm199354 return (-1); 677911106dfSjm199354 hbufp += tlen; hbufsp -= tlen; 678911106dfSjm199354 } 679911106dfSjm199354 680911106dfSjm199354 /* Reserve space to later insert encapsulation offsets, & blank line */ 681911106dfSjm199354 encap_hdr = hbufp; 682911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "%*.*s\r\n\r\n", 683911106dfSjm199354 VS_ENCAP_SZ, VS_ENCAP_SZ, ""); 684911106dfSjm199354 if (tlen >= hbufsp) 685911106dfSjm199354 return (-1); 686911106dfSjm199354 hbufp += tlen; hbufsp -= tlen; 687911106dfSjm199354 688911106dfSjm199354 /* "offset zero" for the encapsulated parts that follow */ 689911106dfSjm199354 encap_off0 = hbufp; 690911106dfSjm199354 691911106dfSjm199354 /* Encapsulated request header (req_hdr) & blank line */ 692911106dfSjm199354 req_hdr = hbufp; 693911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "GET http://%s", my_host_name); 694911106dfSjm199354 if (tlen >= hbufsp) 695911106dfSjm199354 return (-1); 696911106dfSjm199354 hbufp += tlen; hbufsp -= tlen; 697911106dfSjm199354 698911106dfSjm199354 tlen = vs_icap_uri_encode(hbufp, hbufsp, ctx->vsc_fname); 699911106dfSjm199354 if (tlen < 0) 700911106dfSjm199354 return (-1); 701911106dfSjm199354 hbufp += tlen; hbufsp -= tlen; 702911106dfSjm199354 703911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, " HTTP/1.1\r\n\r\n"); 704911106dfSjm199354 if (tlen >= hbufsp) 705911106dfSjm199354 return (-1); 706911106dfSjm199354 hbufp += tlen; hbufsp -= tlen; 707911106dfSjm199354 708911106dfSjm199354 /* Encapsulated response header (res_hdr) & blank line */ 709911106dfSjm199354 res_hdr = hbufp; 710911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "HTTP/1.1 200 OK\r\n"); 711911106dfSjm199354 if (tlen >= hbufsp) 712911106dfSjm199354 return (-1); 713911106dfSjm199354 hbufp += tlen; hbufsp -= tlen; 714911106dfSjm199354 715911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "Transfer-Encoding: chunked\r\n\r\n"); 716911106dfSjm199354 if (tlen >= hbufsp) 717911106dfSjm199354 return (-1); 718911106dfSjm199354 hbufp += tlen; hbufsp -= tlen; 719911106dfSjm199354 720911106dfSjm199354 /* response body section - res-body ("chunked data") */ 721911106dfSjm199354 res_body = hbufp; 722911106dfSjm199354 723911106dfSjm199354 /* Insert offsets in encap_hdr */ 724911106dfSjm199354 tlen = snprintf(encap_hdr, VS_ENCAP_SZ, "Encapsulated: " 725911106dfSjm199354 "req-hdr=%d, res-hdr=%d, res-body=%d", 726911106dfSjm199354 req_hdr - encap_off0, res_hdr - encap_off0, res_body - encap_off0); 727911106dfSjm199354 /* undo the null from snprintf */ 728911106dfSjm199354 encap_hdr[tlen] = ' '; 729911106dfSjm199354 730911106dfSjm199354 /* return length */ 731911106dfSjm199354 return (hbufp - hbuf0); 732911106dfSjm199354 } 733911106dfSjm199354 734911106dfSjm199354 735911106dfSjm199354 /* 736911106dfSjm199354 * vs_icap_read_respmod_resp 737911106dfSjm199354 * 738911106dfSjm199354 * Used for both preview and final RESMOD response 739911106dfSjm199354 */ 740911106dfSjm199354 static int 741911106dfSjm199354 vs_icap_read_respmod_resp(vs_scan_ctx_t *ctx) 742911106dfSjm199354 { 743911106dfSjm199354 if (vs_icap_read_resp_code(ctx) < 0) 744911106dfSjm199354 return (-1); 745911106dfSjm199354 746911106dfSjm199354 if (vs_icap_read_hdr(ctx, resp_hdrs, VS_RESP_HDR_MAX) < 0) 747911106dfSjm199354 return (-1); 748911106dfSjm199354 749911106dfSjm199354 if (ctx->vsc_info.vsi_icap_rc == VS_RESP_CONTINUE) { 750911106dfSjm199354 /* A VS_RESP_CONTINUE should not have encapsulated data */ 751911106dfSjm199354 if ((ctx->vsc_info.vsi_res_hdr) || 752911106dfSjm199354 (ctx->vsc_info.vsi_res_body)) { 753911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error -" 754911106dfSjm199354 "- encapsulated data in Continue response"); 755911106dfSjm199354 return (-1); 756911106dfSjm199354 } 757911106dfSjm199354 } else { 758911106dfSjm199354 if (vs_icap_set_scan_result(ctx) < 0) 759911106dfSjm199354 return (-1); 760911106dfSjm199354 761911106dfSjm199354 if (ctx->vsc_info.vsi_res_hdr) { 762911106dfSjm199354 if (vs_icap_read_encap_hdr(ctx) < 0) 763911106dfSjm199354 return (-1); 764911106dfSjm199354 } 765911106dfSjm199354 766911106dfSjm199354 if (ctx->vsc_info.vsi_res_body) 767911106dfSjm199354 vs_icap_read_encap_data(ctx); 768911106dfSjm199354 else if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED) 769911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN; 770911106dfSjm199354 } 771911106dfSjm199354 772911106dfSjm199354 return (0); 773911106dfSjm199354 } 774911106dfSjm199354 775911106dfSjm199354 776911106dfSjm199354 /* 777911106dfSjm199354 * vs_icap_read_resp_code 778911106dfSjm199354 * 779911106dfSjm199354 * Get the response code from the icap response messages 780911106dfSjm199354 */ 781911106dfSjm199354 static int 782911106dfSjm199354 vs_icap_read_resp_code(vs_scan_ctx_t *ctx) 783911106dfSjm199354 { 784911106dfSjm199354 char *buf = ctx->vsc_info.vsi_recv_buf; 785911106dfSjm199354 int retval; 786911106dfSjm199354 787911106dfSjm199354 /* Break on error or non-blank line. */ 788911106dfSjm199354 for (;;) { 789911106dfSjm199354 (void) memset(buf, '\0', VS_BUF_SZ); 790911106dfSjm199354 791911106dfSjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 792911106dfSjm199354 return (-1); 793911106dfSjm199354 794911106dfSjm199354 if (retval && buf[0]) { 795911106dfSjm199354 if (MATCH(buf, VS_ICAP_VER)) { 796911106dfSjm199354 (void) sscanf(buf+8, "%d", 797911106dfSjm199354 &ctx->vsc_info.vsi_icap_rc); 798911106dfSjm199354 return (0); 799911106dfSjm199354 } 800911106dfSjm199354 801911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error -" 802911106dfSjm199354 "- expected ICAP/1.0, received %s", buf); 803911106dfSjm199354 804911106dfSjm199354 return (-1); 805911106dfSjm199354 } 806911106dfSjm199354 } 807911106dfSjm199354 } 808911106dfSjm199354 809911106dfSjm199354 810911106dfSjm199354 /* 811911106dfSjm199354 * vs_icap_read_hdr 812911106dfSjm199354 * 813911106dfSjm199354 * Reads all response headers. 814911106dfSjm199354 * As each line is read it is parsed and passed to the appropriate handler. 815911106dfSjm199354 * 816911106dfSjm199354 * Returns: 0 - success 817911106dfSjm199354 * -1 - error 818911106dfSjm199354 */ 819911106dfSjm199354 static int 820911106dfSjm199354 vs_icap_read_hdr(vs_scan_ctx_t *ctx, vs_hdr_t hdrs[], int num_hdrs) 821911106dfSjm199354 { 822911106dfSjm199354 char *buf = ctx->vsc_info.vsi_recv_buf; 823911106dfSjm199354 int i, retval; 824911106dfSjm199354 char *name, *val; 825911106dfSjm199354 826911106dfSjm199354 /* Break on error or blank line. */ 827911106dfSjm199354 for (;;) { 828911106dfSjm199354 (void) memset(buf, '\0', VS_BUF_SZ); 829911106dfSjm199354 830911106dfSjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 831911106dfSjm199354 return (-1); 832911106dfSjm199354 833911106dfSjm199354 /* Empty line (CR/LF) normal break */ 834911106dfSjm199354 if ((retval == 0) || (!buf[0])) 835911106dfSjm199354 break; 836911106dfSjm199354 837911106dfSjm199354 vs_icap_parse_hdrs(':', buf, &name, &val); 838911106dfSjm199354 839911106dfSjm199354 for (i = 0; i < num_hdrs; i++) { 840911106dfSjm199354 if (strcmp(name, hdrs[i].vsh_name) == 0) { 841911106dfSjm199354 hdrs[i].vsh_func(ctx, hdrs[i].vsh_id, val); 842911106dfSjm199354 break; 843911106dfSjm199354 } 844911106dfSjm199354 } 845911106dfSjm199354 } 846911106dfSjm199354 847911106dfSjm199354 return ((retval >= 0) ? 0 : -1); 848911106dfSjm199354 } 849911106dfSjm199354 850911106dfSjm199354 851911106dfSjm199354 /* 852911106dfSjm199354 * vs_icap_set_scan_result 853911106dfSjm199354 * 854911106dfSjm199354 * Sets the vs_result_t vsr_rc from the icap_resp_code and 855911106dfSjm199354 * any violation information in vs_result_t 856911106dfSjm199354 * 857911106dfSjm199354 * Returns: 0 - success 858911106dfSjm199354 * -1 - error 859911106dfSjm199354 */ 860911106dfSjm199354 static int 861911106dfSjm199354 vs_icap_set_scan_result(vs_scan_ctx_t *ctx) 862911106dfSjm199354 { 863911106dfSjm199354 int i; 864911106dfSjm199354 vs_result_t *result = ctx->vsc_result; 865911106dfSjm199354 866911106dfSjm199354 if (!result->vsr_scanstamp) 867911106dfSjm199354 (void) strlcpy(result->vsr_scanstamp, 868911106dfSjm199354 ctx->vsc_options.vso_scanstamp, sizeof (vs_scanstamp_t)); 869911106dfSjm199354 870911106dfSjm199354 switch (ctx->vsc_info.vsi_icap_rc) { 871911106dfSjm199354 case VS_RESP_NO_CONT_NEEDED: 872911106dfSjm199354 result->vsr_rc = VS_RESULT_CLEAN; 873911106dfSjm199354 break; 874911106dfSjm199354 875911106dfSjm199354 case VS_RESP_OK: 876911106dfSjm199354 /* if we have no violations , that means all ok */ 877911106dfSjm199354 if (result->vsr_nviolations == 0) { 878911106dfSjm199354 result->vsr_rc = VS_RESULT_CLEAN; 879911106dfSjm199354 break; 880911106dfSjm199354 } 881911106dfSjm199354 882911106dfSjm199354 /* Any infections not repaired? */ 883911106dfSjm199354 result->vsr_rc = VS_RESULT_CLEANED; 884911106dfSjm199354 for (i = 0; i < result->vsr_nviolations; i++) { 885911106dfSjm199354 if (result->vsr_vrec[i].vr_res != 886911106dfSjm199354 VS_RES_FILE_REPAIRED) { 887911106dfSjm199354 result->vsr_rc = VS_RESULT_FORBIDDEN; 888911106dfSjm199354 break; 889911106dfSjm199354 } 890911106dfSjm199354 } 891911106dfSjm199354 break; 892911106dfSjm199354 893911106dfSjm199354 case VS_RESP_CREATED : 894911106dfSjm199354 /* file is repaired */ 895911106dfSjm199354 result->vsr_rc = VS_RESULT_CLEANED; 896911106dfSjm199354 break; 897911106dfSjm199354 898911106dfSjm199354 case VS_RESP_FORBIDDEN: 899911106dfSjm199354 /* file is infected and could not be repaired */ 900911106dfSjm199354 result->vsr_rc = VS_RESULT_FORBIDDEN; 901911106dfSjm199354 break; 902911106dfSjm199354 903911106dfSjm199354 default: 904911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error " 905911106dfSjm199354 "- unsupported scan result: %s", 906911106dfSjm199354 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc)); 907911106dfSjm199354 return (-1); 908911106dfSjm199354 } 909911106dfSjm199354 910911106dfSjm199354 return (0); 911911106dfSjm199354 } 912911106dfSjm199354 913911106dfSjm199354 914911106dfSjm199354 /* 915911106dfSjm199354 * vs_icap_read_encap_hdr 916911106dfSjm199354 * 917911106dfSjm199354 * Read the encapsulated response header to determine the length of 918911106dfSjm199354 * encapsulated data and, in some cases, to detect the infected state 919911106dfSjm199354 * of the file. 920911106dfSjm199354 * 921911106dfSjm199354 * Use of http response code: 922911106dfSjm199354 * Trend IWSS does not return virus information in the RESPMOD response 923911106dfSjm199354 * headers unless the OPTIONAL "include X_Infection_Found" checkbox is 924911106dfSjm199354 * checked and "disable_infected_url_block=yes" is set in intscan.ini. 925911106dfSjm199354 * Thus if we haven't already detected the infected/cleaned status 926911106dfSjm199354 * (ie if vsr_rc == VS_RESULT_CLEAN) we attempt to detect the 927911106dfSjm199354 * infected/cleaned state of a file from a combination of the ICAP and 928911106dfSjm199354 * http resp codes. 929911106dfSjm199354 * Here are the response code values that Trend IWSS returns: 930911106dfSjm199354 * - clean: icap resp = VS_RESP_NO_CONT_NEEDED 931911106dfSjm199354 * - quarantine: icap resp = VS_RESP_OK, http resp = VS_RESP_FORBIDDEN 932911106dfSjm199354 * - cleaned: icap resp = VS_RESP_OK, http resp = VS_RESP_OK 933911106dfSjm199354 * For all other vendors' scan engines (so far) the infected/cleaned 934911106dfSjm199354 * state of the file has already been detected from the RESPMOD 935911106dfSjm199354 * response headers. 936911106dfSjm199354 */ 937911106dfSjm199354 static int 938911106dfSjm199354 vs_icap_read_encap_hdr(vs_scan_ctx_t *ctx) 939911106dfSjm199354 { 940911106dfSjm199354 char *buf = ctx->vsc_info.vsi_recv_buf; 941911106dfSjm199354 char *name, *value; 942911106dfSjm199354 int retval; 943911106dfSjm199354 944911106dfSjm199354 /* Break on error or blank line. */ 945911106dfSjm199354 for (;;) { 946911106dfSjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 947911106dfSjm199354 return (-1); 948911106dfSjm199354 949911106dfSjm199354 /* Empty line (CR/LF) normal break */ 950911106dfSjm199354 if ((retval == 0) || (!buf[0])) 951911106dfSjm199354 break; 952911106dfSjm199354 953911106dfSjm199354 if (MATCH(buf, "HTTP/1.1")) { 954911106dfSjm199354 (void) sscanf(buf + 8, "%d", 955911106dfSjm199354 &ctx->vsc_info.vsi_http_rc); 956911106dfSjm199354 ctx->vsc_info.vsi_html_content = B_TRUE; 957911106dfSjm199354 958911106dfSjm199354 /* if not yet detected infection, interpret http_rc */ 959911106dfSjm199354 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEAN) { 960911106dfSjm199354 if ((ctx->vsc_info.vsi_icap_rc == VS_RESP_OK) && 961911106dfSjm199354 (ctx->vsc_info.vsi_http_rc == VS_RESP_OK)) { 962911106dfSjm199354 ctx->vsc_result->vsr_rc = 963911106dfSjm199354 VS_RESULT_CLEANED; 964911106dfSjm199354 } else { 965911106dfSjm199354 ctx->vsc_result->vsr_rc = 966911106dfSjm199354 VS_RESULT_FORBIDDEN; 967911106dfSjm199354 } 968911106dfSjm199354 } 969911106dfSjm199354 } else { 970911106dfSjm199354 vs_icap_parse_hdrs(':', buf, &name, &value); 971911106dfSjm199354 if (name && (MATCH(name, "Content-Length"))) { 972911106dfSjm199354 (void) sscanf(value, "%d", 973911106dfSjm199354 &ctx->vsc_info.vsi_content_len); 974911106dfSjm199354 } 975911106dfSjm199354 } 976911106dfSjm199354 } 977911106dfSjm199354 978911106dfSjm199354 return (0); 979911106dfSjm199354 } 980911106dfSjm199354 981911106dfSjm199354 982911106dfSjm199354 /* 983911106dfSjm199354 * vs_icap_read_encap_data 984911106dfSjm199354 * 985911106dfSjm199354 * Read the encapsulated response data. 986911106dfSjm199354 * 987911106dfSjm199354 * If the response data represents cleaned file data (for an infected file) 988911106dfSjm199354 * and VS_NO_REPAIR is not set, open repair file to save the reponse body 989911106dfSjm199354 * data in. Set the repair flag in the scan context. The repair flag is used 990911106dfSjm199354 * during the processing of the response data. If the flag is set then the 991911106dfSjm199354 * data is written to file. If any error occurs which invalidates the repaired 992911106dfSjm199354 * data file the repair flag gets reset to 0, and the data will be discarded. 993911106dfSjm199354 * 994911106dfSjm199354 * The result is reset to VS_RESULT_FORBIDDEN until all of the cleaned data 995911106dfSjm199354 * has been successfully received and processed. It is then reset to 996911106dfSjm199354 * VS_RESULT_CLEANED. 997911106dfSjm199354 * 998911106dfSjm199354 * If the data doesn't represent cleaned file data, or we cannot (or don't 999911106dfSjm199354 * want to) write the cleaned data to file, the data is discarded (repair flag 1000911106dfSjm199354 * in ctx == 0). 1001911106dfSjm199354 */ 1002911106dfSjm199354 static void 1003911106dfSjm199354 vs_icap_read_encap_data(vs_scan_ctx_t *ctx) 1004911106dfSjm199354 { 1005911106dfSjm199354 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED) { 1006911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN; 1007911106dfSjm199354 1008911106dfSjm199354 if (!(ctx->vsc_flags & VS_NO_REPAIR)) { 1009911106dfSjm199354 if (vs_icap_create_repair_file(ctx) == 0) 1010911106dfSjm199354 ctx->vsc_repair = B_TRUE; 1011911106dfSjm199354 } 1012911106dfSjm199354 } 1013911106dfSjm199354 1014911106dfSjm199354 /* 1015911106dfSjm199354 * vs_icap_read_resp_body handles errors internally; 1016911106dfSjm199354 * resets ctx->vsc_repair 1017911106dfSjm199354 */ 1018911106dfSjm199354 (void) vs_icap_read_resp_body(ctx); 1019911106dfSjm199354 1020911106dfSjm199354 if (ctx->vsc_repair_fd != -1) { 1021911106dfSjm199354 (void) close(ctx->vsc_repair_fd); 1022911106dfSjm199354 1023911106dfSjm199354 if (ctx->vsc_repair) { 1024911106dfSjm199354 /* repair file contains the cleaned data */ 1025911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_CLEANED; 1026911106dfSjm199354 } else { 1027911106dfSjm199354 /* error occured processing data. Remove repair file */ 1028911106dfSjm199354 (void) unlink(ctx->vsc_repair_fname); 1029911106dfSjm199354 } 1030911106dfSjm199354 } 1031911106dfSjm199354 } 1032911106dfSjm199354 1033911106dfSjm199354 1034911106dfSjm199354 /* 1035911106dfSjm199354 * vs_icap_create_repair_file 1036911106dfSjm199354 * 1037911106dfSjm199354 * Create and open a file to save cleaned data in. 1038911106dfSjm199354 */ 1039911106dfSjm199354 static int 1040911106dfSjm199354 vs_icap_create_repair_file(vs_scan_ctx_t *ctx) 1041911106dfSjm199354 { 1042911106dfSjm199354 if (ctx->vsc_repair_fname == NULL) 1043911106dfSjm199354 return (-1); 1044911106dfSjm199354 1045911106dfSjm199354 if ((ctx->vsc_repair_fd = open(ctx->vsc_repair_fname, 1046911106dfSjm199354 O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0644)) == -1) { 1047911106dfSjm199354 return (-1); 1048911106dfSjm199354 } 1049911106dfSjm199354 1050911106dfSjm199354 return (0); 1051911106dfSjm199354 } 1052911106dfSjm199354 1053911106dfSjm199354 1054911106dfSjm199354 /* 1055911106dfSjm199354 * vs_icap_read_resp_body 1056911106dfSjm199354 * 1057911106dfSjm199354 * Repeatedly call vs_icap_read_body_chunk until it returns: 1058911106dfSjm199354 * 0 indicating that there's no more data to read or 1059911106dfSjm199354 * -1 indicating a read error -> reset ctx->vsc_repair 0 1060911106dfSjm199354 * 1061911106dfSjm199354 * Returns: 0 success 1062911106dfSjm199354 * -1 error 1063911106dfSjm199354 */ 1064911106dfSjm199354 static int 1065911106dfSjm199354 vs_icap_read_resp_body(vs_scan_ctx_t *ctx) 1066911106dfSjm199354 { 1067911106dfSjm199354 int retval; 1068911106dfSjm199354 1069911106dfSjm199354 while ((retval = vs_icap_read_body_chunk(ctx)) > 0) 1070911106dfSjm199354 ; 1071911106dfSjm199354 1072911106dfSjm199354 if (retval < 0) 1073911106dfSjm199354 ctx->vsc_repair = B_FALSE; 1074911106dfSjm199354 1075911106dfSjm199354 return (retval); 1076911106dfSjm199354 } 1077911106dfSjm199354 1078911106dfSjm199354 1079911106dfSjm199354 /* 1080911106dfSjm199354 * vs_icap_read_body_chunk 1081911106dfSjm199354 * 1082911106dfSjm199354 * Read the chunk size, then read the chunk of data and write the 1083911106dfSjm199354 * data to file repair_fd (or discard it). 1084911106dfSjm199354 * If the data cannot be successfully written to file, set repair 1085911106dfSjm199354 * flag in ctx to 0, and discard all subsequent data. 1086911106dfSjm199354 * 1087911106dfSjm199354 * Returns: chunk size 1088911106dfSjm199354 * -1 on error 1089911106dfSjm199354 */ 1090911106dfSjm199354 static int 1091911106dfSjm199354 vs_icap_read_body_chunk(vs_scan_ctx_t *ctx) 1092911106dfSjm199354 { 1093911106dfSjm199354 char *lbuf = ctx->vsc_info.vsi_recv_buf; 1094911106dfSjm199354 unsigned int chunk_size, resid; 1095911106dfSjm199354 int rsize; 1096911106dfSjm199354 1097911106dfSjm199354 /* Read and parse the chunk size. */ 1098911106dfSjm199354 if ((vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0) || 1099911106dfSjm199354 (!sscanf(lbuf, "%x", &chunk_size))) { 1100911106dfSjm199354 return (-1); 1101911106dfSjm199354 } 1102911106dfSjm199354 1103911106dfSjm199354 /* Read and save/discard chunk */ 1104911106dfSjm199354 resid = chunk_size; 1105911106dfSjm199354 while (resid) { 1106911106dfSjm199354 rsize = (resid < VS_BUF_SZ) ? resid : VS_BUF_SZ; 1107911106dfSjm199354 1108911106dfSjm199354 if ((rsize = vs_icap_read(ctx->vsc_sockfd, lbuf, rsize)) <= 0) 1109911106dfSjm199354 return (-1); 1110911106dfSjm199354 1111911106dfSjm199354 if (ctx->vsc_repair) { 1112911106dfSjm199354 if (vs_icap_write(ctx->vsc_repair_fd, lbuf, rsize) < 0) 1113911106dfSjm199354 ctx->vsc_repair = B_FALSE; 1114911106dfSjm199354 } 1115911106dfSjm199354 1116911106dfSjm199354 resid -= rsize; 1117911106dfSjm199354 } 1118911106dfSjm199354 1119911106dfSjm199354 /* Eat one CR/LF after the data */ 1120911106dfSjm199354 if (vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0) 1121911106dfSjm199354 return (-1); 1122911106dfSjm199354 1123911106dfSjm199354 if (lbuf[0]) { 1124911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error - expected blank line"); 1125911106dfSjm199354 return (-1); 1126911106dfSjm199354 } 1127911106dfSjm199354 1128911106dfSjm199354 return (chunk_size); 1129911106dfSjm199354 } 1130911106dfSjm199354 1131911106dfSjm199354 1132911106dfSjm199354 /* *********************************************************************** */ 1133911106dfSjm199354 /* Utility read, write functions */ 1134911106dfSjm199354 /* *********************************************************************** */ 1135911106dfSjm199354 1136911106dfSjm199354 /* 1137911106dfSjm199354 * vs_icap_write 1138911106dfSjm199354 * 1139911106dfSjm199354 * Return: 0 if all data successfully written 1140911106dfSjm199354 * -1 otherwise 1141911106dfSjm199354 */ 1142911106dfSjm199354 static int 1143911106dfSjm199354 vs_icap_write(int fd, char *buf, int buflen) 1144911106dfSjm199354 { 1145911106dfSjm199354 char *ptr = buf; 1146911106dfSjm199354 int resid = buflen; 1147911106dfSjm199354 int bytes_sent = 0; 1148911106dfSjm199354 1149911106dfSjm199354 while (resid > 0) { 1150911106dfSjm199354 errno = 0; 1151bfc848c6Sjm199354 bytes_sent = write(fd, ptr, resid); 1152911106dfSjm199354 if (bytes_sent < 0) { 1153911106dfSjm199354 if (errno == EINTR) 1154911106dfSjm199354 continue; 1155911106dfSjm199354 else 1156911106dfSjm199354 return (-1); 1157911106dfSjm199354 } 1158911106dfSjm199354 resid -= bytes_sent; 1159911106dfSjm199354 ptr += bytes_sent; 1160911106dfSjm199354 } 1161911106dfSjm199354 1162911106dfSjm199354 return (0); 1163911106dfSjm199354 } 1164911106dfSjm199354 1165911106dfSjm199354 1166911106dfSjm199354 /* 1167911106dfSjm199354 * vs_icap_read 1168911106dfSjm199354 * 1169911106dfSjm199354 * Returns: bytes_read (== len unless EOF hit before len bytes read) 1170911106dfSjm199354 * -1 error 1171911106dfSjm199354 */ 1172911106dfSjm199354 static int 1173911106dfSjm199354 vs_icap_read(int fd, char *buf, int len) 1174911106dfSjm199354 { 1175911106dfSjm199354 char *ptr = buf; 1176911106dfSjm199354 int resid = len; 1177911106dfSjm199354 int bytes_read = 0; 1178911106dfSjm199354 1179911106dfSjm199354 while (resid > 0) { 1180911106dfSjm199354 errno = 0; 1181bfc848c6Sjm199354 bytes_read = read(fd, ptr, resid); 1182911106dfSjm199354 if (bytes_read < 0) { 1183911106dfSjm199354 if (errno == EINTR) 1184911106dfSjm199354 continue; 1185911106dfSjm199354 else 1186911106dfSjm199354 return (-1); 1187911106dfSjm199354 } 1188911106dfSjm199354 resid -= bytes_read; 1189911106dfSjm199354 ptr += bytes_read; 1190911106dfSjm199354 } 1191911106dfSjm199354 1192911106dfSjm199354 return (len - resid); 1193911106dfSjm199354 } 1194911106dfSjm199354 1195911106dfSjm199354 1196911106dfSjm199354 /* 1197911106dfSjm199354 * vs_icap_send_chunk 1198911106dfSjm199354 * 1199911106dfSjm199354 * Send a "chunk" of file data, containing: 1200911106dfSjm199354 * - Length (in hex) CR/NL 1201911106dfSjm199354 * - [optiona data] 1202911106dfSjm199354 * - CR/NL 1203911106dfSjm199354 * 1204911106dfSjm199354 * Returns: data length sent (not including encapsulation) 1205911106dfSjm199354 * -1 - error 1206911106dfSjm199354 */ 1207911106dfSjm199354 static int 1208911106dfSjm199354 vs_icap_send_chunk(vs_scan_ctx_t *ctx, int chunk_len) 1209911106dfSjm199354 { 1210911106dfSjm199354 char *hdr = ctx->vsc_info.vsi_send_hdr; 1211911106dfSjm199354 char *dbuf = ctx->vsc_info.vsi_send_buf; 1212911106dfSjm199354 char *tail; 1213911106dfSjm199354 char head[VS_HDR_SZ + 1]; 1214911106dfSjm199354 int nread = 0, hlen, tlen = 2; 1215911106dfSjm199354 1216911106dfSjm199354 if (chunk_len > VS_BUF_SZ) 1217911106dfSjm199354 chunk_len = VS_BUF_SZ; 1218911106dfSjm199354 1219911106dfSjm199354 /* Read the data. */ 1220911106dfSjm199354 if ((nread = vs_icap_read(ctx->vsc_fd, dbuf, chunk_len)) < 0) 1221911106dfSjm199354 return (-1); 1222911106dfSjm199354 1223911106dfSjm199354 if (nread > 0) { 1224911106dfSjm199354 /* wrap data in a header and trailer */ 1225911106dfSjm199354 hlen = snprintf(head, sizeof (head), "%x\r\n", nread); 1226911106dfSjm199354 hdr += (VS_HDR_SZ - hlen); 1227911106dfSjm199354 (void) memcpy(hdr, head, hlen); 1228*db1a607eSjoyce mcintosh tail = dbuf + nread; 1229911106dfSjm199354 tail[0] = '\r'; 1230911106dfSjm199354 tail[1] = '\n'; 1231911106dfSjm199354 1232911106dfSjm199354 if (vs_icap_write(ctx->vsc_sockfd, hdr, 1233911106dfSjm199354 hlen + nread + tlen) < 0) { 1234911106dfSjm199354 return (-1); 1235911106dfSjm199354 } 1236911106dfSjm199354 } 1237911106dfSjm199354 1238911106dfSjm199354 return (nread); 1239911106dfSjm199354 } 1240911106dfSjm199354 1241911106dfSjm199354 1242911106dfSjm199354 /* 1243911106dfSjm199354 * vs_icap_send_termination 1244911106dfSjm199354 * 1245911106dfSjm199354 * Send 0 length termination to scan engine: "0\r\n\r\n" 1246911106dfSjm199354 * 1247911106dfSjm199354 * Returns: 0 - success 1248911106dfSjm199354 * -1 - error 1249911106dfSjm199354 */ 1250911106dfSjm199354 static int 1251911106dfSjm199354 vs_icap_send_termination(vs_scan_ctx_t *ctx) 1252911106dfSjm199354 { 1253911106dfSjm199354 if (vs_icap_write(ctx->vsc_sockfd, VS_TERMINATION, 1254911106dfSjm199354 strlen(VS_TERMINATION)) < 0) { 1255911106dfSjm199354 return (-1); 1256911106dfSjm199354 } 1257911106dfSjm199354 1258911106dfSjm199354 return (0); 1259911106dfSjm199354 } 1260911106dfSjm199354 1261911106dfSjm199354 1262911106dfSjm199354 /* 1263911106dfSjm199354 * vs_icap_readline 1264911106dfSjm199354 * 1265911106dfSjm199354 * Read a line of response data from the socket. \n indicates end of line. 1266911106dfSjm199354 * 1267911106dfSjm199354 * Returns: bytes read 1268911106dfSjm199354 * -1 - error 1269911106dfSjm199354 */ 1270911106dfSjm199354 static int 1271911106dfSjm199354 vs_icap_readline(vs_scan_ctx_t *ctx, char *buf, int buflen) 1272911106dfSjm199354 { 1273911106dfSjm199354 char c; 1274911106dfSjm199354 int i, retval; 1275911106dfSjm199354 1276911106dfSjm199354 i = 0; 1277911106dfSjm199354 for (;;) { 1278911106dfSjm199354 errno = 0; 1279911106dfSjm199354 retval = recv(ctx->vsc_sockfd, &c, 1, 0); 1280911106dfSjm199354 128153c11029Sjm199354 if (retval < 0 && errno == EINTR) 1282911106dfSjm199354 continue; 1283911106dfSjm199354 128453c11029Sjm199354 if (retval <= 0) { 1285bfc848c6Sjm199354 if (vscand_get_state() != VS_STATE_SHUTDOWN) { 1286bfc848c6Sjm199354 syslog(LOG_ERR, "Error receiving data from " 1287bfc848c6Sjm199354 "Scan Engine: %s", strerror(errno)); 1288bfc848c6Sjm199354 } 128953c11029Sjm199354 return (-1); 129053c11029Sjm199354 } 129153c11029Sjm199354 1292911106dfSjm199354 buf[i++] = c; 1293911106dfSjm199354 if (c == '\n') 1294911106dfSjm199354 break; 1295911106dfSjm199354 1296911106dfSjm199354 if (i >= (buflen - 2)) 1297911106dfSjm199354 return (-1); 1298911106dfSjm199354 } 1299911106dfSjm199354 1300911106dfSjm199354 buf[i] = '\0'; 1301911106dfSjm199354 1302911106dfSjm199354 /* remove preceding and trailing whitespace */ 1303911106dfSjm199354 vs_icap_trimspace(buf); 1304911106dfSjm199354 1305911106dfSjm199354 return (i); 1306911106dfSjm199354 } 1307911106dfSjm199354 1308911106dfSjm199354 1309911106dfSjm199354 /* ************************************************************************ */ 1310911106dfSjm199354 /* HEADER processing */ 1311911106dfSjm199354 /* ************************************************************************ */ 1312911106dfSjm199354 1313911106dfSjm199354 /* 1314911106dfSjm199354 * vs_icap_parse_hdrs 1315911106dfSjm199354 * 1316911106dfSjm199354 * parse an icap hdr line to find name and value 1317911106dfSjm199354 */ 1318911106dfSjm199354 static void 1319911106dfSjm199354 vs_icap_parse_hdrs(char delimiter, char *line, char **name, char **val) 1320911106dfSjm199354 { 1321911106dfSjm199354 char *q = line; 1322911106dfSjm199354 int line_len; 1323911106dfSjm199354 1324911106dfSjm199354 /* strip any spaces */ 1325911106dfSjm199354 while (*q == ' ') 1326911106dfSjm199354 q++; 1327911106dfSjm199354 1328911106dfSjm199354 *name = q; 1329911106dfSjm199354 *val = 0; 1330911106dfSjm199354 1331911106dfSjm199354 /* Empty line is normal termination */ 1332911106dfSjm199354 if ((line_len = strlen(line)) == 0) 1333911106dfSjm199354 return; 1334911106dfSjm199354 1335911106dfSjm199354 if ((q = strchr(line, delimiter)) != 0) { 1336911106dfSjm199354 *q++ = '\0'; 1337911106dfSjm199354 } else { 1338911106dfSjm199354 q = line + line_len; 1339911106dfSjm199354 } 1340911106dfSjm199354 1341911106dfSjm199354 /* value part follows spaces */ 1342911106dfSjm199354 while (*q == ' ') 1343911106dfSjm199354 q++; 1344911106dfSjm199354 1345911106dfSjm199354 *val = q; 1346911106dfSjm199354 } 1347911106dfSjm199354 1348911106dfSjm199354 1349911106dfSjm199354 /* 1350911106dfSjm199354 * vs_icap_resp_violations 1351911106dfSjm199354 */ 1352911106dfSjm199354 /*ARGSUSED*/ 1353911106dfSjm199354 static int 1354911106dfSjm199354 vs_icap_resp_violations(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1355911106dfSjm199354 { 1356911106dfSjm199354 int i, rv, vcnt; 1357911106dfSjm199354 1358911106dfSjm199354 (void) sscanf(line, "%d", &vcnt); 1359911106dfSjm199354 1360911106dfSjm199354 ctx->vsc_result->vsr_nviolations = 1361911106dfSjm199354 (vcnt > VS_MAX_VIOLATIONS) ? VS_MAX_VIOLATIONS : vcnt; 1362911106dfSjm199354 1363911106dfSjm199354 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIOLATIONS; 1364911106dfSjm199354 1365911106dfSjm199354 for (i = 0; i < vcnt; i++) { 1366911106dfSjm199354 if ((rv = vs_icap_resp_violation_rec(ctx, i)) < 0) 1367911106dfSjm199354 return (rv); 1368911106dfSjm199354 1369911106dfSjm199354 } 1370911106dfSjm199354 1371911106dfSjm199354 return (1); 1372911106dfSjm199354 } 1373911106dfSjm199354 1374911106dfSjm199354 1375911106dfSjm199354 /* 1376911106dfSjm199354 * vs_icap_resp_violation_rec 1377911106dfSjm199354 * 1378911106dfSjm199354 * take all violation data (up to VS_MAX_VIOLATIONS) and save it 1379911106dfSjm199354 * in violation_info. 1380911106dfSjm199354 * each violation has 4 lines of info: doc name, virus name, 1381911106dfSjm199354 * virus id and resolution 1382911106dfSjm199354 */ 1383911106dfSjm199354 static int 1384911106dfSjm199354 vs_icap_resp_violation_rec(vs_scan_ctx_t *ctx, int vr_idx) 1385911106dfSjm199354 { 1386911106dfSjm199354 int vline; 1387911106dfSjm199354 int retval = 0; 1388911106dfSjm199354 char *buf = ctx->vsc_info.vsi_recv_buf; 1389911106dfSjm199354 vs_vrec_t *vr; 1390911106dfSjm199354 1391911106dfSjm199354 if (vr_idx < VS_MAX_VIOLATIONS) { 1392911106dfSjm199354 vr = &ctx->vsc_result->vsr_vrec[vr_idx]; 1393911106dfSjm199354 } else { 1394911106dfSjm199354 vr = 0; 1395911106dfSjm199354 } 1396911106dfSjm199354 1397911106dfSjm199354 for (vline = 0; vline < VS_VIOLATION_LINES; vline++) { 1398911106dfSjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 1399911106dfSjm199354 return (-1); 1400911106dfSjm199354 1401911106dfSjm199354 /* empty line? */ 1402911106dfSjm199354 if ((retval == 0) || (!buf[0])) 1403911106dfSjm199354 break; 1404911106dfSjm199354 1405911106dfSjm199354 if (vr) { 1406911106dfSjm199354 switch (vline) { 1407911106dfSjm199354 case 0: /* doc name */ 1408911106dfSjm199354 break; 1409911106dfSjm199354 case 1: /* Threat Description */ 1410911106dfSjm199354 (void) strlcpy(vr->vr_desc, buf, 1411911106dfSjm199354 VS_DESCRIPTION_MAX); 1412911106dfSjm199354 break; 1413911106dfSjm199354 case 2: /* Problem ID */ 1414911106dfSjm199354 (void) sscanf(buf, "%d", &vr->vr_id); 1415911106dfSjm199354 break; 1416911106dfSjm199354 case 3: /* Resolution */ 1417911106dfSjm199354 (void) sscanf(buf, "%d", &vr->vr_res); 1418911106dfSjm199354 break; 1419911106dfSjm199354 } 1420911106dfSjm199354 } 1421911106dfSjm199354 } 1422911106dfSjm199354 1423911106dfSjm199354 return (1); 1424911106dfSjm199354 } 1425911106dfSjm199354 1426911106dfSjm199354 1427911106dfSjm199354 /* 1428911106dfSjm199354 * vs_icap_opt_value 1429911106dfSjm199354 * given an icap options hdr string, process value 1430911106dfSjm199354 */ 1431911106dfSjm199354 static int 1432911106dfSjm199354 vs_icap_opt_value(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1433911106dfSjm199354 { 1434911106dfSjm199354 int x; 1435911106dfSjm199354 long val; 1436911106dfSjm199354 char *end; 1437911106dfSjm199354 1438911106dfSjm199354 switch (hdr_id) { 1439911106dfSjm199354 case VS_OPT_PREVIEW: 1440911106dfSjm199354 (void) sscanf(line, "%d", &x); 1441911106dfSjm199354 if (x < VS_MIN_PREVIEW_LEN) 1442911106dfSjm199354 x = VS_MIN_PREVIEW_LEN; 1443911106dfSjm199354 if (x > VS_BUF_SZ) 1444911106dfSjm199354 x = VS_BUF_SZ; 1445911106dfSjm199354 ctx->vsc_options.vso_preview_len = x; 1446911106dfSjm199354 break; 1447911106dfSjm199354 1448911106dfSjm199354 case VS_OPT_TTL: 1449911106dfSjm199354 if (*line == 0) { 1450911106dfSjm199354 ctx->vsc_options.vso_req_time = -1; 1451911106dfSjm199354 break; 1452911106dfSjm199354 } 1453911106dfSjm199354 1454911106dfSjm199354 val = strtol(line, &end, 10); 1455911106dfSjm199354 if ((end != (line + strlen(line))) || (val < 0)) 1456911106dfSjm199354 break; 1457911106dfSjm199354 1458911106dfSjm199354 ctx->vsc_options.vso_ttl = val; 1459911106dfSjm199354 ctx->vsc_options.vso_req_time = time(0); 1460911106dfSjm199354 break; 1461911106dfSjm199354 1462911106dfSjm199354 case VS_OPT_ALLOW: 1463911106dfSjm199354 (void) sscanf(line, "%d", &ctx->vsc_options.vso_allow); 1464911106dfSjm199354 break; 1465911106dfSjm199354 1466911106dfSjm199354 case VS_OPT_SERVICE: 1467911106dfSjm199354 (void) strlcpy(ctx->vsc_options.vso_service, line, 1468911106dfSjm199354 VS_SERVICE_SZ); 1469911106dfSjm199354 break; 1470911106dfSjm199354 1471911106dfSjm199354 case VS_OPT_X_DEF_INFO: 1472911106dfSjm199354 (void) strlcpy(ctx->vsc_options.vso_defninfo, line, 1473911106dfSjm199354 VS_DEFN_SZ); 1474911106dfSjm199354 break; 1475911106dfSjm199354 1476911106dfSjm199354 case VS_OPT_METHODS: 1477911106dfSjm199354 if (strstr(line, "RESPMOD") != NULL) 1478911106dfSjm199354 ctx->vsc_options.vso_respmod = 1; 1479911106dfSjm199354 break; 1480911106dfSjm199354 1481911106dfSjm199354 case VS_OPT_ISTAG: 1482911106dfSjm199354 vs_icap_istag_to_scanstamp(line, 1483911106dfSjm199354 ctx->vsc_options.vso_scanstamp); 1484911106dfSjm199354 break; 1485911106dfSjm199354 1486911106dfSjm199354 default: 1487911106dfSjm199354 break; 1488911106dfSjm199354 1489911106dfSjm199354 } 1490911106dfSjm199354 1491911106dfSjm199354 return (1); 1492911106dfSjm199354 } 1493911106dfSjm199354 1494911106dfSjm199354 1495911106dfSjm199354 /* 1496911106dfSjm199354 * vs_icap_resp_istag 1497911106dfSjm199354 * 1498911106dfSjm199354 * Called to handle ISTAG when received in RESPMOD response. 1499911106dfSjm199354 * - populate result->vsr_scanstamp from istag 1500911106dfSjm199354 * - update the scanstamp in vs_options and log the update. 1501911106dfSjm199354 */ 1502911106dfSjm199354 /*ARGSUSED*/ 1503911106dfSjm199354 static int 1504911106dfSjm199354 vs_icap_resp_istag(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1505911106dfSjm199354 { 1506911106dfSjm199354 vs_icap_istag_to_scanstamp(line, ctx->vsc_result->vsr_scanstamp); 1507911106dfSjm199354 1508911106dfSjm199354 /* update the scanstamp in vs_options */ 1509911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex); 1510911106dfSjm199354 if (vs_icap_compare_se(ctx->vsc_idx, 1511911106dfSjm199354 ctx->vsc_host, ctx->vsc_port) == 0) { 1512911106dfSjm199354 if (strcmp(vs_options[ctx->vsc_idx].vso_scanstamp, 1513911106dfSjm199354 ctx->vsc_result->vsr_scanstamp) != 0) { 1514911106dfSjm199354 (void) strlcpy(vs_options[ctx->vsc_idx].vso_scanstamp, 1515911106dfSjm199354 ctx->vsc_result->vsr_scanstamp, 1516911106dfSjm199354 sizeof (vs_scanstamp_t)); 1517911106dfSjm199354 } 1518911106dfSjm199354 } 1519911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex); 1520911106dfSjm199354 1521911106dfSjm199354 return (1); 1522911106dfSjm199354 } 1523911106dfSjm199354 1524911106dfSjm199354 1525911106dfSjm199354 /* 1526911106dfSjm199354 * vs_icap_istag_to_scanstamp 1527911106dfSjm199354 * 1528911106dfSjm199354 * Copies istag into scanstamp, stripping leading and trailing 1529911106dfSjm199354 * quotes '"' from istag. If the istag is invalid (too long) 1530911106dfSjm199354 * scanstamp will be left unchanged. 1531911106dfSjm199354 * 1532911106dfSjm199354 * vs_scanstamp_t is defined to be large enough to hold the 1533911106dfSjm199354 * istag plus a null terminator. 1534911106dfSjm199354 */ 1535911106dfSjm199354 static void 1536911106dfSjm199354 vs_icap_istag_to_scanstamp(char *istag, vs_scanstamp_t scanstamp) 1537911106dfSjm199354 { 1538911106dfSjm199354 char *p = istag; 1539911106dfSjm199354 int len; 1540911106dfSjm199354 1541911106dfSjm199354 /* eliminate preceding '"' */ 1542911106dfSjm199354 if (p[0] == '"') 1543911106dfSjm199354 ++p; 1544911106dfSjm199354 1545911106dfSjm199354 /* eliminate trailing '"' */ 1546911106dfSjm199354 len = strlen(p); 1547911106dfSjm199354 if (p[len - 1] == '"') 1548911106dfSjm199354 --len; 1549911106dfSjm199354 1550911106dfSjm199354 if (len < sizeof (vs_scanstamp_t)) 1551911106dfSjm199354 (void) strlcpy(scanstamp, p, len + 1); 1552911106dfSjm199354 } 1553911106dfSjm199354 1554911106dfSjm199354 1555911106dfSjm199354 /* 1556911106dfSjm199354 * vs_icap_opt_ext 1557911106dfSjm199354 * 1558911106dfSjm199354 * read the transfer preview / transfer complete headers to 1559911106dfSjm199354 * determine which file types can be previewed 1560911106dfSjm199354 */ 1561911106dfSjm199354 static int 1562911106dfSjm199354 vs_icap_opt_ext(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1563911106dfSjm199354 { 1564911106dfSjm199354 vs_options_t *opt = &ctx->vsc_options; 1565911106dfSjm199354 1566911106dfSjm199354 switch (hdr_id) { 1567911106dfSjm199354 case VS_OPT_XFER_PREVIEW: 1568911106dfSjm199354 if (opt->vso_xfer_preview) { 1569911106dfSjm199354 free(opt->vso_xfer_preview); 1570911106dfSjm199354 opt->vso_xfer_preview = 0; 1571911106dfSjm199354 } 1572911106dfSjm199354 if (strstr(line, "*")) { 1573911106dfSjm199354 opt->vso_xfer_how = VS_PREVIEW_ALL; 1574911106dfSjm199354 } else { 1575911106dfSjm199354 opt->vso_xfer_preview = vs_icap_make_strvec 1576911106dfSjm199354 (line, EXT_SEPARATOR); 1577911106dfSjm199354 opt->vso_xfer_how = VS_PREVIEW_LIST; 1578911106dfSjm199354 } 1579911106dfSjm199354 break; 1580911106dfSjm199354 1581911106dfSjm199354 case VS_OPT_XFER_COMPLETE : 1582911106dfSjm199354 if (opt->vso_xfer_complete) { 1583911106dfSjm199354 free(opt->vso_xfer_complete); 1584911106dfSjm199354 opt->vso_xfer_complete = 0; 1585911106dfSjm199354 } 1586911106dfSjm199354 if (strstr(line, "*")) { 1587911106dfSjm199354 opt->vso_xfer_how = VS_PREVIEW_NONE; 1588911106dfSjm199354 } else { 1589911106dfSjm199354 opt->vso_xfer_complete = vs_icap_make_strvec 1590911106dfSjm199354 (line, EXT_SEPARATOR); 1591911106dfSjm199354 opt->vso_xfer_how = VS_PREVIEW_EXCEPT; 1592911106dfSjm199354 } 1593911106dfSjm199354 break; 1594911106dfSjm199354 default: 1595911106dfSjm199354 break; 1596911106dfSjm199354 } 1597911106dfSjm199354 1598911106dfSjm199354 return (1); 1599911106dfSjm199354 } 1600911106dfSjm199354 1601911106dfSjm199354 1602911106dfSjm199354 /* 1603911106dfSjm199354 * vs_icap_resp_infection 1604911106dfSjm199354 * 1605911106dfSjm199354 * read the type, resolution and threat description for each 1606911106dfSjm199354 * reported violation and save in ctx->vsc_result 1607911106dfSjm199354 */ 1608911106dfSjm199354 /*ARGSUSED*/ 1609911106dfSjm199354 static int 1610911106dfSjm199354 vs_icap_resp_infection(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1611911106dfSjm199354 { 1612911106dfSjm199354 char *name, *val; 1613911106dfSjm199354 int i, got = 0; 1614911106dfSjm199354 int type = 0, res = 0; 1615911106dfSjm199354 char *desc = 0; 1616911106dfSjm199354 vs_vrec_t *vr = 0; 1617911106dfSjm199354 1618911106dfSjm199354 for (i = 0; i < VS_INFECTION_FIELDS; i++) { 1619911106dfSjm199354 vs_icap_parse_hdrs('=', line, &name, &val); 1620911106dfSjm199354 1621911106dfSjm199354 switch (i) { 1622911106dfSjm199354 case 0: 1623911106dfSjm199354 if (MATCH(name, "Type")) { 1624911106dfSjm199354 (void) sscanf(val, "%d", &type); 1625911106dfSjm199354 got++; 1626911106dfSjm199354 } 1627911106dfSjm199354 break; 1628911106dfSjm199354 case 1: 1629911106dfSjm199354 if (MATCH(name, "Resolution")) { 1630911106dfSjm199354 (void) sscanf(val, "%d", &res); 1631911106dfSjm199354 got++; 1632911106dfSjm199354 } 1633911106dfSjm199354 break; 1634911106dfSjm199354 case 2: 1635911106dfSjm199354 if (MATCH(name, "Threat")) { 1636911106dfSjm199354 desc = val; 1637911106dfSjm199354 got++; 1638911106dfSjm199354 } 1639911106dfSjm199354 break; 1640911106dfSjm199354 default : 1641911106dfSjm199354 break; 1642911106dfSjm199354 } 1643911106dfSjm199354 1644911106dfSjm199354 if ((line = strstr(val, ";"))) 1645911106dfSjm199354 line++; 1646911106dfSjm199354 } 1647911106dfSjm199354 1648911106dfSjm199354 if (got != VS_INFECTION_FIELDS) 1649911106dfSjm199354 return (0); 1650911106dfSjm199354 1651911106dfSjm199354 /* 1652911106dfSjm199354 * We may have info from an X-Violations-Found record, (which provides 1653911106dfSjm199354 * more complete information). If so, don't destroy what we have. 1654911106dfSjm199354 */ 1655911106dfSjm199354 if ((ctx->vsc_result->vsr_nviolations == 0) || 1656911106dfSjm199354 (ctx->vsc_info.vsi_threat_hdr < VS_RESP_X_INFECTION)) { 1657911106dfSjm199354 vr = &ctx->vsc_result->vsr_vrec[0]; 1658911106dfSjm199354 vr->vr_id = type; 1659911106dfSjm199354 vr->vr_res = res; 1660911106dfSjm199354 (void) strlcpy(vr->vr_desc, desc, VS_DESCRIPTION_MAX); 1661911106dfSjm199354 ctx->vsc_result->vsr_nviolations = 1; 1662911106dfSjm199354 1663911106dfSjm199354 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_INFECTION; 1664911106dfSjm199354 } 1665911106dfSjm199354 1666911106dfSjm199354 return (1); 1667911106dfSjm199354 } 1668911106dfSjm199354 1669911106dfSjm199354 1670911106dfSjm199354 /* 1671911106dfSjm199354 * vs_icap_resp_virus_id 1672911106dfSjm199354 * 1673911106dfSjm199354 * X-Virus-ID is defined as being a shorter alternative to X-Infection-Found. 1674911106dfSjm199354 * If we already have virus information, from either X-Infection-Found or 1675911106dfSjm199354 * X-Violations-Found, it will be more complete, so don't overwrite it with 1676911106dfSjm199354 * the info from X-Virus-ID. 1677911106dfSjm199354 */ 1678911106dfSjm199354 /*ARGSUSED*/ 1679911106dfSjm199354 static int 1680911106dfSjm199354 vs_icap_resp_virus_id(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1681911106dfSjm199354 { 1682911106dfSjm199354 vs_vrec_t *vr = 0; 1683911106dfSjm199354 1684911106dfSjm199354 if (ctx->vsc_result->vsr_nviolations == 0) { 1685911106dfSjm199354 vr = &ctx->vsc_result->vsr_vrec[0]; 1686911106dfSjm199354 vr->vr_id = 0; 1687911106dfSjm199354 vr->vr_res = 0; 1688911106dfSjm199354 (void) strlcpy(vr->vr_desc, line, VS_DESCRIPTION_MAX); 1689911106dfSjm199354 ctx->vsc_result->vsr_nviolations = 1; 1690911106dfSjm199354 1691911106dfSjm199354 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIRUS_ID; 1692911106dfSjm199354 } 1693911106dfSjm199354 1694911106dfSjm199354 return (1); 1695911106dfSjm199354 } 1696911106dfSjm199354 1697911106dfSjm199354 1698911106dfSjm199354 /* 1699911106dfSjm199354 * vs_icap_resp_encap 1700911106dfSjm199354 * 1701911106dfSjm199354 * get the encapsulated header info 1702911106dfSjm199354 */ 1703911106dfSjm199354 /*ARGSUSED*/ 1704911106dfSjm199354 static int 1705911106dfSjm199354 vs_icap_resp_encap(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1706911106dfSjm199354 { 1707911106dfSjm199354 if (strstr(line, "res-hdr")) 1708911106dfSjm199354 ctx->vsc_info.vsi_res_hdr = B_TRUE; 1709911106dfSjm199354 1710911106dfSjm199354 if (strstr(line, "res-body")) 1711911106dfSjm199354 ctx->vsc_info.vsi_res_body = B_TRUE; 1712911106dfSjm199354 1713911106dfSjm199354 return (1); 1714911106dfSjm199354 } 1715911106dfSjm199354 1716911106dfSjm199354 1717911106dfSjm199354 /* 1718911106dfSjm199354 * Utility functions for handling OPTIONS data: vs_options_t 1719911106dfSjm199354 */ 1720911106dfSjm199354 1721911106dfSjm199354 /* 1722911106dfSjm199354 * vs_icap_compare_scanstamp 1723911106dfSjm199354 * compare scanstamp with that stored for engine idx 1724911106dfSjm199354 * 1725911106dfSjm199354 * Returns: 0 - if equal 1726911106dfSjm199354 */ 1727911106dfSjm199354 int 1728911106dfSjm199354 vs_icap_compare_scanstamp(int idx, vs_scanstamp_t scanstamp) 1729911106dfSjm199354 { 1730911106dfSjm199354 int rc; 1731911106dfSjm199354 1732911106dfSjm199354 if (!scanstamp || scanstamp[0] == '\0') 1733911106dfSjm199354 return (-1); 1734911106dfSjm199354 1735911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex); 1736911106dfSjm199354 rc = strcmp(scanstamp, vs_options[idx].vso_scanstamp); 1737911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex); 1738911106dfSjm199354 1739911106dfSjm199354 return (rc); 1740911106dfSjm199354 } 1741911106dfSjm199354 1742911106dfSjm199354 1743911106dfSjm199354 /* 1744911106dfSjm199354 * vs_icap_compare_se 1745911106dfSjm199354 * compare host and port with that stored for engine idx 1746911106dfSjm199354 * 1747911106dfSjm199354 * Returns: 0 - if equal 1748911106dfSjm199354 */ 1749911106dfSjm199354 static int 1750911106dfSjm199354 vs_icap_compare_se(int idx, char *host, int port) 1751911106dfSjm199354 { 1752911106dfSjm199354 if (vs_options[idx].vso_port != port) 1753911106dfSjm199354 return (-1); 1754911106dfSjm199354 1755911106dfSjm199354 if (strcmp(vs_options[idx].vso_host, host) != 0) 1756911106dfSjm199354 return (-1); 1757911106dfSjm199354 1758911106dfSjm199354 return (0); 1759911106dfSjm199354 } 1760911106dfSjm199354 1761911106dfSjm199354 1762911106dfSjm199354 /* 1763911106dfSjm199354 * vs_icap_free_options 1764911106dfSjm199354 * 1765911106dfSjm199354 * Free dynamic parts of vs_options_t: xfer_preview, xfer_complete 1766911106dfSjm199354 */ 1767911106dfSjm199354 static void 1768911106dfSjm199354 vs_icap_free_options(vs_options_t *options) 1769911106dfSjm199354 { 1770911106dfSjm199354 if (options->vso_xfer_preview) 1771911106dfSjm199354 free(options->vso_xfer_preview); 1772911106dfSjm199354 1773911106dfSjm199354 if (options->vso_xfer_complete) 1774911106dfSjm199354 free(options->vso_xfer_complete); 1775911106dfSjm199354 1776911106dfSjm199354 (void) memset(options, 0, sizeof (vs_options_t)); 1777911106dfSjm199354 } 1778911106dfSjm199354 1779911106dfSjm199354 1780911106dfSjm199354 /* 1781911106dfSjm199354 * vs_icap_copy_options 1782911106dfSjm199354 */ 1783911106dfSjm199354 void 1784911106dfSjm199354 vs_icap_copy_options(vs_options_t *to_opt, vs_options_t *from_opt) 1785911106dfSjm199354 { 1786911106dfSjm199354 *to_opt = *from_opt; 1787911106dfSjm199354 1788911106dfSjm199354 if (from_opt->vso_xfer_preview) { 1789911106dfSjm199354 to_opt->vso_xfer_preview = 1790911106dfSjm199354 vs_icap_copy_strvec(from_opt->vso_xfer_preview); 1791911106dfSjm199354 } 1792911106dfSjm199354 1793911106dfSjm199354 if (from_opt->vso_xfer_complete) { 1794911106dfSjm199354 to_opt->vso_xfer_complete = 1795911106dfSjm199354 vs_icap_copy_strvec(from_opt->vso_xfer_complete); 1796911106dfSjm199354 } 1797911106dfSjm199354 } 1798911106dfSjm199354 1799911106dfSjm199354 1800911106dfSjm199354 /* 1801911106dfSjm199354 * vs_icap_update_options 1802911106dfSjm199354 */ 1803911106dfSjm199354 static void 1804911106dfSjm199354 vs_icap_update_options(vs_scan_ctx_t *ctx) 1805911106dfSjm199354 { 1806911106dfSjm199354 int idx = ctx->vsc_idx; 1807911106dfSjm199354 1808911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex); 1809911106dfSjm199354 1810911106dfSjm199354 if (vs_icap_compare_se(idx, ctx->vsc_host, ctx->vsc_port) == 0) { 1811911106dfSjm199354 vs_icap_free_options(&vs_options[idx]); 1812911106dfSjm199354 vs_icap_copy_options(&vs_options[idx], &ctx->vsc_options); 1813911106dfSjm199354 } 1814911106dfSjm199354 1815911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex); 1816911106dfSjm199354 } 1817911106dfSjm199354 1818911106dfSjm199354 1819911106dfSjm199354 /* 1820911106dfSjm199354 * vs_icap_make_strvec 1821911106dfSjm199354 * 1822911106dfSjm199354 * Populate a iovec_t from line, where line is a string of 'sep' 1823911106dfSjm199354 * separated fields. Within the copy of line in the iovec_t each 1824911106dfSjm199354 * field will be null terminated with leading & trailing whitespace 1825911106dfSjm199354 * removed. This allows for fast searching. 1826911106dfSjm199354 * 1827911106dfSjm199354 * The iovec_t itself and the data it points to are allocated 1828911106dfSjm199354 * as a single chunk. 1829911106dfSjm199354 */ 1830911106dfSjm199354 static iovec_t * 1831911106dfSjm199354 vs_icap_make_strvec(char *line, const char *sep) 1832911106dfSjm199354 { 1833911106dfSjm199354 iovec_t *vec; 1834911106dfSjm199354 char *tmp, *ctx; 1835911106dfSjm199354 int datalen, len; 1836911106dfSjm199354 1837911106dfSjm199354 datalen = strlen(line) + 1; 1838911106dfSjm199354 len = sizeof (iovec_t) + datalen; 1839911106dfSjm199354 1840911106dfSjm199354 if ((vec = (iovec_t *)calloc(1, len)) == 0) 1841911106dfSjm199354 return (0); 1842911106dfSjm199354 1843911106dfSjm199354 vec->iov_len = len; 1844911106dfSjm199354 vec->iov_base = (char *)vec + sizeof (iovec_t); 1845911106dfSjm199354 (void) strlcpy(vec->iov_base, line, datalen); 1846911106dfSjm199354 1847911106dfSjm199354 /* tokenize data for easier searching */ 1848911106dfSjm199354 for (tmp = strtok_r(vec->iov_base, sep, &ctx); tmp; 1849911106dfSjm199354 tmp = strtok_r(0, sep, &ctx)) { 1850911106dfSjm199354 } 1851911106dfSjm199354 1852911106dfSjm199354 return (vec); 1853911106dfSjm199354 } 1854911106dfSjm199354 1855911106dfSjm199354 1856911106dfSjm199354 /* 1857911106dfSjm199354 * vs_icap_copy_strvec 1858911106dfSjm199354 * 1859911106dfSjm199354 * allocate and copy strvec 1860911106dfSjm199354 */ 1861911106dfSjm199354 static iovec_t * 1862911106dfSjm199354 vs_icap_copy_strvec(iovec_t *from_vec) 1863911106dfSjm199354 { 1864911106dfSjm199354 iovec_t *to_vec; 1865911106dfSjm199354 1866911106dfSjm199354 if ((to_vec = (iovec_t *)calloc(1, from_vec->iov_len)) == 0) 1867911106dfSjm199354 return (0); 1868911106dfSjm199354 1869911106dfSjm199354 bcopy(from_vec, to_vec, from_vec->iov_len); 1870911106dfSjm199354 to_vec->iov_base = (char *)to_vec + sizeof (iovec_t); 1871911106dfSjm199354 1872911106dfSjm199354 return (to_vec); 1873911106dfSjm199354 } 1874911106dfSjm199354 1875911106dfSjm199354 1876911106dfSjm199354 /* 1877911106dfSjm199354 * vs_icap_check_ext 1878911106dfSjm199354 * 1879911106dfSjm199354 * Returns: 1 - if ext in strvec 1880911106dfSjm199354 * 0 - otherwise 1881911106dfSjm199354 */ 1882911106dfSjm199354 static int 1883911106dfSjm199354 vs_icap_check_ext(char *ext, iovec_t *vec) 1884911106dfSjm199354 { 1885911106dfSjm199354 char *p, *end = (char *)vec + vec->iov_len; 1886911106dfSjm199354 1887911106dfSjm199354 for (p = vec->iov_base; p < end; p += strlen(p) + 1) { 1888911106dfSjm199354 if (MATCH(ext, p)) 1889911106dfSjm199354 return (1); 1890911106dfSjm199354 } 1891911106dfSjm199354 1892911106dfSjm199354 return (0); 1893911106dfSjm199354 } 1894911106dfSjm199354 1895911106dfSjm199354 1896911106dfSjm199354 /* 1897911106dfSjm199354 * vs_icap_resp_str 1898911106dfSjm199354 */ 1899911106dfSjm199354 static char * 1900911106dfSjm199354 vs_icap_resp_str(int rc) 1901911106dfSjm199354 { 1902911106dfSjm199354 vs_resp_msg_t *p = icap_resp; 1903911106dfSjm199354 1904911106dfSjm199354 if (rc < 0) 1905911106dfSjm199354 rc = -rc; 1906911106dfSjm199354 1907911106dfSjm199354 while (p->vsm_rc != VS_RESP_UNKNOWN) { 1908911106dfSjm199354 if (p->vsm_rc == rc) 1909911106dfSjm199354 break; 1910911106dfSjm199354 p++; 1911911106dfSjm199354 } 1912911106dfSjm199354 1913911106dfSjm199354 return (p->vsm_msg); 1914911106dfSjm199354 } 1915911106dfSjm199354 1916911106dfSjm199354 1917911106dfSjm199354 /* 1918911106dfSjm199354 * vs_icap_trimspace 1919911106dfSjm199354 * 1920911106dfSjm199354 * Trims whitespace from both the beginning and end of a string. This 1921911106dfSjm199354 * function alters the string buffer in-place. 1922911106dfSjm199354 * 1923911106dfSjm199354 * Whitespaces found at the beginning of the string are eliminated by 1924911106dfSjm199354 * moving forward the start of the string at the first non-whitespace 1925911106dfSjm199354 * character. 1926911106dfSjm199354 * Whitespace found at the end of the string are overwritten with nulls. 1927911106dfSjm199354 * 1928911106dfSjm199354 */ 1929911106dfSjm199354 static void 1930911106dfSjm199354 vs_icap_trimspace(char *buf) 1931911106dfSjm199354 { 1932911106dfSjm199354 char *p = buf; 1933911106dfSjm199354 char *q = buf; 1934911106dfSjm199354 1935911106dfSjm199354 if (buf == 0) 1936911106dfSjm199354 return; 1937911106dfSjm199354 1938911106dfSjm199354 while (*p && isspace(*p)) 1939911106dfSjm199354 ++p; 1940911106dfSjm199354 1941911106dfSjm199354 while ((*q = *p++) != 0) 1942911106dfSjm199354 ++q; 1943911106dfSjm199354 1944911106dfSjm199354 if (q != buf) { 1945911106dfSjm199354 while ((--q, isspace(*q)) != 0) 1946911106dfSjm199354 *q = '\0'; 1947911106dfSjm199354 } 1948911106dfSjm199354 } 1949911106dfSjm199354 1950911106dfSjm199354 1951911106dfSjm199354 /* 1952911106dfSjm199354 * vs_icap_uri_encode 1953911106dfSjm199354 * 1954911106dfSjm199354 * Encode uri data (eg filename) in accordance with RFC 2396 1955911106dfSjm199354 * 'Illegal' characters should be replaced with %hh, where hh is 1956911106dfSjm199354 * the hex value of the character. For example a space would be 1957911106dfSjm199354 * replaced with %20. 1958911106dfSjm199354 * Filenames are all already UTF-8 encoded. Any UTF-8 octects that 1959911106dfSjm199354 * are 'illegal' characters will be encoded as described above. 1960911106dfSjm199354 * 1961911106dfSjm199354 * Paramaters: data - string to be encoded (NULL terminated) 1962911106dfSjm199354 * buf - output buffer (NULL terminated) 1963911106dfSjm199354 * size - size of output buffer 1964911106dfSjm199354 * 1965911106dfSjm199354 * Returns: strlen of encoded data on success 1966911106dfSjm199354 * -1 size on error (contents of buf undefined) 1967911106dfSjm199354 */ 1968911106dfSjm199354 static int 1969911106dfSjm199354 vs_icap_uri_encode(char *buf, int size, char *data) 1970911106dfSjm199354 { 1971911106dfSjm199354 unsigned char *iptr; 1972911106dfSjm199354 char *optr = buf; 1973911106dfSjm199354 int len = strlen(data); 1974911106dfSjm199354 1975911106dfSjm199354 /* modify the data */ 1976911106dfSjm199354 for (iptr = (unsigned char *)data; *iptr; iptr++) { 1977911106dfSjm199354 if (vs_icap_uri_illegal_char(*iptr)) { 1978911106dfSjm199354 if ((len += 2) >= size) 1979911106dfSjm199354 return (-1); 1980911106dfSjm199354 (void) sprintf(optr, "%%%0x", *iptr); 1981911106dfSjm199354 optr += 3; 1982911106dfSjm199354 } else { 1983911106dfSjm199354 if (len >= size) 1984911106dfSjm199354 return (-1); 1985911106dfSjm199354 *optr++ = *iptr; 1986911106dfSjm199354 } 1987911106dfSjm199354 } 1988911106dfSjm199354 1989911106dfSjm199354 *optr = '\0'; 1990911106dfSjm199354 return (len); 1991911106dfSjm199354 } 1992911106dfSjm199354 1993911106dfSjm199354 1994911106dfSjm199354 /* 1995911106dfSjm199354 * vs_icap_uri_illegal_char 1996911106dfSjm199354 * 1997911106dfSjm199354 * The following us-ascii characters (UTF-8 octets) are 'illegal': 1998911106dfSjm199354 * < > # % " { } | \ ^ [ ] ` space, 0x01 -> 0x1F & 0x7F 1999911106dfSjm199354 * All non us-ascii UTF-8 octets ( >= 0x80) are illegal. 2000911106dfSjm199354 * 2001911106dfSjm199354 * Returns: 1 if character is not allowed in a URI 2002911106dfSjm199354 * 0 otherwise 2003911106dfSjm199354 */ 2004911106dfSjm199354 static int 2005911106dfSjm199354 vs_icap_uri_illegal_char(char c) 2006911106dfSjm199354 { 2007911106dfSjm199354 static const char *uri_illegal_chars = "<>#%\" {}|\\^[]`"; 2008911106dfSjm199354 2009911106dfSjm199354 /* us-ascii non printable characters or non us-ascii */ 2010911106dfSjm199354 if ((c <= 0x1F) || (c >= 0x7F)) 2011911106dfSjm199354 return (1); 2012911106dfSjm199354 2013911106dfSjm199354 /* us-ascii dis-allowed characters */ 2014911106dfSjm199354 if (strchr(uri_illegal_chars, c)) 2015911106dfSjm199354 return (1); 2016911106dfSjm199354 2017911106dfSjm199354 return (0); 2018911106dfSjm199354 2019911106dfSjm199354 } 2020