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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Description: Module contains supporting functions used by functions 28 * defined in vs_svc.c. It also contains some internal(static) functions. 29 */ 30 31 #include <stdarg.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <errno.h> 36 #include <time.h> 37 #include <fcntl.h> 38 #include <syslog.h> 39 #include <ctype.h> 40 #include <strings.h> 41 #include <string.h> 42 #include <limits.h> 43 #include <pthread.h> 44 #include <sys/types.h> 45 #include <sys/socket.h> 46 #include <sys/debug.h> 47 #include <netinet/in.h> 48 #include <arpa/inet.h> 49 50 #include "vs_incl.h" 51 #include "vs_icap.h" 52 53 /* prototypes of local functions */ 54 static int vs_icap_option_request(vs_scan_ctx_t *); 55 static int vs_icap_send_option_req(vs_scan_ctx_t *); 56 static int vs_icap_read_option_resp(vs_scan_ctx_t *); 57 58 static int vs_icap_respmod_request(vs_scan_ctx_t *); 59 static int vs_icap_may_preview(vs_scan_ctx_t *); 60 static char *vs_icap_find_ext(char *); 61 static int vs_icap_send_preview(vs_scan_ctx_t *); 62 static int vs_icap_send_respmod_hdr(vs_scan_ctx_t *, int); 63 static int vs_icap_create_respmod_hdr(vs_scan_ctx_t *, int); 64 static int vs_icap_uri_encode(char *, int, char *); 65 static int vs_icap_uri_illegal_char(char); 66 67 static int vs_icap_read_respmod_resp(vs_scan_ctx_t *); 68 static int vs_icap_read_resp_code(vs_scan_ctx_t *); 69 static int vs_icap_read_hdr(vs_scan_ctx_t *, vs_hdr_t *, int); 70 71 static int vs_icap_set_scan_result(vs_scan_ctx_t *); 72 static int vs_icap_read_encap_hdr(vs_scan_ctx_t *); 73 static void vs_icap_read_encap_data(vs_scan_ctx_t *); 74 static int vs_icap_create_repair_file(vs_scan_ctx_t *); 75 static int vs_icap_read_resp_body(vs_scan_ctx_t *); 76 static int vs_icap_read_body_chunk(vs_scan_ctx_t *); 77 78 static int vs_icap_send_chunk(vs_scan_ctx_t *, int); 79 static int vs_icap_send_termination(vs_scan_ctx_t *); 80 static int vs_icap_readline(vs_scan_ctx_t *, char *, int); 81 82 static int vs_icap_write(int, char *, int); 83 static int vs_icap_read(int, char *, int); 84 85 /* process options and respmod headers */ 86 static void vs_icap_parse_hdrs(char, char *, char **, char **); 87 static int vs_icap_opt_value(vs_scan_ctx_t *, int, char *); 88 static int vs_icap_opt_ext(vs_scan_ctx_t *, int, char *); 89 static int vs_icap_resp_violations(vs_scan_ctx_t *, int, char *); 90 static int vs_icap_resp_violation_rec(vs_scan_ctx_t *, int); 91 static int vs_icap_resp_infection(vs_scan_ctx_t *, int, char *); 92 static int vs_icap_resp_virus_id(vs_scan_ctx_t *, int, char *); 93 static int vs_icap_resp_encap(vs_scan_ctx_t *, int, char *); 94 static int vs_icap_resp_istag(vs_scan_ctx_t *, int, char *); 95 static void vs_icap_istag_to_scanstamp(char *, vs_scanstamp_t); 96 97 /* Utility functions for handling OPTIONS data: vs_options_t */ 98 static void vs_icap_free_options(vs_options_t *); 99 static void vs_icap_copy_options(vs_options_t *, vs_options_t *); 100 static void vs_icap_update_options(vs_scan_ctx_t *); 101 static int vs_icap_compare_se(int, char *, int); 102 103 static iovec_t *vs_icap_make_strvec(char *, const char *); 104 static iovec_t *vs_icap_copy_strvec(iovec_t *); 105 static int vs_icap_check_ext(char *, iovec_t *); 106 static void vs_icap_trimspace(char *); 107 108 /* icap response message */ 109 static char *vs_icap_resp_str(int); 110 111 /* 112 * local variables 113 */ 114 115 /* option headers - and handler functions */ 116 vs_hdr_t option_hdrs[] = { 117 { VS_OPT_SERVICE, "Service", vs_icap_opt_value}, 118 { VS_OPT_ISTAG, "ISTag", vs_icap_opt_value}, 119 { VS_OPT_METHODS, "Methods", vs_icap_opt_value}, 120 { VS_OPT_ALLOW, "Allow", vs_icap_opt_value}, 121 { VS_OPT_PREVIEW, "Preview", vs_icap_opt_value}, 122 { VS_OPT_XFER_PREVIEW, "Transfer-Preview", vs_icap_opt_ext}, 123 { VS_OPT_XFER_COMPLETE, "Transfer-Complete", vs_icap_opt_ext}, 124 { VS_OPT_MAX_CONNECTIONS, "Max-Connections", vs_icap_opt_value}, 125 { VS_OPT_TTL, "Options-TTL", vs_icap_opt_value}, 126 { VS_OPT_X_DEF_INFO, "X-Definition-Info", vs_icap_opt_value} 127 }; 128 129 130 /* resp hdrs - and handler functions */ 131 vs_hdr_t resp_hdrs[] = { 132 { VS_RESP_ENCAPSULATED, "Encapsulated", vs_icap_resp_encap}, 133 { VS_RESP_ISTAG, "ISTag", vs_icap_resp_istag}, 134 { VS_RESP_X_VIRUS_ID, "X-Virus-ID", vs_icap_resp_virus_id}, 135 { VS_RESP_X_INFECTION, "X-Infection-Found", vs_icap_resp_infection}, 136 { VS_RESP_X_VIOLATIONS, "X-Violations-Found", vs_icap_resp_violations} 137 }; 138 139 /* ICAP response code to string mappings */ 140 vs_resp_msg_t icap_resp[] = { 141 { VS_RESP_CONTINUE, "Continue"}, 142 { VS_RESP_OK, "OK"}, 143 { VS_RESP_CREATED, "Virus Detected and Repaired"}, 144 { VS_RESP_NO_CONT_NEEDED, "No Content Necessary"}, 145 { VS_RESP_BAD_REQ, "Bad Request"}, 146 { VS_RESP_FORBIDDEN, "File Infected and not repaired"}, 147 { VS_RESP_NOT_FOUND, "URI not found"}, 148 { VS_RESP_NOT_ALLOWED, "Method not allowed"}, 149 { VS_RESP_TIMEOUT, "Request timedout"}, 150 { VS_RESP_INTERNAL_ERR, "Internal server error"}, 151 { VS_RESP_NOT_IMPL, "Method not implemented"}, 152 { VS_RESP_SERV_UNAVAIL, "Service unavailable/overloaded"}, 153 { VS_RESP_ICAP_VER_UNSUPP, "ICAP version not supported"}, 154 { VS_RESP_SCAN_ERR, "Error scanning file"}, 155 { VS_RESP_NO_LICENSE, "No AV License"}, 156 { VS_RESP_RES_UNAVAIL, "Resource unavailable"}, 157 { VS_RESP_UNKNOWN, "Unknown Error"}, 158 }; 159 160 static const char *EXT_SEPARATOR = ","; 161 static vs_options_t vs_options[VS_SE_MAX]; 162 static pthread_mutex_t vs_opt_mutex = PTHREAD_MUTEX_INITIALIZER; 163 164 /* 165 * vs_icap_init 166 * initialization performed when daemon is loaded 167 */ 168 void 169 vs_icap_init() 170 { 171 172 (void) pthread_mutex_lock(&vs_opt_mutex); 173 (void) memset(vs_options, 0, sizeof (vs_options_t)); 174 (void) pthread_mutex_unlock(&vs_opt_mutex); 175 } 176 177 178 /* 179 * vs_icap_fini 180 * cleanup performed when daemon is unloaded 181 */ 182 void 183 vs_icap_fini() 184 { 185 int i; 186 187 (void) pthread_mutex_lock(&vs_opt_mutex); 188 189 for (i = 0; i < VS_SE_MAX; i++) 190 vs_icap_free_options(&vs_options[i]); 191 192 (void) pthread_mutex_unlock(&vs_opt_mutex); 193 } 194 195 196 /* 197 * vs_icap_config 198 * 199 * When a new VSCAN configuration is specified, this will be 200 * called per scan engine. If the scan engine host or port has 201 * changed delete the vs_options entry for that scan engine. 202 */ 203 void 204 vs_icap_config(int idx, char *host, int port) 205 { 206 (void) pthread_mutex_lock(&vs_opt_mutex); 207 if (vs_icap_compare_se(idx, host, port) != 0) { 208 vs_icap_free_options(&vs_options[idx]); 209 (void) strlcpy(vs_options[idx].vso_host, host, 210 sizeof (vs_options[idx].vso_host)); 211 vs_options[idx].vso_port = port; 212 } 213 (void) pthread_mutex_unlock(&vs_opt_mutex); 214 } 215 216 217 /* 218 * vs_icap_scan_file 219 * 220 * Create a context (vs_scan_ctx_t) for the scan operation and initialize 221 * its options info. If the scan engine connection's IP or port is different 222 * from that held in vs_options the vs_options info is old and should 223 * be deleted (vs_icap_free_options). Otherwise, copy the vs_options info 224 * into the context. 225 * file name, size and decsriptor are also copied into the context 226 * 227 * Handle the ICAP protocol communication with the external Scan Engine to 228 * perform the scan 229 * - send an OPTIONS request if necessary 230 * - send RESPMOD scan request 231 * - process the response and save any cleaned data to file 232 * 233 * Returns: result->vsr_rc 234 */ 235 int 236 vs_icap_scan_file(vs_eng_ctx_t *eng, char *devname, char *fname, 237 uint64_t fsize, int flags, vs_result_t *result) 238 { 239 vs_scan_ctx_t ctx; 240 int fd; 241 242 fd = open(devname, O_RDONLY); 243 244 /* retry once on ENOENT as /dev link may not be created yet */ 245 if ((fd == -1) && (errno == ENOENT)) { 246 (void) sleep(1); 247 fd = open(devname, O_RDONLY); 248 } 249 250 if (fd == -1) { 251 syslog(LOG_ERR, "Failed to open device %s - %s", 252 devname, strerror(errno)); 253 result->vsr_rc = VS_RESULT_ERROR; 254 return (result->vsr_rc); 255 } 256 257 /* initialize context */ 258 (void) memset(&ctx, 0, sizeof (vs_scan_ctx_t)); 259 ctx.vsc_idx = eng->vse_eidx; 260 (void) strlcpy(ctx.vsc_host, eng->vse_host, sizeof (ctx.vsc_host)); 261 ctx.vsc_port = eng->vse_port; 262 ctx.vsc_sockfd = eng->vse_sockfd; 263 ctx.vsc_fd = fd; 264 ctx.vsc_fname = fname; 265 ctx.vsc_fsize = fsize; 266 ctx.vsc_flags = flags; 267 ctx.vsc_result = result; 268 269 /* Hooks for future saving of repaired data, not yet in use */ 270 ctx.vsc_flags |= VS_NO_REPAIR; 271 ctx.vsc_repair = 0; 272 ctx.vsc_repair_fname = NULL; 273 ctx.vsc_repair_fd = -1; 274 275 /* take a copy of vs_options[idx] if they match the SE specified */ 276 (void) pthread_mutex_lock(&vs_opt_mutex); 277 if (vs_icap_compare_se(ctx.vsc_idx, ctx.vsc_host, ctx.vsc_port) == 0) { 278 vs_icap_copy_options(&ctx.vsc_options, 279 &vs_options[ctx.vsc_idx]); 280 } 281 282 (void) pthread_mutex_unlock(&vs_opt_mutex); 283 284 /* 285 * default the result to scan engine error. 286 * Any non scan-engine errors will reset it to VS_RESULT_ERROR 287 */ 288 result->vsr_rc = VS_RESULT_SE_ERROR; 289 290 /* do the scan */ 291 if (vs_icap_option_request(&ctx) == 0) 292 (void) vs_icap_respmod_request(&ctx); 293 294 (void) close(fd); 295 vs_icap_free_options(&ctx.vsc_options); 296 return (result->vsr_rc); 297 } 298 299 300 /* ********************************************************************* */ 301 /* Local Function definitions */ 302 /* ********************************************************************* */ 303 304 /* 305 * vs_icap_option_request 306 * 307 * Send ICAP options message and await/process the response. 308 * 309 * The ICAP options request needs to be sent when a connection 310 * is first made with the scan engine. Unless the scan engine 311 * determines that the options will never expire (which we save 312 * as optione_req_time == -1) the request should be resent after 313 * the expiry time specified by the icap server. 314 * 315 * Returns: 0 - success 316 * -1 - error 317 */ 318 static int 319 vs_icap_option_request(vs_scan_ctx_t *ctx) 320 { 321 if (ctx->vsc_options.vso_req_time != -1 && 322 ((time(0) - ctx->vsc_options.vso_req_time) > 323 ctx->vsc_options.vso_ttl)) { 324 325 if (vs_icap_send_option_req(ctx) < 0) 326 return (-1); 327 328 if (vs_icap_read_option_resp(ctx) < 0) 329 return (-1); 330 331 vs_icap_update_options(ctx); 332 } 333 334 return (0); 335 } 336 337 338 /* 339 * vs_icap_send_option_req 340 * 341 * Send an OPTIONS request to the scan engine 342 * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME) 343 * after the IP address, otherwise it closes the connection. 344 * 345 * Returns: 0 - success 346 * -1 - error 347 */ 348 static int 349 vs_icap_send_option_req(vs_scan_ctx_t *ctx) 350 { 351 char my_host_name[MAXHOSTNAMELEN]; 352 int bufsp = VS_BUF_SZ; 353 char *buf0 = ctx->vsc_info.vsi_send_buf; 354 char *bufp = buf0; 355 int tlen; 356 357 if (gethostname(my_host_name, sizeof (my_host_name)) != 0) { 358 /* non SE error */ 359 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR; 360 return (-1); 361 } 362 363 (void) memset(ctx->vsc_info.vsi_send_buf, 0, 364 sizeof (ctx->vsc_info.vsi_send_buf)); 365 366 tlen = snprintf(bufp, bufsp, "OPTIONS icap://%s:%d/%s %s\r\n", 367 ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER); 368 bufp += tlen; 369 bufsp -= tlen; 370 371 tlen = snprintf(bufp, bufsp, "Host: %s\r\n\r\n", my_host_name); 372 bufp += tlen; 373 374 if (vs_icap_write(ctx->vsc_sockfd, buf0, (bufp - buf0)) < 0) 375 return (-1); 376 377 return (0); 378 } 379 380 381 /* 382 * vs_icap_read_option_resp 383 * 384 * Returns: 0 - success 385 * -1 - error 386 */ 387 static int 388 vs_icap_read_option_resp(vs_scan_ctx_t *ctx) 389 { 390 if (vs_icap_read_resp_code(ctx) < 0) 391 return (-1); 392 393 if (ctx->vsc_info.vsi_icap_rc != VS_RESP_OK) { 394 syslog(LOG_ERR, "ICAP protocol error " 395 "- unexpected option response: %s", 396 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc)); 397 return (-1); 398 } 399 400 if (vs_icap_read_hdr(ctx, option_hdrs, VS_OPT_HDR_MAX) != 0) 401 return (-1); 402 403 if ((ctx->vsc_options.vso_scanstamp[0] == 0) || 404 (ctx->vsc_options.vso_respmod == 0) || 405 (ctx->vsc_options.vso_req_time == 0)) { 406 syslog(LOG_ERR, "ICAP protocol error " 407 "- missing or invalid option response hdrs"); 408 return (-1); 409 } 410 411 return (0); 412 } 413 414 415 /* 416 * vs_icap_respmod_request 417 * 418 * Send respmod request and receive and process ICAP response. 419 * Preview: 420 * ICAP allows for an optional "preview" request. In the option negotiation, 421 * the server may ask for a list of types to be previewed, or to be sent 422 * complete (no preview). 423 * This is advisory. It is ok to skip the preview step, as done when the file 424 * is smaller than the preview_len. 425 * Process Response: 426 * - read and parse the RESPMOD response headers 427 * - populate the result structure 428 * - read any encapsulated response headers 429 * - read any encapsulated response body and, if it represents cleaned 430 * file data, overwrite the file with it 431 * 432 * Returns: 0 - success 433 * -1 - error 434 */ 435 static int 436 vs_icap_respmod_request(vs_scan_ctx_t *ctx) 437 { 438 int rv; 439 int bytes_sent, send_len; 440 uint64_t resid = ctx->vsc_fsize; 441 442 if (vs_icap_may_preview(ctx)) { 443 444 if ((rv = vs_icap_send_preview(ctx)) < 0) 445 return (-1); 446 447 if (vs_icap_read_respmod_resp(ctx) < 0) 448 return (-1); 449 450 if (ctx->vsc_info.vsi_icap_rc != VS_RESP_CONTINUE) 451 return (0); 452 453 bytes_sent = rv; 454 455 /* If > block (VS_BUF_SZ) remains, re-align to block boundary */ 456 if ((ctx->vsc_fsize - (uint64_t)bytes_sent) > VS_BUF_SZ) { 457 send_len = VS_BUF_SZ - bytes_sent; 458 if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0) 459 return (-1); 460 bytes_sent += rv; 461 } 462 463 resid -= (uint64_t)bytes_sent; 464 465 } else { 466 467 if (vs_icap_send_respmod_hdr(ctx, 0) < 0) 468 return (-1); 469 } 470 471 /* Send the remainder of the file... */ 472 while (resid) { 473 send_len = (resid > VS_BUF_SZ) ? VS_BUF_SZ : resid; 474 475 if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0) 476 return (-1); 477 478 if (rv == 0) 479 break; 480 481 resid -= (uint64_t)rv; 482 } 483 484 if (vs_icap_send_termination(ctx) < 0) 485 return (-1); 486 487 /* sending of ICAP request complete */ 488 if (vs_icap_read_respmod_resp(ctx) < 0) 489 return (-1); 490 491 return (0); 492 } 493 494 495 /* 496 * vs_icap_may_preview 497 * 498 * Returns: 1 - preview 499 * 0 - don't preview 500 */ 501 static int 502 vs_icap_may_preview(vs_scan_ctx_t *ctx) 503 { 504 int in_list = 0; 505 char *ext; 506 vs_options_t *opts = &ctx->vsc_options; 507 508 if (opts->vso_xfer_how == VS_PREVIEW_NONE) 509 return (0); 510 511 /* if the file is smaller than the preview size, don't preview */ 512 if (ctx->vsc_fsize < (uint64_t)ctx->vsc_options.vso_preview_len) 513 return (0); 514 515 switch (opts->vso_xfer_how) { 516 case VS_PREVIEW_ALL: 517 return (1); 518 case VS_PREVIEW_EXCEPT: 519 /* Preview everything except types in xfer_complete */ 520 if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0) 521 in_list = vs_icap_check_ext(ext, 522 opts->vso_xfer_complete); 523 return ((in_list) ? 0 : 1); 524 case VS_PREVIEW_LIST: 525 /* Preview only types in the the xfer_preview list */ 526 if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0) 527 in_list = vs_icap_check_ext(ext, 528 opts->vso_xfer_preview); 529 return ((in_list) ? 1 : 0); 530 } 531 532 return (1); 533 } 534 535 536 /* 537 * vs_icap_find_ext 538 * 539 * Returns: ptr to file's extension in fname 540 * 0 if no extension 541 */ 542 static char * 543 vs_icap_find_ext(char *fname) 544 { 545 char *last_comp, *ext_str = 0; 546 547 if ((last_comp = strrchr(fname, '/')) != 0) { 548 last_comp++; 549 } else { 550 last_comp = fname; 551 } 552 553 /* Get file extension */ 554 if ((ext_str = strrchr(last_comp, '.')) != 0) { 555 ext_str++; 556 if (strlen(ext_str) == 0) 557 ext_str = 0; 558 } 559 560 return (ext_str); 561 } 562 563 564 /* 565 * vs_icap_send_preview 566 * 567 * Returns: bytes sent (preview + alignment) 568 * -1 - error 569 */ 570 static int 571 vs_icap_send_preview(vs_scan_ctx_t *ctx) 572 { 573 int preview_len = ctx->vsc_options.vso_preview_len; 574 int bytes_sent; 575 576 /* Send a RESPMOD request with "preview" mode. */ 577 if (vs_icap_send_respmod_hdr(ctx, 'P') < 0) 578 return (-1); 579 580 if ((bytes_sent = vs_icap_send_chunk(ctx, preview_len)) < 0) 581 return (-1); 582 583 if (bytes_sent < preview_len) 584 return (-1); 585 586 if (vs_icap_send_termination(ctx) < 0) 587 return (-1); 588 589 return (bytes_sent); 590 } 591 592 593 /* 594 * vs_icap_send_respmod_hdr 595 * 596 * Create and send the RESPMOD request headers to the scan engine. 597 * 598 * Returns: 0 success 599 * < 0 error 600 */ 601 static int 602 vs_icap_send_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview) 603 { 604 int len; 605 606 if ((len = vs_icap_create_respmod_hdr(ctx, ispreview)) == -1) { 607 /* non SE error */ 608 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR; 609 return (-1); 610 } 611 612 /* send the headers */ 613 if (vs_icap_write(ctx->vsc_sockfd, 614 ctx->vsc_info.vsi_send_buf, len) < 0) { 615 return (-1); 616 } 617 618 return (0); 619 } 620 621 622 /* 623 * vs_icap_create_respmod_hdr 624 * 625 * Create the RESPMOD request headers. 626 * - RESPMOD, Host, Allow, [Preview], Encapsulated, encapsulated request hdr, 627 * encapsulated response hdr 628 * Encapsulated data is sent separately subsequent to vs_icap_send_respmod_hdr, 629 * via calls to vs_icap_send_chunk. 630 * 631 * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME) 632 * after the IP address, otherwise it closes the connection. 633 * 634 * Returns: -1 error 635 * length of headers data 636 */ 637 static int 638 vs_icap_create_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview) 639 { 640 char my_host_name[MAXHOSTNAMELEN]; 641 int hbufsp = VS_BUF_SZ; 642 char *hbuf0 = ctx->vsc_info.vsi_send_buf; 643 char *hbufp = hbuf0; 644 char *encap_hdr, *encap_off0, *req_hdr, *res_hdr, *res_body; 645 int preview_len = ctx->vsc_options.vso_preview_len; 646 int tlen; 647 648 if (gethostname(my_host_name, sizeof (my_host_name)) != 0) { 649 /* non SE error */ 650 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR; 651 return (-1); 652 } 653 654 (void) memset(hbufp, 0, hbufsp); 655 656 /* First the ICAP "request" part. (at offset 0) */ 657 tlen = snprintf(hbufp, hbufsp, "RESPMOD icap://%s:%d/%s %s\r\n", 658 ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER); 659 if (tlen >= hbufsp) 660 return (-1); 661 hbufp += tlen; hbufsp -= tlen; 662 663 tlen = snprintf(hbufp, hbufsp, "Host: %s\r\n", my_host_name); 664 if (tlen >= hbufsp) 665 return (-1); 666 hbufp += tlen; hbufsp -= tlen; 667 668 tlen = snprintf(hbufp, hbufsp, "Allow: 204\r\n"); 669 if (tlen >= hbufsp) 670 return (-1); 671 hbufp += tlen; hbufsp -= tlen; 672 673 if (ispreview) { 674 tlen = snprintf(hbufp, hbufsp, "Preview: %d\r\n", preview_len); 675 if (tlen >= hbufsp) 676 return (-1); 677 hbufp += tlen; hbufsp -= tlen; 678 } 679 680 /* Reserve space to later insert encapsulation offsets, & blank line */ 681 encap_hdr = hbufp; 682 tlen = snprintf(hbufp, hbufsp, "%*.*s\r\n\r\n", 683 VS_ENCAP_SZ, VS_ENCAP_SZ, ""); 684 if (tlen >= hbufsp) 685 return (-1); 686 hbufp += tlen; hbufsp -= tlen; 687 688 /* "offset zero" for the encapsulated parts that follow */ 689 encap_off0 = hbufp; 690 691 /* Encapsulated request header (req_hdr) & blank line */ 692 req_hdr = hbufp; 693 tlen = snprintf(hbufp, hbufsp, "GET http://%s", my_host_name); 694 if (tlen >= hbufsp) 695 return (-1); 696 hbufp += tlen; hbufsp -= tlen; 697 698 tlen = vs_icap_uri_encode(hbufp, hbufsp, ctx->vsc_fname); 699 if (tlen < 0) 700 return (-1); 701 hbufp += tlen; hbufsp -= tlen; 702 703 tlen = snprintf(hbufp, hbufsp, " HTTP/1.1\r\n\r\n"); 704 if (tlen >= hbufsp) 705 return (-1); 706 hbufp += tlen; hbufsp -= tlen; 707 708 /* Encapsulated response header (res_hdr) & blank line */ 709 res_hdr = hbufp; 710 tlen = snprintf(hbufp, hbufsp, "HTTP/1.1 200 OK\r\n"); 711 if (tlen >= hbufsp) 712 return (-1); 713 hbufp += tlen; hbufsp -= tlen; 714 715 tlen = snprintf(hbufp, hbufsp, "Transfer-Encoding: chunked\r\n\r\n"); 716 if (tlen >= hbufsp) 717 return (-1); 718 hbufp += tlen; hbufsp -= tlen; 719 720 /* response body section - res-body ("chunked data") */ 721 res_body = hbufp; 722 723 /* Insert offsets in encap_hdr */ 724 tlen = snprintf(encap_hdr, VS_ENCAP_SZ, "Encapsulated: " 725 "req-hdr=%d, res-hdr=%d, res-body=%d", 726 req_hdr - encap_off0, res_hdr - encap_off0, res_body - encap_off0); 727 /* undo the null from snprintf */ 728 encap_hdr[tlen] = ' '; 729 730 /* return length */ 731 return (hbufp - hbuf0); 732 } 733 734 735 /* 736 * vs_icap_read_respmod_resp 737 * 738 * Used for both preview and final RESMOD response 739 */ 740 static int 741 vs_icap_read_respmod_resp(vs_scan_ctx_t *ctx) 742 { 743 if (vs_icap_read_resp_code(ctx) < 0) 744 return (-1); 745 746 if (vs_icap_read_hdr(ctx, resp_hdrs, VS_RESP_HDR_MAX) < 0) 747 return (-1); 748 749 if (ctx->vsc_info.vsi_icap_rc == VS_RESP_CONTINUE) { 750 /* A VS_RESP_CONTINUE should not have encapsulated data */ 751 if ((ctx->vsc_info.vsi_res_hdr) || 752 (ctx->vsc_info.vsi_res_body)) { 753 syslog(LOG_ERR, "ICAP protocol error -" 754 "- encapsulated data in Continue response"); 755 return (-1); 756 } 757 } else { 758 if (vs_icap_set_scan_result(ctx) < 0) 759 return (-1); 760 761 if (ctx->vsc_info.vsi_res_hdr) { 762 if (vs_icap_read_encap_hdr(ctx) < 0) 763 return (-1); 764 } 765 766 if (ctx->vsc_info.vsi_res_body) 767 vs_icap_read_encap_data(ctx); 768 else if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED) 769 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN; 770 } 771 772 return (0); 773 } 774 775 776 /* 777 * vs_icap_read_resp_code 778 * 779 * Get the response code from the icap response messages 780 */ 781 static int 782 vs_icap_read_resp_code(vs_scan_ctx_t *ctx) 783 { 784 char *buf = ctx->vsc_info.vsi_recv_buf; 785 int retval; 786 787 /* Break on error or non-blank line. */ 788 for (;;) { 789 (void) memset(buf, '\0', VS_BUF_SZ); 790 791 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 792 return (-1); 793 794 if (retval && buf[0]) { 795 if (MATCH(buf, VS_ICAP_VER)) { 796 (void) sscanf(buf+8, "%d", 797 &ctx->vsc_info.vsi_icap_rc); 798 return (0); 799 } 800 801 syslog(LOG_ERR, "ICAP protocol error -" 802 "- expected ICAP/1.0, received %s", buf); 803 804 return (-1); 805 } 806 } 807 } 808 809 810 /* 811 * vs_icap_read_hdr 812 * 813 * Reads all response headers. 814 * As each line is read it is parsed and passed to the appropriate handler. 815 * 816 * Returns: 0 - success 817 * -1 - error 818 */ 819 static int 820 vs_icap_read_hdr(vs_scan_ctx_t *ctx, vs_hdr_t hdrs[], int num_hdrs) 821 { 822 char *buf = ctx->vsc_info.vsi_recv_buf; 823 int i, retval; 824 char *name, *val; 825 826 /* Break on error or blank line. */ 827 for (;;) { 828 (void) memset(buf, '\0', VS_BUF_SZ); 829 830 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 831 return (-1); 832 833 /* Empty line (CR/LF) normal break */ 834 if ((retval == 0) || (!buf[0])) 835 break; 836 837 vs_icap_parse_hdrs(':', buf, &name, &val); 838 839 for (i = 0; i < num_hdrs; i++) { 840 if (strcmp(name, hdrs[i].vsh_name) == 0) { 841 hdrs[i].vsh_func(ctx, hdrs[i].vsh_id, val); 842 break; 843 } 844 } 845 } 846 847 return ((retval >= 0) ? 0 : -1); 848 } 849 850 851 /* 852 * vs_icap_set_scan_result 853 * 854 * Sets the vs_result_t vsr_rc from the icap_resp_code and 855 * any violation information in vs_result_t 856 * 857 * Returns: 0 - success 858 * -1 - error 859 */ 860 static int 861 vs_icap_set_scan_result(vs_scan_ctx_t *ctx) 862 { 863 int i; 864 vs_result_t *result = ctx->vsc_result; 865 866 (void) strlcpy(result->vsr_scanstamp, 867 ctx->vsc_options.vso_scanstamp, sizeof (vs_scanstamp_t)); 868 869 switch (ctx->vsc_info.vsi_icap_rc) { 870 case VS_RESP_NO_CONT_NEEDED: 871 result->vsr_rc = VS_RESULT_CLEAN; 872 break; 873 874 case VS_RESP_OK: 875 /* if we have no violations , that means all ok */ 876 if (result->vsr_nviolations == 0) { 877 result->vsr_rc = VS_RESULT_CLEAN; 878 break; 879 } 880 881 /* Any infections not repaired? */ 882 result->vsr_rc = VS_RESULT_CLEANED; 883 for (i = 0; i < result->vsr_nviolations; i++) { 884 if (result->vsr_vrec[i].vr_res != 885 VS_RES_FILE_REPAIRED) { 886 result->vsr_rc = VS_RESULT_FORBIDDEN; 887 break; 888 } 889 } 890 break; 891 892 case VS_RESP_CREATED : 893 /* file is repaired */ 894 result->vsr_rc = VS_RESULT_CLEANED; 895 break; 896 897 case VS_RESP_FORBIDDEN: 898 /* file is infected and could not be repaired */ 899 result->vsr_rc = VS_RESULT_FORBIDDEN; 900 break; 901 902 default: 903 syslog(LOG_ERR, "ICAP protocol error " 904 "- unsupported scan result: %s", 905 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc)); 906 return (-1); 907 } 908 909 return (0); 910 } 911 912 913 /* 914 * vs_icap_read_encap_hdr 915 * 916 * Read the encapsulated response header to determine the length of 917 * encapsulated data and, in some cases, to detect the infected state 918 * of the file. 919 * 920 * Use of http response code: 921 * Trend IWSS does not return virus information in the RESPMOD response 922 * headers unless the OPTIONAL "include X_Infection_Found" checkbox is 923 * checked and "disable_infected_url_block=yes" is set in intscan.ini. 924 * Thus if we haven't already detected the infected/cleaned status 925 * (ie if vsr_rc == VS_RESULT_CLEAN) we attempt to detect the 926 * infected/cleaned state of a file from a combination of the ICAP and 927 * http resp codes. 928 * Here are the response code values that Trend IWSS returns: 929 * - clean: icap resp = VS_RESP_NO_CONT_NEEDED 930 * - quarantine: icap resp = VS_RESP_OK, http resp = VS_RESP_FORBIDDEN 931 * - cleaned: icap resp = VS_RESP_OK, http resp = VS_RESP_OK 932 * For all other vendors' scan engines (so far) the infected/cleaned 933 * state of the file has already been detected from the RESPMOD 934 * response headers. 935 */ 936 static int 937 vs_icap_read_encap_hdr(vs_scan_ctx_t *ctx) 938 { 939 char *buf = ctx->vsc_info.vsi_recv_buf; 940 char *name, *value; 941 int retval; 942 943 /* Break on error or blank line. */ 944 for (;;) { 945 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 946 return (-1); 947 948 /* Empty line (CR/LF) normal break */ 949 if ((retval == 0) || (!buf[0])) 950 break; 951 952 if (MATCH(buf, "HTTP/1.1")) { 953 (void) sscanf(buf + 8, "%d", 954 &ctx->vsc_info.vsi_http_rc); 955 ctx->vsc_info.vsi_html_content = B_TRUE; 956 957 /* if not yet detected infection, interpret http_rc */ 958 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEAN) { 959 if ((ctx->vsc_info.vsi_icap_rc == VS_RESP_OK) && 960 (ctx->vsc_info.vsi_http_rc == VS_RESP_OK)) { 961 ctx->vsc_result->vsr_rc = 962 VS_RESULT_CLEANED; 963 } else { 964 ctx->vsc_result->vsr_rc = 965 VS_RESULT_FORBIDDEN; 966 } 967 } 968 } else { 969 vs_icap_parse_hdrs(':', buf, &name, &value); 970 if (name && (MATCH(name, "Content-Length"))) { 971 (void) sscanf(value, "%d", 972 &ctx->vsc_info.vsi_content_len); 973 } 974 } 975 } 976 977 return (0); 978 } 979 980 981 /* 982 * vs_icap_read_encap_data 983 * 984 * Read the encapsulated response data. 985 * 986 * If the response data represents cleaned file data (for an infected file) 987 * and VS_NO_REPAIR is not set, open repair file to save the reponse body 988 * data in. Set the repair flag in the scan context. The repair flag is used 989 * during the processing of the response data. If the flag is set then the 990 * data is written to file. If any error occurs which invalidates the repaired 991 * data file the repair flag gets reset to 0, and the data will be discarded. 992 * 993 * The result is reset to VS_RESULT_FORBIDDEN until all of the cleaned data 994 * has been successfully received and processed. It is then reset to 995 * VS_RESULT_CLEANED. 996 * 997 * If the data doesn't represent cleaned file data, or we cannot (or don't 998 * want to) write the cleaned data to file, the data is discarded (repair flag 999 * in ctx == 0). 1000 */ 1001 static void 1002 vs_icap_read_encap_data(vs_scan_ctx_t *ctx) 1003 { 1004 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED) { 1005 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN; 1006 1007 if (!(ctx->vsc_flags & VS_NO_REPAIR)) { 1008 if (vs_icap_create_repair_file(ctx) == 0) 1009 ctx->vsc_repair = B_TRUE; 1010 } 1011 } 1012 1013 /* 1014 * vs_icap_read_resp_body handles errors internally; 1015 * resets ctx->vsc_repair 1016 */ 1017 (void) vs_icap_read_resp_body(ctx); 1018 1019 if (ctx->vsc_repair_fd != -1) { 1020 (void) close(ctx->vsc_repair_fd); 1021 1022 if (ctx->vsc_repair) { 1023 /* repair file contains the cleaned data */ 1024 ctx->vsc_result->vsr_rc = VS_RESULT_CLEANED; 1025 } else { 1026 /* error occured processing data. Remove repair file */ 1027 (void) unlink(ctx->vsc_repair_fname); 1028 } 1029 } 1030 } 1031 1032 1033 /* 1034 * vs_icap_create_repair_file 1035 * 1036 * Create and open a file to save cleaned data in. 1037 */ 1038 static int 1039 vs_icap_create_repair_file(vs_scan_ctx_t *ctx) 1040 { 1041 if (ctx->vsc_repair_fname == NULL) 1042 return (-1); 1043 1044 if ((ctx->vsc_repair_fd = open(ctx->vsc_repair_fname, 1045 O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0644)) == -1) { 1046 return (-1); 1047 } 1048 1049 return (0); 1050 } 1051 1052 1053 /* 1054 * vs_icap_read_resp_body 1055 * 1056 * Repeatedly call vs_icap_read_body_chunk until it returns: 1057 * 0 indicating that there's no more data to read or 1058 * -1 indicating a read error -> reset ctx->vsc_repair 0 1059 * 1060 * Returns: 0 success 1061 * -1 error 1062 */ 1063 static int 1064 vs_icap_read_resp_body(vs_scan_ctx_t *ctx) 1065 { 1066 int retval; 1067 1068 while ((retval = vs_icap_read_body_chunk(ctx)) > 0) 1069 ; 1070 1071 if (retval < 0) 1072 ctx->vsc_repair = B_FALSE; 1073 1074 return (retval); 1075 } 1076 1077 1078 /* 1079 * vs_icap_read_body_chunk 1080 * 1081 * Read the chunk size, then read the chunk of data and write the 1082 * data to file repair_fd (or discard it). 1083 * If the data cannot be successfully written to file, set repair 1084 * flag in ctx to 0, and discard all subsequent data. 1085 * 1086 * Returns: chunk size 1087 * -1 on error 1088 */ 1089 static int 1090 vs_icap_read_body_chunk(vs_scan_ctx_t *ctx) 1091 { 1092 char *lbuf = ctx->vsc_info.vsi_recv_buf; 1093 unsigned int chunk_size, resid; 1094 int rsize; 1095 1096 /* Read and parse the chunk size. */ 1097 if ((vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0) || 1098 (!sscanf(lbuf, "%x", &chunk_size))) { 1099 return (-1); 1100 } 1101 1102 /* Read and save/discard chunk */ 1103 resid = chunk_size; 1104 while (resid) { 1105 rsize = (resid < VS_BUF_SZ) ? resid : VS_BUF_SZ; 1106 1107 if ((rsize = vs_icap_read(ctx->vsc_sockfd, lbuf, rsize)) <= 0) 1108 return (-1); 1109 1110 if (ctx->vsc_repair) { 1111 if (vs_icap_write(ctx->vsc_repair_fd, lbuf, rsize) < 0) 1112 ctx->vsc_repair = B_FALSE; 1113 } 1114 1115 resid -= rsize; 1116 } 1117 1118 /* Eat one CR/LF after the data */ 1119 if (vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0) 1120 return (-1); 1121 1122 if (lbuf[0]) { 1123 syslog(LOG_ERR, "ICAP protocol error - expected blank line"); 1124 return (-1); 1125 } 1126 1127 return (chunk_size); 1128 } 1129 1130 1131 /* *********************************************************************** */ 1132 /* Utility read, write functions */ 1133 /* *********************************************************************** */ 1134 1135 /* 1136 * vs_icap_write 1137 * 1138 * Return: 0 if all data successfully written 1139 * -1 otherwise 1140 */ 1141 static int 1142 vs_icap_write(int fd, char *buf, int buflen) 1143 { 1144 char *ptr = buf; 1145 int resid = buflen; 1146 int bytes_sent = 0; 1147 1148 while (resid > 0) { 1149 errno = 0; 1150 bytes_sent = write(fd, ptr, resid); 1151 if (bytes_sent < 0) { 1152 if (errno == EINTR) 1153 continue; 1154 else 1155 return (-1); 1156 } 1157 resid -= bytes_sent; 1158 ptr += bytes_sent; 1159 } 1160 1161 return (0); 1162 } 1163 1164 1165 /* 1166 * vs_icap_read 1167 * 1168 * Returns: bytes_read (== len unless EOF hit before len bytes read) 1169 * -1 error 1170 */ 1171 static int 1172 vs_icap_read(int fd, char *buf, int len) 1173 { 1174 char *ptr = buf; 1175 int resid = len; 1176 int bytes_read = 0; 1177 1178 while (resid > 0) { 1179 errno = 0; 1180 bytes_read = read(fd, ptr, resid); 1181 if (bytes_read < 0) { 1182 if (errno == EINTR) 1183 continue; 1184 else 1185 return (-1); 1186 } 1187 resid -= bytes_read; 1188 ptr += bytes_read; 1189 } 1190 1191 return (len - resid); 1192 } 1193 1194 1195 /* 1196 * vs_icap_send_chunk 1197 * 1198 * Send a "chunk" of file data, containing: 1199 * - Length (in hex) CR/NL 1200 * - [optiona data] 1201 * - CR/NL 1202 * 1203 * Returns: data length sent (not including encapsulation) 1204 * -1 - error 1205 */ 1206 static int 1207 vs_icap_send_chunk(vs_scan_ctx_t *ctx, int chunk_len) 1208 { 1209 char *hdr = ctx->vsc_info.vsi_send_hdr; 1210 char *dbuf = ctx->vsc_info.vsi_send_buf; 1211 char *tail; 1212 char head[VS_HDR_SZ + 1]; 1213 int nread = 0, hlen, tlen = 2; 1214 1215 if (chunk_len > VS_BUF_SZ) 1216 chunk_len = VS_BUF_SZ; 1217 1218 /* Read the data. */ 1219 if ((nread = vs_icap_read(ctx->vsc_fd, dbuf, chunk_len)) < 0) 1220 return (-1); 1221 1222 if (nread > 0) { 1223 /* wrap data in a header and trailer */ 1224 hlen = snprintf(head, sizeof (head), "%x\r\n", nread); 1225 hdr += (VS_HDR_SZ - hlen); 1226 (void) memcpy(hdr, head, hlen); 1227 tail = dbuf + nread; 1228 tail[0] = '\r'; 1229 tail[1] = '\n'; 1230 1231 if (vs_icap_write(ctx->vsc_sockfd, hdr, 1232 hlen + nread + tlen) < 0) { 1233 return (-1); 1234 } 1235 } 1236 1237 return (nread); 1238 } 1239 1240 1241 /* 1242 * vs_icap_send_termination 1243 * 1244 * Send 0 length termination to scan engine: "0\r\n\r\n" 1245 * 1246 * Returns: 0 - success 1247 * -1 - error 1248 */ 1249 static int 1250 vs_icap_send_termination(vs_scan_ctx_t *ctx) 1251 { 1252 if (vs_icap_write(ctx->vsc_sockfd, VS_TERMINATION, 1253 strlen(VS_TERMINATION)) < 0) { 1254 return (-1); 1255 } 1256 1257 return (0); 1258 } 1259 1260 1261 /* 1262 * vs_icap_readline 1263 * 1264 * Read a line of response data from the socket. \n indicates end of line. 1265 * 1266 * Returns: bytes read 1267 * -1 - error 1268 */ 1269 static int 1270 vs_icap_readline(vs_scan_ctx_t *ctx, char *buf, int buflen) 1271 { 1272 char c; 1273 int i, retval; 1274 1275 i = 0; 1276 for (;;) { 1277 errno = 0; 1278 retval = recv(ctx->vsc_sockfd, &c, 1, 0); 1279 1280 if (retval < 0 && errno == EINTR) 1281 continue; 1282 1283 if (retval <= 0) { 1284 if (vscand_get_state() != VS_STATE_SHUTDOWN) { 1285 syslog(LOG_ERR, "Error receiving data from " 1286 "Scan Engine: %s", strerror(errno)); 1287 } 1288 return (-1); 1289 } 1290 1291 buf[i++] = c; 1292 if (c == '\n') 1293 break; 1294 1295 if (i >= (buflen - 2)) 1296 return (-1); 1297 } 1298 1299 buf[i] = '\0'; 1300 1301 /* remove preceding and trailing whitespace */ 1302 vs_icap_trimspace(buf); 1303 1304 return (i); 1305 } 1306 1307 1308 /* ************************************************************************ */ 1309 /* HEADER processing */ 1310 /* ************************************************************************ */ 1311 1312 /* 1313 * vs_icap_parse_hdrs 1314 * 1315 * parse an icap hdr line to find name and value 1316 */ 1317 static void 1318 vs_icap_parse_hdrs(char delimiter, char *line, char **name, char **val) 1319 { 1320 char *q = line; 1321 int line_len; 1322 1323 /* strip any spaces */ 1324 while (*q == ' ') 1325 q++; 1326 1327 *name = q; 1328 *val = 0; 1329 1330 /* Empty line is normal termination */ 1331 if ((line_len = strlen(line)) == 0) 1332 return; 1333 1334 if ((q = strchr(line, delimiter)) != 0) { 1335 *q++ = '\0'; 1336 } else { 1337 q = line + line_len; 1338 } 1339 1340 /* value part follows spaces */ 1341 while (*q == ' ') 1342 q++; 1343 1344 *val = q; 1345 } 1346 1347 1348 /* 1349 * vs_icap_resp_violations 1350 */ 1351 /*ARGSUSED*/ 1352 static int 1353 vs_icap_resp_violations(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1354 { 1355 int i, rv, vcnt; 1356 1357 (void) sscanf(line, "%d", &vcnt); 1358 1359 ctx->vsc_result->vsr_nviolations = 1360 (vcnt > VS_MAX_VIOLATIONS) ? VS_MAX_VIOLATIONS : vcnt; 1361 1362 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIOLATIONS; 1363 1364 for (i = 0; i < vcnt; i++) { 1365 if ((rv = vs_icap_resp_violation_rec(ctx, i)) < 0) 1366 return (rv); 1367 1368 } 1369 1370 return (1); 1371 } 1372 1373 1374 /* 1375 * vs_icap_resp_violation_rec 1376 * 1377 * take all violation data (up to VS_MAX_VIOLATIONS) and save it 1378 * in violation_info. 1379 * each violation has 4 lines of info: doc name, virus name, 1380 * virus id and resolution 1381 */ 1382 static int 1383 vs_icap_resp_violation_rec(vs_scan_ctx_t *ctx, int vr_idx) 1384 { 1385 int vline; 1386 int retval = 0; 1387 char *buf = ctx->vsc_info.vsi_recv_buf; 1388 vs_vrec_t *vr; 1389 1390 if (vr_idx < VS_MAX_VIOLATIONS) { 1391 vr = &ctx->vsc_result->vsr_vrec[vr_idx]; 1392 } else { 1393 vr = 0; 1394 } 1395 1396 for (vline = 0; vline < VS_VIOLATION_LINES; vline++) { 1397 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 1398 return (-1); 1399 1400 /* empty line? */ 1401 if ((retval == 0) || (!buf[0])) 1402 break; 1403 1404 if (vr) { 1405 switch (vline) { 1406 case 0: /* doc name */ 1407 break; 1408 case 1: /* Threat Description */ 1409 (void) strlcpy(vr->vr_desc, buf, 1410 VS_DESCRIPTION_MAX); 1411 break; 1412 case 2: /* Problem ID */ 1413 (void) sscanf(buf, "%d", &vr->vr_id); 1414 break; 1415 case 3: /* Resolution */ 1416 (void) sscanf(buf, "%d", &vr->vr_res); 1417 break; 1418 } 1419 } 1420 } 1421 1422 return (1); 1423 } 1424 1425 1426 /* 1427 * vs_icap_opt_value 1428 * given an icap options hdr string, process value 1429 */ 1430 static int 1431 vs_icap_opt_value(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1432 { 1433 int x; 1434 long val; 1435 char *end; 1436 1437 switch (hdr_id) { 1438 case VS_OPT_PREVIEW: 1439 (void) sscanf(line, "%d", &x); 1440 if (x < VS_MIN_PREVIEW_LEN) 1441 x = VS_MIN_PREVIEW_LEN; 1442 if (x > VS_BUF_SZ) 1443 x = VS_BUF_SZ; 1444 ctx->vsc_options.vso_preview_len = x; 1445 break; 1446 1447 case VS_OPT_TTL: 1448 if (*line == 0) { 1449 ctx->vsc_options.vso_req_time = -1; 1450 break; 1451 } 1452 1453 val = strtol(line, &end, 10); 1454 if ((end != (line + strlen(line))) || (val < 0)) 1455 break; 1456 1457 ctx->vsc_options.vso_ttl = val; 1458 ctx->vsc_options.vso_req_time = time(0); 1459 break; 1460 1461 case VS_OPT_ALLOW: 1462 (void) sscanf(line, "%d", &ctx->vsc_options.vso_allow); 1463 break; 1464 1465 case VS_OPT_SERVICE: 1466 (void) strlcpy(ctx->vsc_options.vso_service, line, 1467 VS_SERVICE_SZ); 1468 break; 1469 1470 case VS_OPT_X_DEF_INFO: 1471 (void) strlcpy(ctx->vsc_options.vso_defninfo, line, 1472 VS_DEFN_SZ); 1473 break; 1474 1475 case VS_OPT_METHODS: 1476 if (strstr(line, "RESPMOD") != NULL) 1477 ctx->vsc_options.vso_respmod = 1; 1478 break; 1479 1480 case VS_OPT_ISTAG: 1481 vs_icap_istag_to_scanstamp(line, 1482 ctx->vsc_options.vso_scanstamp); 1483 break; 1484 1485 default: 1486 break; 1487 1488 } 1489 1490 return (1); 1491 } 1492 1493 1494 /* 1495 * vs_icap_resp_istag 1496 * 1497 * Called to handle ISTAG when received in RESPMOD response. 1498 * - populate result->vsr_scanstamp from istag 1499 * - update the scanstamp in vs_options and log the update. 1500 */ 1501 /*ARGSUSED*/ 1502 static int 1503 vs_icap_resp_istag(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1504 { 1505 vs_icap_istag_to_scanstamp(line, ctx->vsc_result->vsr_scanstamp); 1506 1507 /* update the scanstamp in vs_options */ 1508 (void) pthread_mutex_lock(&vs_opt_mutex); 1509 if (vs_icap_compare_se(ctx->vsc_idx, 1510 ctx->vsc_host, ctx->vsc_port) == 0) { 1511 if (strcmp(vs_options[ctx->vsc_idx].vso_scanstamp, 1512 ctx->vsc_result->vsr_scanstamp) != 0) { 1513 (void) strlcpy(vs_options[ctx->vsc_idx].vso_scanstamp, 1514 ctx->vsc_result->vsr_scanstamp, 1515 sizeof (vs_scanstamp_t)); 1516 } 1517 } 1518 (void) pthread_mutex_unlock(&vs_opt_mutex); 1519 1520 return (1); 1521 } 1522 1523 1524 /* 1525 * vs_icap_istag_to_scanstamp 1526 * 1527 * Copies istag into scanstamp, stripping leading and trailing 1528 * quotes '"' from istag. If the istag is invalid (too long) 1529 * scanstamp will be left unchanged. 1530 * 1531 * vs_scanstamp_t is defined to be large enough to hold the 1532 * istag plus a null terminator. 1533 */ 1534 static void 1535 vs_icap_istag_to_scanstamp(char *istag, vs_scanstamp_t scanstamp) 1536 { 1537 char *p = istag; 1538 int len; 1539 1540 /* eliminate preceding '"' */ 1541 if (p[0] == '"') 1542 ++p; 1543 1544 /* eliminate trailing '"' */ 1545 len = strlen(p); 1546 if (p[len - 1] == '"') 1547 --len; 1548 1549 if (len < sizeof (vs_scanstamp_t)) 1550 (void) strlcpy(scanstamp, p, len + 1); 1551 } 1552 1553 1554 /* 1555 * vs_icap_opt_ext 1556 * 1557 * read the transfer preview / transfer complete headers to 1558 * determine which file types can be previewed 1559 */ 1560 static int 1561 vs_icap_opt_ext(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1562 { 1563 vs_options_t *opt = &ctx->vsc_options; 1564 1565 switch (hdr_id) { 1566 case VS_OPT_XFER_PREVIEW: 1567 if (opt->vso_xfer_preview) { 1568 free(opt->vso_xfer_preview); 1569 opt->vso_xfer_preview = 0; 1570 } 1571 if (strstr(line, "*")) { 1572 opt->vso_xfer_how = VS_PREVIEW_ALL; 1573 } else { 1574 opt->vso_xfer_preview = vs_icap_make_strvec 1575 (line, EXT_SEPARATOR); 1576 opt->vso_xfer_how = VS_PREVIEW_LIST; 1577 } 1578 break; 1579 1580 case VS_OPT_XFER_COMPLETE : 1581 if (opt->vso_xfer_complete) { 1582 free(opt->vso_xfer_complete); 1583 opt->vso_xfer_complete = 0; 1584 } 1585 if (strstr(line, "*")) { 1586 opt->vso_xfer_how = VS_PREVIEW_NONE; 1587 } else { 1588 opt->vso_xfer_complete = vs_icap_make_strvec 1589 (line, EXT_SEPARATOR); 1590 opt->vso_xfer_how = VS_PREVIEW_EXCEPT; 1591 } 1592 break; 1593 default: 1594 break; 1595 } 1596 1597 return (1); 1598 } 1599 1600 1601 /* 1602 * vs_icap_resp_infection 1603 * 1604 * read the type, resolution and threat description for each 1605 * reported violation and save in ctx->vsc_result 1606 */ 1607 /*ARGSUSED*/ 1608 static int 1609 vs_icap_resp_infection(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1610 { 1611 char *name, *val; 1612 int i, got = 0; 1613 int type = 0, res = 0; 1614 char *desc = 0; 1615 vs_vrec_t *vr = 0; 1616 1617 for (i = 0; i < VS_INFECTION_FIELDS; i++) { 1618 vs_icap_parse_hdrs('=', line, &name, &val); 1619 1620 switch (i) { 1621 case 0: 1622 if (MATCH(name, "Type")) { 1623 (void) sscanf(val, "%d", &type); 1624 got++; 1625 } 1626 break; 1627 case 1: 1628 if (MATCH(name, "Resolution")) { 1629 (void) sscanf(val, "%d", &res); 1630 got++; 1631 } 1632 break; 1633 case 2: 1634 if (MATCH(name, "Threat")) { 1635 desc = val; 1636 got++; 1637 } 1638 break; 1639 default : 1640 break; 1641 } 1642 1643 if ((line = strstr(val, ";"))) 1644 line++; 1645 } 1646 1647 if (got != VS_INFECTION_FIELDS) 1648 return (0); 1649 1650 /* 1651 * We may have info from an X-Violations-Found record, (which provides 1652 * more complete information). If so, don't destroy what we have. 1653 */ 1654 if ((ctx->vsc_result->vsr_nviolations == 0) || 1655 (ctx->vsc_info.vsi_threat_hdr < VS_RESP_X_INFECTION)) { 1656 vr = &ctx->vsc_result->vsr_vrec[0]; 1657 vr->vr_id = type; 1658 vr->vr_res = res; 1659 (void) strlcpy(vr->vr_desc, desc, VS_DESCRIPTION_MAX); 1660 ctx->vsc_result->vsr_nviolations = 1; 1661 1662 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_INFECTION; 1663 } 1664 1665 return (1); 1666 } 1667 1668 1669 /* 1670 * vs_icap_resp_virus_id 1671 * 1672 * X-Virus-ID is defined as being a shorter alternative to X-Infection-Found. 1673 * If we already have virus information, from either X-Infection-Found or 1674 * X-Violations-Found, it will be more complete, so don't overwrite it with 1675 * the info from X-Virus-ID. 1676 */ 1677 /*ARGSUSED*/ 1678 static int 1679 vs_icap_resp_virus_id(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1680 { 1681 vs_vrec_t *vr = 0; 1682 1683 if (ctx->vsc_result->vsr_nviolations == 0) { 1684 vr = &ctx->vsc_result->vsr_vrec[0]; 1685 vr->vr_id = 0; 1686 vr->vr_res = 0; 1687 (void) strlcpy(vr->vr_desc, line, VS_DESCRIPTION_MAX); 1688 ctx->vsc_result->vsr_nviolations = 1; 1689 1690 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIRUS_ID; 1691 } 1692 1693 return (1); 1694 } 1695 1696 1697 /* 1698 * vs_icap_resp_encap 1699 * 1700 * get the encapsulated header info 1701 */ 1702 /*ARGSUSED*/ 1703 static int 1704 vs_icap_resp_encap(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1705 { 1706 if (strstr(line, "res-hdr")) 1707 ctx->vsc_info.vsi_res_hdr = B_TRUE; 1708 1709 if (strstr(line, "res-body")) 1710 ctx->vsc_info.vsi_res_body = B_TRUE; 1711 1712 return (1); 1713 } 1714 1715 1716 /* 1717 * Utility functions for handling OPTIONS data: vs_options_t 1718 */ 1719 1720 /* 1721 * vs_icap_compare_scanstamp 1722 * compare scanstamp with that stored for engine idx 1723 * 1724 * Returns: 0 - if equal 1725 */ 1726 int 1727 vs_icap_compare_scanstamp(int idx, vs_scanstamp_t scanstamp) 1728 { 1729 int rc; 1730 1731 if (!scanstamp || scanstamp[0] == '\0') 1732 return (-1); 1733 1734 (void) pthread_mutex_lock(&vs_opt_mutex); 1735 rc = strcmp(scanstamp, vs_options[idx].vso_scanstamp); 1736 (void) pthread_mutex_unlock(&vs_opt_mutex); 1737 1738 return (rc); 1739 } 1740 1741 1742 /* 1743 * vs_icap_compare_se 1744 * compare host and port with that stored for engine idx 1745 * 1746 * Returns: 0 - if equal 1747 */ 1748 static int 1749 vs_icap_compare_se(int idx, char *host, int port) 1750 { 1751 if (vs_options[idx].vso_port != port) 1752 return (-1); 1753 1754 if (strcmp(vs_options[idx].vso_host, host) != 0) 1755 return (-1); 1756 1757 return (0); 1758 } 1759 1760 1761 /* 1762 * vs_icap_free_options 1763 * 1764 * Free dynamic parts of vs_options_t: xfer_preview, xfer_complete 1765 */ 1766 static void 1767 vs_icap_free_options(vs_options_t *options) 1768 { 1769 if (options->vso_xfer_preview) 1770 free(options->vso_xfer_preview); 1771 1772 if (options->vso_xfer_complete) 1773 free(options->vso_xfer_complete); 1774 1775 (void) memset(options, 0, sizeof (vs_options_t)); 1776 } 1777 1778 1779 /* 1780 * vs_icap_copy_options 1781 */ 1782 void 1783 vs_icap_copy_options(vs_options_t *to_opt, vs_options_t *from_opt) 1784 { 1785 *to_opt = *from_opt; 1786 1787 if (from_opt->vso_xfer_preview) { 1788 to_opt->vso_xfer_preview = 1789 vs_icap_copy_strvec(from_opt->vso_xfer_preview); 1790 } 1791 1792 if (from_opt->vso_xfer_complete) { 1793 to_opt->vso_xfer_complete = 1794 vs_icap_copy_strvec(from_opt->vso_xfer_complete); 1795 } 1796 } 1797 1798 1799 /* 1800 * vs_icap_update_options 1801 */ 1802 static void 1803 vs_icap_update_options(vs_scan_ctx_t *ctx) 1804 { 1805 int idx = ctx->vsc_idx; 1806 1807 (void) pthread_mutex_lock(&vs_opt_mutex); 1808 1809 if (vs_icap_compare_se(idx, ctx->vsc_host, ctx->vsc_port) == 0) { 1810 vs_icap_free_options(&vs_options[idx]); 1811 vs_icap_copy_options(&vs_options[idx], &ctx->vsc_options); 1812 } 1813 1814 (void) pthread_mutex_unlock(&vs_opt_mutex); 1815 } 1816 1817 1818 /* 1819 * vs_icap_make_strvec 1820 * 1821 * Populate a iovec_t from line, where line is a string of 'sep' 1822 * separated fields. Within the copy of line in the iovec_t each 1823 * field will be null terminated with leading & trailing whitespace 1824 * removed. This allows for fast searching. 1825 * 1826 * The iovec_t itself and the data it points to are allocated 1827 * as a single chunk. 1828 */ 1829 static iovec_t * 1830 vs_icap_make_strvec(char *line, const char *sep) 1831 { 1832 iovec_t *vec; 1833 char *tmp, *ctx; 1834 int datalen, len; 1835 1836 datalen = strlen(line) + 1; 1837 len = sizeof (iovec_t) + datalen; 1838 1839 if ((vec = (iovec_t *)calloc(1, len)) == 0) 1840 return (0); 1841 1842 vec->iov_len = len; 1843 vec->iov_base = (char *)vec + sizeof (iovec_t); 1844 (void) strlcpy(vec->iov_base, line, datalen); 1845 1846 /* tokenize data for easier searching */ 1847 for (tmp = strtok_r(vec->iov_base, sep, &ctx); tmp; 1848 tmp = strtok_r(0, sep, &ctx)) { 1849 } 1850 1851 return (vec); 1852 } 1853 1854 1855 /* 1856 * vs_icap_copy_strvec 1857 * 1858 * allocate and copy strvec 1859 */ 1860 static iovec_t * 1861 vs_icap_copy_strvec(iovec_t *from_vec) 1862 { 1863 iovec_t *to_vec; 1864 1865 if ((to_vec = (iovec_t *)calloc(1, from_vec->iov_len)) == 0) 1866 return (0); 1867 1868 bcopy(from_vec, to_vec, from_vec->iov_len); 1869 to_vec->iov_base = (char *)to_vec + sizeof (iovec_t); 1870 1871 return (to_vec); 1872 } 1873 1874 1875 /* 1876 * vs_icap_check_ext 1877 * 1878 * Returns: 1 - if ext in strvec 1879 * 0 - otherwise 1880 */ 1881 static int 1882 vs_icap_check_ext(char *ext, iovec_t *vec) 1883 { 1884 char *p, *end = (char *)vec + vec->iov_len; 1885 1886 for (p = vec->iov_base; p < end; p += strlen(p) + 1) { 1887 if (MATCH(ext, p)) 1888 return (1); 1889 } 1890 1891 return (0); 1892 } 1893 1894 1895 /* 1896 * vs_icap_resp_str 1897 */ 1898 static char * 1899 vs_icap_resp_str(int rc) 1900 { 1901 vs_resp_msg_t *p = icap_resp; 1902 1903 if (rc < 0) 1904 rc = -rc; 1905 1906 while (p->vsm_rc != VS_RESP_UNKNOWN) { 1907 if (p->vsm_rc == rc) 1908 break; 1909 p++; 1910 } 1911 1912 return (p->vsm_msg); 1913 } 1914 1915 1916 /* 1917 * vs_icap_trimspace 1918 * 1919 * Trims whitespace from both the beginning and end of a string. This 1920 * function alters the string buffer in-place. 1921 * 1922 * Whitespaces found at the beginning of the string are eliminated by 1923 * moving forward the start of the string at the first non-whitespace 1924 * character. 1925 * Whitespace found at the end of the string are overwritten with nulls. 1926 * 1927 */ 1928 static void 1929 vs_icap_trimspace(char *buf) 1930 { 1931 char *p = buf; 1932 char *q = buf; 1933 1934 if (buf == 0) 1935 return; 1936 1937 while (*p && isspace(*p)) 1938 ++p; 1939 1940 while ((*q = *p++) != 0) 1941 ++q; 1942 1943 if (q != buf) { 1944 while ((--q, isspace(*q)) != 0) 1945 *q = '\0'; 1946 } 1947 } 1948 1949 1950 /* 1951 * vs_icap_uri_encode 1952 * 1953 * Encode uri data (eg filename) in accordance with RFC 2396 1954 * 'Illegal' characters should be replaced with %hh, where hh is 1955 * the hex value of the character. For example a space would be 1956 * replaced with %20. 1957 * Filenames are all already UTF-8 encoded. Any UTF-8 octects that 1958 * are 'illegal' characters will be encoded as described above. 1959 * 1960 * Paramaters: data - string to be encoded (NULL terminated) 1961 * buf - output buffer (NULL terminated) 1962 * size - size of output buffer 1963 * 1964 * Returns: strlen of encoded data on success 1965 * -1 size on error (contents of buf undefined) 1966 */ 1967 static int 1968 vs_icap_uri_encode(char *buf, int size, char *data) 1969 { 1970 unsigned char *iptr; 1971 char *optr = buf; 1972 int len = strlen(data); 1973 1974 /* modify the data */ 1975 for (iptr = (unsigned char *)data; *iptr; iptr++) { 1976 if (vs_icap_uri_illegal_char(*iptr)) { 1977 if ((len += 2) >= size) 1978 return (-1); 1979 (void) sprintf(optr, "%%%0x", *iptr); 1980 optr += 3; 1981 } else { 1982 if (len >= size) 1983 return (-1); 1984 *optr++ = *iptr; 1985 } 1986 } 1987 1988 *optr = '\0'; 1989 return (len); 1990 } 1991 1992 1993 /* 1994 * vs_icap_uri_illegal_char 1995 * 1996 * The following us-ascii characters (UTF-8 octets) are 'illegal': 1997 * < > # % " { } | \ ^ [ ] ` space, 0x01 -> 0x1F & 0x7F 1998 * All non us-ascii UTF-8 octets ( >= 0x80) are illegal. 1999 * 2000 * Returns: 1 if character is not allowed in a URI 2001 * 0 otherwise 2002 */ 2003 static int 2004 vs_icap_uri_illegal_char(char c) 2005 { 2006 static const char *uri_illegal_chars = "<>#%\" {}|\\^[]`"; 2007 2008 /* us-ascii non printable characters or non us-ascii */ 2009 if ((c <= 0x1F) || (c >= 0x7F)) 2010 return (1); 2011 2012 /* us-ascii dis-allowed characters */ 2013 if (strchr(uri_illegal_chars, c)) 2014 return (1); 2015 2016 return (0); 2017 2018 } 2019