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