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 if (!result->vsr_scanstamp) 867 (void) strlcpy(result->vsr_scanstamp, 868 ctx->vsc_options.vso_scanstamp, sizeof (vs_scanstamp_t)); 869 870 switch (ctx->vsc_info.vsi_icap_rc) { 871 case VS_RESP_NO_CONT_NEEDED: 872 result->vsr_rc = VS_RESULT_CLEAN; 873 break; 874 875 case VS_RESP_OK: 876 /* if we have no violations , that means all ok */ 877 if (result->vsr_nviolations == 0) { 878 result->vsr_rc = VS_RESULT_CLEAN; 879 break; 880 } 881 882 /* Any infections not repaired? */ 883 result->vsr_rc = VS_RESULT_CLEANED; 884 for (i = 0; i < result->vsr_nviolations; i++) { 885 if (result->vsr_vrec[i].vr_res != 886 VS_RES_FILE_REPAIRED) { 887 result->vsr_rc = VS_RESULT_FORBIDDEN; 888 break; 889 } 890 } 891 break; 892 893 case VS_RESP_CREATED : 894 /* file is repaired */ 895 result->vsr_rc = VS_RESULT_CLEANED; 896 break; 897 898 case VS_RESP_FORBIDDEN: 899 /* file is infected and could not be repaired */ 900 result->vsr_rc = VS_RESULT_FORBIDDEN; 901 break; 902 903 default: 904 syslog(LOG_ERR, "ICAP protocol error " 905 "- unsupported scan result: %s", 906 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc)); 907 return (-1); 908 } 909 910 return (0); 911 } 912 913 914 /* 915 * vs_icap_read_encap_hdr 916 * 917 * Read the encapsulated response header to determine the length of 918 * encapsulated data and, in some cases, to detect the infected state 919 * of the file. 920 * 921 * Use of http response code: 922 * Trend IWSS does not return virus information in the RESPMOD response 923 * headers unless the OPTIONAL "include X_Infection_Found" checkbox is 924 * checked and "disable_infected_url_block=yes" is set in intscan.ini. 925 * Thus if we haven't already detected the infected/cleaned status 926 * (ie if vsr_rc == VS_RESULT_CLEAN) we attempt to detect the 927 * infected/cleaned state of a file from a combination of the ICAP and 928 * http resp codes. 929 * Here are the response code values that Trend IWSS returns: 930 * - clean: icap resp = VS_RESP_NO_CONT_NEEDED 931 * - quarantine: icap resp = VS_RESP_OK, http resp = VS_RESP_FORBIDDEN 932 * - cleaned: icap resp = VS_RESP_OK, http resp = VS_RESP_OK 933 * For all other vendors' scan engines (so far) the infected/cleaned 934 * state of the file has already been detected from the RESPMOD 935 * response headers. 936 */ 937 static int 938 vs_icap_read_encap_hdr(vs_scan_ctx_t *ctx) 939 { 940 char *buf = ctx->vsc_info.vsi_recv_buf; 941 char *name, *value; 942 int retval; 943 944 /* Break on error or blank line. */ 945 for (;;) { 946 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 947 return (-1); 948 949 /* Empty line (CR/LF) normal break */ 950 if ((retval == 0) || (!buf[0])) 951 break; 952 953 if (MATCH(buf, "HTTP/1.1")) { 954 (void) sscanf(buf + 8, "%d", 955 &ctx->vsc_info.vsi_http_rc); 956 ctx->vsc_info.vsi_html_content = B_TRUE; 957 958 /* if not yet detected infection, interpret http_rc */ 959 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEAN) { 960 if ((ctx->vsc_info.vsi_icap_rc == VS_RESP_OK) && 961 (ctx->vsc_info.vsi_http_rc == VS_RESP_OK)) { 962 ctx->vsc_result->vsr_rc = 963 VS_RESULT_CLEANED; 964 } else { 965 ctx->vsc_result->vsr_rc = 966 VS_RESULT_FORBIDDEN; 967 } 968 } 969 } else { 970 vs_icap_parse_hdrs(':', buf, &name, &value); 971 if (name && (MATCH(name, "Content-Length"))) { 972 (void) sscanf(value, "%d", 973 &ctx->vsc_info.vsi_content_len); 974 } 975 } 976 } 977 978 return (0); 979 } 980 981 982 /* 983 * vs_icap_read_encap_data 984 * 985 * Read the encapsulated response data. 986 * 987 * If the response data represents cleaned file data (for an infected file) 988 * and VS_NO_REPAIR is not set, open repair file to save the reponse body 989 * data in. Set the repair flag in the scan context. The repair flag is used 990 * during the processing of the response data. If the flag is set then the 991 * data is written to file. If any error occurs which invalidates the repaired 992 * data file the repair flag gets reset to 0, and the data will be discarded. 993 * 994 * The result is reset to VS_RESULT_FORBIDDEN until all of the cleaned data 995 * has been successfully received and processed. It is then reset to 996 * VS_RESULT_CLEANED. 997 * 998 * If the data doesn't represent cleaned file data, or we cannot (or don't 999 * want to) write the cleaned data to file, the data is discarded (repair flag 1000 * in ctx == 0). 1001 */ 1002 static void 1003 vs_icap_read_encap_data(vs_scan_ctx_t *ctx) 1004 { 1005 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED) { 1006 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN; 1007 1008 if (!(ctx->vsc_flags & VS_NO_REPAIR)) { 1009 if (vs_icap_create_repair_file(ctx) == 0) 1010 ctx->vsc_repair = B_TRUE; 1011 } 1012 } 1013 1014 /* 1015 * vs_icap_read_resp_body handles errors internally; 1016 * resets ctx->vsc_repair 1017 */ 1018 (void) vs_icap_read_resp_body(ctx); 1019 1020 if (ctx->vsc_repair_fd != -1) { 1021 (void) close(ctx->vsc_repair_fd); 1022 1023 if (ctx->vsc_repair) { 1024 /* repair file contains the cleaned data */ 1025 ctx->vsc_result->vsr_rc = VS_RESULT_CLEANED; 1026 } else { 1027 /* error occured processing data. Remove repair file */ 1028 (void) unlink(ctx->vsc_repair_fname); 1029 } 1030 } 1031 } 1032 1033 1034 /* 1035 * vs_icap_create_repair_file 1036 * 1037 * Create and open a file to save cleaned data in. 1038 */ 1039 static int 1040 vs_icap_create_repair_file(vs_scan_ctx_t *ctx) 1041 { 1042 if (ctx->vsc_repair_fname == NULL) 1043 return (-1); 1044 1045 if ((ctx->vsc_repair_fd = open(ctx->vsc_repair_fname, 1046 O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0644)) == -1) { 1047 return (-1); 1048 } 1049 1050 return (0); 1051 } 1052 1053 1054 /* 1055 * vs_icap_read_resp_body 1056 * 1057 * Repeatedly call vs_icap_read_body_chunk until it returns: 1058 * 0 indicating that there's no more data to read or 1059 * -1 indicating a read error -> reset ctx->vsc_repair 0 1060 * 1061 * Returns: 0 success 1062 * -1 error 1063 */ 1064 static int 1065 vs_icap_read_resp_body(vs_scan_ctx_t *ctx) 1066 { 1067 int retval; 1068 1069 while ((retval = vs_icap_read_body_chunk(ctx)) > 0) 1070 ; 1071 1072 if (retval < 0) 1073 ctx->vsc_repair = B_FALSE; 1074 1075 return (retval); 1076 } 1077 1078 1079 /* 1080 * vs_icap_read_body_chunk 1081 * 1082 * Read the chunk size, then read the chunk of data and write the 1083 * data to file repair_fd (or discard it). 1084 * If the data cannot be successfully written to file, set repair 1085 * flag in ctx to 0, and discard all subsequent data. 1086 * 1087 * Returns: chunk size 1088 * -1 on error 1089 */ 1090 static int 1091 vs_icap_read_body_chunk(vs_scan_ctx_t *ctx) 1092 { 1093 char *lbuf = ctx->vsc_info.vsi_recv_buf; 1094 unsigned int chunk_size, resid; 1095 int rsize; 1096 1097 /* Read and parse the chunk size. */ 1098 if ((vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0) || 1099 (!sscanf(lbuf, "%x", &chunk_size))) { 1100 return (-1); 1101 } 1102 1103 /* Read and save/discard chunk */ 1104 resid = chunk_size; 1105 while (resid) { 1106 rsize = (resid < VS_BUF_SZ) ? resid : VS_BUF_SZ; 1107 1108 if ((rsize = vs_icap_read(ctx->vsc_sockfd, lbuf, rsize)) <= 0) 1109 return (-1); 1110 1111 if (ctx->vsc_repair) { 1112 if (vs_icap_write(ctx->vsc_repair_fd, lbuf, rsize) < 0) 1113 ctx->vsc_repair = B_FALSE; 1114 } 1115 1116 resid -= rsize; 1117 } 1118 1119 /* Eat one CR/LF after the data */ 1120 if (vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0) 1121 return (-1); 1122 1123 if (lbuf[0]) { 1124 syslog(LOG_ERR, "ICAP protocol error - expected blank line"); 1125 return (-1); 1126 } 1127 1128 return (chunk_size); 1129 } 1130 1131 1132 /* *********************************************************************** */ 1133 /* Utility read, write functions */ 1134 /* *********************************************************************** */ 1135 1136 /* 1137 * vs_icap_write 1138 * 1139 * Return: 0 if all data successfully written 1140 * -1 otherwise 1141 */ 1142 static int 1143 vs_icap_write(int fd, char *buf, int buflen) 1144 { 1145 char *ptr = buf; 1146 int resid = buflen; 1147 int bytes_sent = 0; 1148 1149 while (resid > 0) { 1150 errno = 0; 1151 bytes_sent = write(fd, ptr, resid); 1152 if (bytes_sent < 0) { 1153 if (errno == EINTR) 1154 continue; 1155 else 1156 return (-1); 1157 } 1158 resid -= bytes_sent; 1159 ptr += bytes_sent; 1160 } 1161 1162 return (0); 1163 } 1164 1165 1166 /* 1167 * vs_icap_read 1168 * 1169 * Returns: bytes_read (== len unless EOF hit before len bytes read) 1170 * -1 error 1171 */ 1172 static int 1173 vs_icap_read(int fd, char *buf, int len) 1174 { 1175 char *ptr = buf; 1176 int resid = len; 1177 int bytes_read = 0; 1178 1179 while (resid > 0) { 1180 errno = 0; 1181 bytes_read = read(fd, ptr, resid); 1182 if (bytes_read < 0) { 1183 if (errno == EINTR) 1184 continue; 1185 else 1186 return (-1); 1187 } 1188 resid -= bytes_read; 1189 ptr += bytes_read; 1190 } 1191 1192 return (len - resid); 1193 } 1194 1195 1196 /* 1197 * vs_icap_send_chunk 1198 * 1199 * Send a "chunk" of file data, containing: 1200 * - Length (in hex) CR/NL 1201 * - [optiona data] 1202 * - CR/NL 1203 * 1204 * Returns: data length sent (not including encapsulation) 1205 * -1 - error 1206 */ 1207 static int 1208 vs_icap_send_chunk(vs_scan_ctx_t *ctx, int chunk_len) 1209 { 1210 char *hdr = ctx->vsc_info.vsi_send_hdr; 1211 char *dbuf = ctx->vsc_info.vsi_send_buf; 1212 char *tail; 1213 char head[VS_HDR_SZ + 1]; 1214 int nread = 0, hlen, tlen = 2; 1215 1216 if (chunk_len > VS_BUF_SZ) 1217 chunk_len = VS_BUF_SZ; 1218 1219 /* Read the data. */ 1220 if ((nread = vs_icap_read(ctx->vsc_fd, dbuf, chunk_len)) < 0) 1221 return (-1); 1222 1223 if (nread > 0) { 1224 /* wrap data in a header and trailer */ 1225 hlen = snprintf(head, sizeof (head), "%x\r\n", nread); 1226 hdr += (VS_HDR_SZ - hlen); 1227 (void) memcpy(hdr, head, hlen); 1228 tail = dbuf + nread; 1229 tail[0] = '\r'; 1230 tail[1] = '\n'; 1231 1232 if (vs_icap_write(ctx->vsc_sockfd, hdr, 1233 hlen + nread + tlen) < 0) { 1234 return (-1); 1235 } 1236 } 1237 1238 return (nread); 1239 } 1240 1241 1242 /* 1243 * vs_icap_send_termination 1244 * 1245 * Send 0 length termination to scan engine: "0\r\n\r\n" 1246 * 1247 * Returns: 0 - success 1248 * -1 - error 1249 */ 1250 static int 1251 vs_icap_send_termination(vs_scan_ctx_t *ctx) 1252 { 1253 if (vs_icap_write(ctx->vsc_sockfd, VS_TERMINATION, 1254 strlen(VS_TERMINATION)) < 0) { 1255 return (-1); 1256 } 1257 1258 return (0); 1259 } 1260 1261 1262 /* 1263 * vs_icap_readline 1264 * 1265 * Read a line of response data from the socket. \n indicates end of line. 1266 * 1267 * Returns: bytes read 1268 * -1 - error 1269 */ 1270 static int 1271 vs_icap_readline(vs_scan_ctx_t *ctx, char *buf, int buflen) 1272 { 1273 char c; 1274 int i, retval; 1275 1276 i = 0; 1277 for (;;) { 1278 errno = 0; 1279 retval = recv(ctx->vsc_sockfd, &c, 1, 0); 1280 1281 if (retval < 0 && errno == EINTR) 1282 continue; 1283 1284 if (retval <= 0) { 1285 if (vscand_get_state() != VS_STATE_SHUTDOWN) { 1286 syslog(LOG_ERR, "Error receiving data from " 1287 "Scan Engine: %s", strerror(errno)); 1288 } 1289 return (-1); 1290 } 1291 1292 buf[i++] = c; 1293 if (c == '\n') 1294 break; 1295 1296 if (i >= (buflen - 2)) 1297 return (-1); 1298 } 1299 1300 buf[i] = '\0'; 1301 1302 /* remove preceding and trailing whitespace */ 1303 vs_icap_trimspace(buf); 1304 1305 return (i); 1306 } 1307 1308 1309 /* ************************************************************************ */ 1310 /* HEADER processing */ 1311 /* ************************************************************************ */ 1312 1313 /* 1314 * vs_icap_parse_hdrs 1315 * 1316 * parse an icap hdr line to find name and value 1317 */ 1318 static void 1319 vs_icap_parse_hdrs(char delimiter, char *line, char **name, char **val) 1320 { 1321 char *q = line; 1322 int line_len; 1323 1324 /* strip any spaces */ 1325 while (*q == ' ') 1326 q++; 1327 1328 *name = q; 1329 *val = 0; 1330 1331 /* Empty line is normal termination */ 1332 if ((line_len = strlen(line)) == 0) 1333 return; 1334 1335 if ((q = strchr(line, delimiter)) != 0) { 1336 *q++ = '\0'; 1337 } else { 1338 q = line + line_len; 1339 } 1340 1341 /* value part follows spaces */ 1342 while (*q == ' ') 1343 q++; 1344 1345 *val = q; 1346 } 1347 1348 1349 /* 1350 * vs_icap_resp_violations 1351 */ 1352 /*ARGSUSED*/ 1353 static int 1354 vs_icap_resp_violations(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1355 { 1356 int i, rv, vcnt; 1357 1358 (void) sscanf(line, "%d", &vcnt); 1359 1360 ctx->vsc_result->vsr_nviolations = 1361 (vcnt > VS_MAX_VIOLATIONS) ? VS_MAX_VIOLATIONS : vcnt; 1362 1363 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIOLATIONS; 1364 1365 for (i = 0; i < vcnt; i++) { 1366 if ((rv = vs_icap_resp_violation_rec(ctx, i)) < 0) 1367 return (rv); 1368 1369 } 1370 1371 return (1); 1372 } 1373 1374 1375 /* 1376 * vs_icap_resp_violation_rec 1377 * 1378 * take all violation data (up to VS_MAX_VIOLATIONS) and save it 1379 * in violation_info. 1380 * each violation has 4 lines of info: doc name, virus name, 1381 * virus id and resolution 1382 */ 1383 static int 1384 vs_icap_resp_violation_rec(vs_scan_ctx_t *ctx, int vr_idx) 1385 { 1386 int vline; 1387 int retval = 0; 1388 char *buf = ctx->vsc_info.vsi_recv_buf; 1389 vs_vrec_t *vr; 1390 1391 if (vr_idx < VS_MAX_VIOLATIONS) { 1392 vr = &ctx->vsc_result->vsr_vrec[vr_idx]; 1393 } else { 1394 vr = 0; 1395 } 1396 1397 for (vline = 0; vline < VS_VIOLATION_LINES; vline++) { 1398 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 1399 return (-1); 1400 1401 /* empty line? */ 1402 if ((retval == 0) || (!buf[0])) 1403 break; 1404 1405 if (vr) { 1406 switch (vline) { 1407 case 0: /* doc name */ 1408 break; 1409 case 1: /* Threat Description */ 1410 (void) strlcpy(vr->vr_desc, buf, 1411 VS_DESCRIPTION_MAX); 1412 break; 1413 case 2: /* Problem ID */ 1414 (void) sscanf(buf, "%d", &vr->vr_id); 1415 break; 1416 case 3: /* Resolution */ 1417 (void) sscanf(buf, "%d", &vr->vr_res); 1418 break; 1419 } 1420 } 1421 } 1422 1423 return (1); 1424 } 1425 1426 1427 /* 1428 * vs_icap_opt_value 1429 * given an icap options hdr string, process value 1430 */ 1431 static int 1432 vs_icap_opt_value(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1433 { 1434 int x; 1435 long val; 1436 char *end; 1437 1438 switch (hdr_id) { 1439 case VS_OPT_PREVIEW: 1440 (void) sscanf(line, "%d", &x); 1441 if (x < VS_MIN_PREVIEW_LEN) 1442 x = VS_MIN_PREVIEW_LEN; 1443 if (x > VS_BUF_SZ) 1444 x = VS_BUF_SZ; 1445 ctx->vsc_options.vso_preview_len = x; 1446 break; 1447 1448 case VS_OPT_TTL: 1449 if (*line == 0) { 1450 ctx->vsc_options.vso_req_time = -1; 1451 break; 1452 } 1453 1454 val = strtol(line, &end, 10); 1455 if ((end != (line + strlen(line))) || (val < 0)) 1456 break; 1457 1458 ctx->vsc_options.vso_ttl = val; 1459 ctx->vsc_options.vso_req_time = time(0); 1460 break; 1461 1462 case VS_OPT_ALLOW: 1463 (void) sscanf(line, "%d", &ctx->vsc_options.vso_allow); 1464 break; 1465 1466 case VS_OPT_SERVICE: 1467 (void) strlcpy(ctx->vsc_options.vso_service, line, 1468 VS_SERVICE_SZ); 1469 break; 1470 1471 case VS_OPT_X_DEF_INFO: 1472 (void) strlcpy(ctx->vsc_options.vso_defninfo, line, 1473 VS_DEFN_SZ); 1474 break; 1475 1476 case VS_OPT_METHODS: 1477 if (strstr(line, "RESPMOD") != NULL) 1478 ctx->vsc_options.vso_respmod = 1; 1479 break; 1480 1481 case VS_OPT_ISTAG: 1482 vs_icap_istag_to_scanstamp(line, 1483 ctx->vsc_options.vso_scanstamp); 1484 break; 1485 1486 default: 1487 break; 1488 1489 } 1490 1491 return (1); 1492 } 1493 1494 1495 /* 1496 * vs_icap_resp_istag 1497 * 1498 * Called to handle ISTAG when received in RESPMOD response. 1499 * - populate result->vsr_scanstamp from istag 1500 * - update the scanstamp in vs_options and log the update. 1501 */ 1502 /*ARGSUSED*/ 1503 static int 1504 vs_icap_resp_istag(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1505 { 1506 vs_icap_istag_to_scanstamp(line, ctx->vsc_result->vsr_scanstamp); 1507 1508 /* update the scanstamp in vs_options */ 1509 (void) pthread_mutex_lock(&vs_opt_mutex); 1510 if (vs_icap_compare_se(ctx->vsc_idx, 1511 ctx->vsc_host, ctx->vsc_port) == 0) { 1512 if (strcmp(vs_options[ctx->vsc_idx].vso_scanstamp, 1513 ctx->vsc_result->vsr_scanstamp) != 0) { 1514 (void) strlcpy(vs_options[ctx->vsc_idx].vso_scanstamp, 1515 ctx->vsc_result->vsr_scanstamp, 1516 sizeof (vs_scanstamp_t)); 1517 } 1518 } 1519 (void) pthread_mutex_unlock(&vs_opt_mutex); 1520 1521 return (1); 1522 } 1523 1524 1525 /* 1526 * vs_icap_istag_to_scanstamp 1527 * 1528 * Copies istag into scanstamp, stripping leading and trailing 1529 * quotes '"' from istag. If the istag is invalid (too long) 1530 * scanstamp will be left unchanged. 1531 * 1532 * vs_scanstamp_t is defined to be large enough to hold the 1533 * istag plus a null terminator. 1534 */ 1535 static void 1536 vs_icap_istag_to_scanstamp(char *istag, vs_scanstamp_t scanstamp) 1537 { 1538 char *p = istag; 1539 int len; 1540 1541 /* eliminate preceding '"' */ 1542 if (p[0] == '"') 1543 ++p; 1544 1545 /* eliminate trailing '"' */ 1546 len = strlen(p); 1547 if (p[len - 1] == '"') 1548 --len; 1549 1550 if (len < sizeof (vs_scanstamp_t)) 1551 (void) strlcpy(scanstamp, p, len + 1); 1552 } 1553 1554 1555 /* 1556 * vs_icap_opt_ext 1557 * 1558 * read the transfer preview / transfer complete headers to 1559 * determine which file types can be previewed 1560 */ 1561 static int 1562 vs_icap_opt_ext(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1563 { 1564 vs_options_t *opt = &ctx->vsc_options; 1565 1566 switch (hdr_id) { 1567 case VS_OPT_XFER_PREVIEW: 1568 if (opt->vso_xfer_preview) { 1569 free(opt->vso_xfer_preview); 1570 opt->vso_xfer_preview = 0; 1571 } 1572 if (strstr(line, "*")) { 1573 opt->vso_xfer_how = VS_PREVIEW_ALL; 1574 } else { 1575 opt->vso_xfer_preview = vs_icap_make_strvec 1576 (line, EXT_SEPARATOR); 1577 opt->vso_xfer_how = VS_PREVIEW_LIST; 1578 } 1579 break; 1580 1581 case VS_OPT_XFER_COMPLETE : 1582 if (opt->vso_xfer_complete) { 1583 free(opt->vso_xfer_complete); 1584 opt->vso_xfer_complete = 0; 1585 } 1586 if (strstr(line, "*")) { 1587 opt->vso_xfer_how = VS_PREVIEW_NONE; 1588 } else { 1589 opt->vso_xfer_complete = vs_icap_make_strvec 1590 (line, EXT_SEPARATOR); 1591 opt->vso_xfer_how = VS_PREVIEW_EXCEPT; 1592 } 1593 break; 1594 default: 1595 break; 1596 } 1597 1598 return (1); 1599 } 1600 1601 1602 /* 1603 * vs_icap_resp_infection 1604 * 1605 * read the type, resolution and threat description for each 1606 * reported violation and save in ctx->vsc_result 1607 */ 1608 /*ARGSUSED*/ 1609 static int 1610 vs_icap_resp_infection(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1611 { 1612 char *name, *val; 1613 int i, got = 0; 1614 int type = 0, res = 0; 1615 char *desc = 0; 1616 vs_vrec_t *vr = 0; 1617 1618 for (i = 0; i < VS_INFECTION_FIELDS; i++) { 1619 vs_icap_parse_hdrs('=', line, &name, &val); 1620 1621 switch (i) { 1622 case 0: 1623 if (MATCH(name, "Type")) { 1624 (void) sscanf(val, "%d", &type); 1625 got++; 1626 } 1627 break; 1628 case 1: 1629 if (MATCH(name, "Resolution")) { 1630 (void) sscanf(val, "%d", &res); 1631 got++; 1632 } 1633 break; 1634 case 2: 1635 if (MATCH(name, "Threat")) { 1636 desc = val; 1637 got++; 1638 } 1639 break; 1640 default : 1641 break; 1642 } 1643 1644 if ((line = strstr(val, ";"))) 1645 line++; 1646 } 1647 1648 if (got != VS_INFECTION_FIELDS) 1649 return (0); 1650 1651 /* 1652 * We may have info from an X-Violations-Found record, (which provides 1653 * more complete information). If so, don't destroy what we have. 1654 */ 1655 if ((ctx->vsc_result->vsr_nviolations == 0) || 1656 (ctx->vsc_info.vsi_threat_hdr < VS_RESP_X_INFECTION)) { 1657 vr = &ctx->vsc_result->vsr_vrec[0]; 1658 vr->vr_id = type; 1659 vr->vr_res = res; 1660 (void) strlcpy(vr->vr_desc, desc, VS_DESCRIPTION_MAX); 1661 ctx->vsc_result->vsr_nviolations = 1; 1662 1663 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_INFECTION; 1664 } 1665 1666 return (1); 1667 } 1668 1669 1670 /* 1671 * vs_icap_resp_virus_id 1672 * 1673 * X-Virus-ID is defined as being a shorter alternative to X-Infection-Found. 1674 * If we already have virus information, from either X-Infection-Found or 1675 * X-Violations-Found, it will be more complete, so don't overwrite it with 1676 * the info from X-Virus-ID. 1677 */ 1678 /*ARGSUSED*/ 1679 static int 1680 vs_icap_resp_virus_id(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1681 { 1682 vs_vrec_t *vr = 0; 1683 1684 if (ctx->vsc_result->vsr_nviolations == 0) { 1685 vr = &ctx->vsc_result->vsr_vrec[0]; 1686 vr->vr_id = 0; 1687 vr->vr_res = 0; 1688 (void) strlcpy(vr->vr_desc, line, VS_DESCRIPTION_MAX); 1689 ctx->vsc_result->vsr_nviolations = 1; 1690 1691 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIRUS_ID; 1692 } 1693 1694 return (1); 1695 } 1696 1697 1698 /* 1699 * vs_icap_resp_encap 1700 * 1701 * get the encapsulated header info 1702 */ 1703 /*ARGSUSED*/ 1704 static int 1705 vs_icap_resp_encap(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1706 { 1707 if (strstr(line, "res-hdr")) 1708 ctx->vsc_info.vsi_res_hdr = B_TRUE; 1709 1710 if (strstr(line, "res-body")) 1711 ctx->vsc_info.vsi_res_body = B_TRUE; 1712 1713 return (1); 1714 } 1715 1716 1717 /* 1718 * Utility functions for handling OPTIONS data: vs_options_t 1719 */ 1720 1721 /* 1722 * vs_icap_compare_scanstamp 1723 * compare scanstamp with that stored for engine idx 1724 * 1725 * Returns: 0 - if equal 1726 */ 1727 int 1728 vs_icap_compare_scanstamp(int idx, vs_scanstamp_t scanstamp) 1729 { 1730 int rc; 1731 1732 if (!scanstamp || scanstamp[0] == '\0') 1733 return (-1); 1734 1735 (void) pthread_mutex_lock(&vs_opt_mutex); 1736 rc = strcmp(scanstamp, vs_options[idx].vso_scanstamp); 1737 (void) pthread_mutex_unlock(&vs_opt_mutex); 1738 1739 return (rc); 1740 } 1741 1742 1743 /* 1744 * vs_icap_compare_se 1745 * compare host and port with that stored for engine idx 1746 * 1747 * Returns: 0 - if equal 1748 */ 1749 static int 1750 vs_icap_compare_se(int idx, char *host, int port) 1751 { 1752 if (vs_options[idx].vso_port != port) 1753 return (-1); 1754 1755 if (strcmp(vs_options[idx].vso_host, host) != 0) 1756 return (-1); 1757 1758 return (0); 1759 } 1760 1761 1762 /* 1763 * vs_icap_free_options 1764 * 1765 * Free dynamic parts of vs_options_t: xfer_preview, xfer_complete 1766 */ 1767 static void 1768 vs_icap_free_options(vs_options_t *options) 1769 { 1770 if (options->vso_xfer_preview) 1771 free(options->vso_xfer_preview); 1772 1773 if (options->vso_xfer_complete) 1774 free(options->vso_xfer_complete); 1775 1776 (void) memset(options, 0, sizeof (vs_options_t)); 1777 } 1778 1779 1780 /* 1781 * vs_icap_copy_options 1782 */ 1783 void 1784 vs_icap_copy_options(vs_options_t *to_opt, vs_options_t *from_opt) 1785 { 1786 *to_opt = *from_opt; 1787 1788 if (from_opt->vso_xfer_preview) { 1789 to_opt->vso_xfer_preview = 1790 vs_icap_copy_strvec(from_opt->vso_xfer_preview); 1791 } 1792 1793 if (from_opt->vso_xfer_complete) { 1794 to_opt->vso_xfer_complete = 1795 vs_icap_copy_strvec(from_opt->vso_xfer_complete); 1796 } 1797 } 1798 1799 1800 /* 1801 * vs_icap_update_options 1802 */ 1803 static void 1804 vs_icap_update_options(vs_scan_ctx_t *ctx) 1805 { 1806 int idx = ctx->vsc_idx; 1807 1808 (void) pthread_mutex_lock(&vs_opt_mutex); 1809 1810 if (vs_icap_compare_se(idx, ctx->vsc_host, ctx->vsc_port) == 0) { 1811 vs_icap_free_options(&vs_options[idx]); 1812 vs_icap_copy_options(&vs_options[idx], &ctx->vsc_options); 1813 } 1814 1815 (void) pthread_mutex_unlock(&vs_opt_mutex); 1816 } 1817 1818 1819 /* 1820 * vs_icap_make_strvec 1821 * 1822 * Populate a iovec_t from line, where line is a string of 'sep' 1823 * separated fields. Within the copy of line in the iovec_t each 1824 * field will be null terminated with leading & trailing whitespace 1825 * removed. This allows for fast searching. 1826 * 1827 * The iovec_t itself and the data it points to are allocated 1828 * as a single chunk. 1829 */ 1830 static iovec_t * 1831 vs_icap_make_strvec(char *line, const char *sep) 1832 { 1833 iovec_t *vec; 1834 char *tmp, *ctx; 1835 int datalen, len; 1836 1837 datalen = strlen(line) + 1; 1838 len = sizeof (iovec_t) + datalen; 1839 1840 if ((vec = (iovec_t *)calloc(1, len)) == 0) 1841 return (0); 1842 1843 vec->iov_len = len; 1844 vec->iov_base = (char *)vec + sizeof (iovec_t); 1845 (void) strlcpy(vec->iov_base, line, datalen); 1846 1847 /* tokenize data for easier searching */ 1848 for (tmp = strtok_r(vec->iov_base, sep, &ctx); tmp; 1849 tmp = strtok_r(0, sep, &ctx)) { 1850 } 1851 1852 return (vec); 1853 } 1854 1855 1856 /* 1857 * vs_icap_copy_strvec 1858 * 1859 * allocate and copy strvec 1860 */ 1861 static iovec_t * 1862 vs_icap_copy_strvec(iovec_t *from_vec) 1863 { 1864 iovec_t *to_vec; 1865 1866 if ((to_vec = (iovec_t *)calloc(1, from_vec->iov_len)) == 0) 1867 return (0); 1868 1869 bcopy(from_vec, to_vec, from_vec->iov_len); 1870 to_vec->iov_base = (char *)to_vec + sizeof (iovec_t); 1871 1872 return (to_vec); 1873 } 1874 1875 1876 /* 1877 * vs_icap_check_ext 1878 * 1879 * Returns: 1 - if ext in strvec 1880 * 0 - otherwise 1881 */ 1882 static int 1883 vs_icap_check_ext(char *ext, iovec_t *vec) 1884 { 1885 char *p, *end = (char *)vec + vec->iov_len; 1886 1887 for (p = vec->iov_base; p < end; p += strlen(p) + 1) { 1888 if (MATCH(ext, p)) 1889 return (1); 1890 } 1891 1892 return (0); 1893 } 1894 1895 1896 /* 1897 * vs_icap_resp_str 1898 */ 1899 static char * 1900 vs_icap_resp_str(int rc) 1901 { 1902 vs_resp_msg_t *p = icap_resp; 1903 1904 if (rc < 0) 1905 rc = -rc; 1906 1907 while (p->vsm_rc != VS_RESP_UNKNOWN) { 1908 if (p->vsm_rc == rc) 1909 break; 1910 p++; 1911 } 1912 1913 return (p->vsm_msg); 1914 } 1915 1916 1917 /* 1918 * vs_icap_trimspace 1919 * 1920 * Trims whitespace from both the beginning and end of a string. This 1921 * function alters the string buffer in-place. 1922 * 1923 * Whitespaces found at the beginning of the string are eliminated by 1924 * moving forward the start of the string at the first non-whitespace 1925 * character. 1926 * Whitespace found at the end of the string are overwritten with nulls. 1927 * 1928 */ 1929 static void 1930 vs_icap_trimspace(char *buf) 1931 { 1932 char *p = buf; 1933 char *q = buf; 1934 1935 if (buf == 0) 1936 return; 1937 1938 while (*p && isspace(*p)) 1939 ++p; 1940 1941 while ((*q = *p++) != 0) 1942 ++q; 1943 1944 if (q != buf) { 1945 while ((--q, isspace(*q)) != 0) 1946 *q = '\0'; 1947 } 1948 } 1949 1950 1951 /* 1952 * vs_icap_uri_encode 1953 * 1954 * Encode uri data (eg filename) in accordance with RFC 2396 1955 * 'Illegal' characters should be replaced with %hh, where hh is 1956 * the hex value of the character. For example a space would be 1957 * replaced with %20. 1958 * Filenames are all already UTF-8 encoded. Any UTF-8 octects that 1959 * are 'illegal' characters will be encoded as described above. 1960 * 1961 * Paramaters: data - string to be encoded (NULL terminated) 1962 * buf - output buffer (NULL terminated) 1963 * size - size of output buffer 1964 * 1965 * Returns: strlen of encoded data on success 1966 * -1 size on error (contents of buf undefined) 1967 */ 1968 static int 1969 vs_icap_uri_encode(char *buf, int size, char *data) 1970 { 1971 unsigned char *iptr; 1972 char *optr = buf; 1973 int len = strlen(data); 1974 1975 /* modify the data */ 1976 for (iptr = (unsigned char *)data; *iptr; iptr++) { 1977 if (vs_icap_uri_illegal_char(*iptr)) { 1978 if ((len += 2) >= size) 1979 return (-1); 1980 (void) sprintf(optr, "%%%0x", *iptr); 1981 optr += 3; 1982 } else { 1983 if (len >= size) 1984 return (-1); 1985 *optr++ = *iptr; 1986 } 1987 } 1988 1989 *optr = '\0'; 1990 return (len); 1991 } 1992 1993 1994 /* 1995 * vs_icap_uri_illegal_char 1996 * 1997 * The following us-ascii characters (UTF-8 octets) are 'illegal': 1998 * < > # % " { } | \ ^ [ ] ` space, 0x01 -> 0x1F & 0x7F 1999 * All non us-ascii UTF-8 octets ( >= 0x80) are illegal. 2000 * 2001 * Returns: 1 if character is not allowed in a URI 2002 * 0 otherwise 2003 */ 2004 static int 2005 vs_icap_uri_illegal_char(char c) 2006 { 2007 static const char *uri_illegal_chars = "<>#%\" {}|\\^[]`"; 2008 2009 /* us-ascii non printable characters or non us-ascii */ 2010 if ((c <= 0x1F) || (c >= 0x7F)) 2011 return (1); 2012 2013 /* us-ascii dis-allowed characters */ 2014 if (strchr(uri_illegal_chars, c)) 2015 return (1); 2016 2017 return (0); 2018 2019 } 2020