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