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 2007 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 NAS AVA 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\n", 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 return (-1); 1276 1277 if (retval <= 0) 1278 continue; 1279 1280 buf[i++] = c; 1281 if (c == '\n') 1282 break; 1283 1284 if (i >= (buflen - 2)) 1285 return (-1); 1286 } 1287 1288 buf[i] = '\0'; 1289 1290 /* remove preceding and trailing whitespace */ 1291 vs_icap_trimspace(buf); 1292 1293 return (i); 1294 } 1295 1296 1297 /* ************************************************************************ */ 1298 /* HEADER processing */ 1299 /* ************************************************************************ */ 1300 1301 /* 1302 * vs_icap_parse_hdrs 1303 * 1304 * parse an icap hdr line to find name and value 1305 */ 1306 static void 1307 vs_icap_parse_hdrs(char delimiter, char *line, char **name, char **val) 1308 { 1309 char *q = line; 1310 int line_len; 1311 1312 /* strip any spaces */ 1313 while (*q == ' ') 1314 q++; 1315 1316 *name = q; 1317 *val = 0; 1318 1319 /* Empty line is normal termination */ 1320 if ((line_len = strlen(line)) == 0) 1321 return; 1322 1323 if ((q = strchr(line, delimiter)) != 0) { 1324 *q++ = '\0'; 1325 } else { 1326 q = line + line_len; 1327 } 1328 1329 /* value part follows spaces */ 1330 while (*q == ' ') 1331 q++; 1332 1333 *val = q; 1334 } 1335 1336 1337 /* 1338 * vs_icap_resp_violations 1339 */ 1340 /*ARGSUSED*/ 1341 static int 1342 vs_icap_resp_violations(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1343 { 1344 int i, rv, vcnt; 1345 1346 (void) sscanf(line, "%d", &vcnt); 1347 1348 ctx->vsc_result->vsr_nviolations = 1349 (vcnt > VS_MAX_VIOLATIONS) ? VS_MAX_VIOLATIONS : vcnt; 1350 1351 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIOLATIONS; 1352 1353 for (i = 0; i < vcnt; i++) { 1354 if ((rv = vs_icap_resp_violation_rec(ctx, i)) < 0) 1355 return (rv); 1356 1357 } 1358 1359 return (1); 1360 } 1361 1362 1363 /* 1364 * vs_icap_resp_violation_rec 1365 * 1366 * take all violation data (up to VS_MAX_VIOLATIONS) and save it 1367 * in violation_info. 1368 * each violation has 4 lines of info: doc name, virus name, 1369 * virus id and resolution 1370 */ 1371 static int 1372 vs_icap_resp_violation_rec(vs_scan_ctx_t *ctx, int vr_idx) 1373 { 1374 int vline; 1375 int retval = 0; 1376 char *buf = ctx->vsc_info.vsi_recv_buf; 1377 vs_vrec_t *vr; 1378 1379 if (vr_idx < VS_MAX_VIOLATIONS) { 1380 vr = &ctx->vsc_result->vsr_vrec[vr_idx]; 1381 } else { 1382 vr = 0; 1383 } 1384 1385 for (vline = 0; vline < VS_VIOLATION_LINES; vline++) { 1386 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0) 1387 return (-1); 1388 1389 /* empty line? */ 1390 if ((retval == 0) || (!buf[0])) 1391 break; 1392 1393 if (vr) { 1394 switch (vline) { 1395 case 0: /* doc name */ 1396 break; 1397 case 1: /* Threat Description */ 1398 (void) strlcpy(vr->vr_desc, buf, 1399 VS_DESCRIPTION_MAX); 1400 break; 1401 case 2: /* Problem ID */ 1402 (void) sscanf(buf, "%d", &vr->vr_id); 1403 break; 1404 case 3: /* Resolution */ 1405 (void) sscanf(buf, "%d", &vr->vr_res); 1406 break; 1407 } 1408 } 1409 } 1410 1411 return (1); 1412 } 1413 1414 1415 /* 1416 * vs_icap_opt_value 1417 * given an icap options hdr string, process value 1418 */ 1419 static int 1420 vs_icap_opt_value(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1421 { 1422 int x; 1423 long val; 1424 char *end; 1425 1426 switch (hdr_id) { 1427 case VS_OPT_PREVIEW: 1428 (void) sscanf(line, "%d", &x); 1429 if (x < VS_MIN_PREVIEW_LEN) 1430 x = VS_MIN_PREVIEW_LEN; 1431 if (x > VS_BUF_SZ) 1432 x = VS_BUF_SZ; 1433 ctx->vsc_options.vso_preview_len = x; 1434 break; 1435 1436 case VS_OPT_TTL: 1437 if (*line == 0) { 1438 ctx->vsc_options.vso_req_time = -1; 1439 break; 1440 } 1441 1442 val = strtol(line, &end, 10); 1443 if ((end != (line + strlen(line))) || (val < 0)) 1444 break; 1445 1446 ctx->vsc_options.vso_ttl = val; 1447 ctx->vsc_options.vso_req_time = time(0); 1448 break; 1449 1450 case VS_OPT_ALLOW: 1451 (void) sscanf(line, "%d", &ctx->vsc_options.vso_allow); 1452 break; 1453 1454 case VS_OPT_SERVICE: 1455 (void) strlcpy(ctx->vsc_options.vso_service, line, 1456 VS_SERVICE_SZ); 1457 break; 1458 1459 case VS_OPT_X_DEF_INFO: 1460 (void) strlcpy(ctx->vsc_options.vso_defninfo, line, 1461 VS_DEFN_SZ); 1462 break; 1463 1464 case VS_OPT_METHODS: 1465 if (strstr(line, "RESPMOD") != NULL) 1466 ctx->vsc_options.vso_respmod = 1; 1467 break; 1468 1469 case VS_OPT_ISTAG: 1470 vs_icap_istag_to_scanstamp(line, 1471 ctx->vsc_options.vso_scanstamp); 1472 break; 1473 1474 default: 1475 break; 1476 1477 } 1478 1479 return (1); 1480 } 1481 1482 1483 /* 1484 * vs_icap_resp_istag 1485 * 1486 * Called to handle ISTAG when received in RESPMOD response. 1487 * - populate result->vsr_scanstamp from istag 1488 * - update the scanstamp in vs_options and log the update. 1489 */ 1490 /*ARGSUSED*/ 1491 static int 1492 vs_icap_resp_istag(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1493 { 1494 vs_icap_istag_to_scanstamp(line, ctx->vsc_result->vsr_scanstamp); 1495 1496 /* update the scanstamp in vs_options */ 1497 (void) pthread_mutex_lock(&vs_opt_mutex); 1498 if (vs_icap_compare_se(ctx->vsc_idx, 1499 ctx->vsc_host, ctx->vsc_port) == 0) { 1500 if (strcmp(vs_options[ctx->vsc_idx].vso_scanstamp, 1501 ctx->vsc_result->vsr_scanstamp) != 0) { 1502 (void) strlcpy(vs_options[ctx->vsc_idx].vso_scanstamp, 1503 ctx->vsc_result->vsr_scanstamp, 1504 sizeof (vs_scanstamp_t)); 1505 } 1506 } 1507 (void) pthread_mutex_unlock(&vs_opt_mutex); 1508 1509 return (1); 1510 } 1511 1512 1513 /* 1514 * vs_icap_istag_to_scanstamp 1515 * 1516 * Copies istag into scanstamp, stripping leading and trailing 1517 * quotes '"' from istag. If the istag is invalid (too long) 1518 * scanstamp will be left unchanged. 1519 * 1520 * vs_scanstamp_t is defined to be large enough to hold the 1521 * istag plus a null terminator. 1522 */ 1523 static void 1524 vs_icap_istag_to_scanstamp(char *istag, vs_scanstamp_t scanstamp) 1525 { 1526 char *p = istag; 1527 int len; 1528 1529 /* eliminate preceding '"' */ 1530 if (p[0] == '"') 1531 ++p; 1532 1533 /* eliminate trailing '"' */ 1534 len = strlen(p); 1535 if (p[len - 1] == '"') 1536 --len; 1537 1538 if (len < sizeof (vs_scanstamp_t)) 1539 (void) strlcpy(scanstamp, p, len + 1); 1540 } 1541 1542 1543 /* 1544 * vs_icap_opt_ext 1545 * 1546 * read the transfer preview / transfer complete headers to 1547 * determine which file types can be previewed 1548 */ 1549 static int 1550 vs_icap_opt_ext(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1551 { 1552 vs_options_t *opt = &ctx->vsc_options; 1553 1554 switch (hdr_id) { 1555 case VS_OPT_XFER_PREVIEW: 1556 if (opt->vso_xfer_preview) { 1557 free(opt->vso_xfer_preview); 1558 opt->vso_xfer_preview = 0; 1559 } 1560 if (strstr(line, "*")) { 1561 opt->vso_xfer_how = VS_PREVIEW_ALL; 1562 } else { 1563 opt->vso_xfer_preview = vs_icap_make_strvec 1564 (line, EXT_SEPARATOR); 1565 opt->vso_xfer_how = VS_PREVIEW_LIST; 1566 } 1567 break; 1568 1569 case VS_OPT_XFER_COMPLETE : 1570 if (opt->vso_xfer_complete) { 1571 free(opt->vso_xfer_complete); 1572 opt->vso_xfer_complete = 0; 1573 } 1574 if (strstr(line, "*")) { 1575 opt->vso_xfer_how = VS_PREVIEW_NONE; 1576 } else { 1577 opt->vso_xfer_complete = vs_icap_make_strvec 1578 (line, EXT_SEPARATOR); 1579 opt->vso_xfer_how = VS_PREVIEW_EXCEPT; 1580 } 1581 break; 1582 default: 1583 break; 1584 } 1585 1586 return (1); 1587 } 1588 1589 1590 /* 1591 * vs_icap_resp_infection 1592 * 1593 * read the type, resolution and threat description for each 1594 * reported violation and save in ctx->vsc_result 1595 */ 1596 /*ARGSUSED*/ 1597 static int 1598 vs_icap_resp_infection(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1599 { 1600 char *name, *val; 1601 int i, got = 0; 1602 int type = 0, res = 0; 1603 char *desc = 0; 1604 vs_vrec_t *vr = 0; 1605 1606 for (i = 0; i < VS_INFECTION_FIELDS; i++) { 1607 vs_icap_parse_hdrs('=', line, &name, &val); 1608 1609 switch (i) { 1610 case 0: 1611 if (MATCH(name, "Type")) { 1612 (void) sscanf(val, "%d", &type); 1613 got++; 1614 } 1615 break; 1616 case 1: 1617 if (MATCH(name, "Resolution")) { 1618 (void) sscanf(val, "%d", &res); 1619 got++; 1620 } 1621 break; 1622 case 2: 1623 if (MATCH(name, "Threat")) { 1624 desc = val; 1625 got++; 1626 } 1627 break; 1628 default : 1629 break; 1630 } 1631 1632 if ((line = strstr(val, ";"))) 1633 line++; 1634 } 1635 1636 if (got != VS_INFECTION_FIELDS) 1637 return (0); 1638 1639 /* 1640 * We may have info from an X-Violations-Found record, (which provides 1641 * more complete information). If so, don't destroy what we have. 1642 */ 1643 if ((ctx->vsc_result->vsr_nviolations == 0) || 1644 (ctx->vsc_info.vsi_threat_hdr < VS_RESP_X_INFECTION)) { 1645 vr = &ctx->vsc_result->vsr_vrec[0]; 1646 vr->vr_id = type; 1647 vr->vr_res = res; 1648 (void) strlcpy(vr->vr_desc, desc, VS_DESCRIPTION_MAX); 1649 ctx->vsc_result->vsr_nviolations = 1; 1650 1651 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_INFECTION; 1652 } 1653 1654 return (1); 1655 } 1656 1657 1658 /* 1659 * vs_icap_resp_virus_id 1660 * 1661 * X-Virus-ID is defined as being a shorter alternative to X-Infection-Found. 1662 * If we already have virus information, from either X-Infection-Found or 1663 * X-Violations-Found, it will be more complete, so don't overwrite it with 1664 * the info from X-Virus-ID. 1665 */ 1666 /*ARGSUSED*/ 1667 static int 1668 vs_icap_resp_virus_id(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1669 { 1670 vs_vrec_t *vr = 0; 1671 1672 if (ctx->vsc_result->vsr_nviolations == 0) { 1673 vr = &ctx->vsc_result->vsr_vrec[0]; 1674 vr->vr_id = 0; 1675 vr->vr_res = 0; 1676 (void) strlcpy(vr->vr_desc, line, VS_DESCRIPTION_MAX); 1677 ctx->vsc_result->vsr_nviolations = 1; 1678 1679 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIRUS_ID; 1680 } 1681 1682 return (1); 1683 } 1684 1685 1686 /* 1687 * vs_icap_resp_encap 1688 * 1689 * get the encapsulated header info 1690 */ 1691 /*ARGSUSED*/ 1692 static int 1693 vs_icap_resp_encap(vs_scan_ctx_t *ctx, int hdr_id, char *line) 1694 { 1695 if (strstr(line, "res-hdr")) 1696 ctx->vsc_info.vsi_res_hdr = B_TRUE; 1697 1698 if (strstr(line, "res-body")) 1699 ctx->vsc_info.vsi_res_body = B_TRUE; 1700 1701 return (1); 1702 } 1703 1704 1705 /* 1706 * Utility functions for handling OPTIONS data: vs_options_t 1707 */ 1708 1709 /* 1710 * vs_icap_compare_scanstamp 1711 * compare scanstamp with that stored for engine idx 1712 * 1713 * Returns: 0 - if equal 1714 */ 1715 int 1716 vs_icap_compare_scanstamp(int idx, vs_scanstamp_t scanstamp) 1717 { 1718 int rc; 1719 1720 if (!scanstamp || scanstamp[0] == '\0') 1721 return (-1); 1722 1723 (void) pthread_mutex_lock(&vs_opt_mutex); 1724 rc = strcmp(scanstamp, vs_options[idx].vso_scanstamp); 1725 (void) pthread_mutex_unlock(&vs_opt_mutex); 1726 1727 return (rc); 1728 } 1729 1730 1731 /* 1732 * vs_icap_compare_se 1733 * compare host and port with that stored for engine idx 1734 * 1735 * Returns: 0 - if equal 1736 */ 1737 static int 1738 vs_icap_compare_se(int idx, char *host, int port) 1739 { 1740 if (vs_options[idx].vso_port != port) 1741 return (-1); 1742 1743 if (strcmp(vs_options[idx].vso_host, host) != 0) 1744 return (-1); 1745 1746 return (0); 1747 } 1748 1749 1750 /* 1751 * vs_icap_free_options 1752 * 1753 * Free dynamic parts of vs_options_t: xfer_preview, xfer_complete 1754 */ 1755 static void 1756 vs_icap_free_options(vs_options_t *options) 1757 { 1758 if (options->vso_xfer_preview) 1759 free(options->vso_xfer_preview); 1760 1761 if (options->vso_xfer_complete) 1762 free(options->vso_xfer_complete); 1763 1764 (void) memset(options, 0, sizeof (vs_options_t)); 1765 } 1766 1767 1768 /* 1769 * vs_icap_copy_options 1770 */ 1771 void 1772 vs_icap_copy_options(vs_options_t *to_opt, vs_options_t *from_opt) 1773 { 1774 *to_opt = *from_opt; 1775 1776 if (from_opt->vso_xfer_preview) { 1777 to_opt->vso_xfer_preview = 1778 vs_icap_copy_strvec(from_opt->vso_xfer_preview); 1779 } 1780 1781 if (from_opt->vso_xfer_complete) { 1782 to_opt->vso_xfer_complete = 1783 vs_icap_copy_strvec(from_opt->vso_xfer_complete); 1784 } 1785 } 1786 1787 1788 /* 1789 * vs_icap_update_options 1790 */ 1791 static void 1792 vs_icap_update_options(vs_scan_ctx_t *ctx) 1793 { 1794 int idx = ctx->vsc_idx; 1795 1796 (void) pthread_mutex_lock(&vs_opt_mutex); 1797 1798 if (vs_icap_compare_se(idx, ctx->vsc_host, ctx->vsc_port) == 0) { 1799 vs_icap_free_options(&vs_options[idx]); 1800 vs_icap_copy_options(&vs_options[idx], &ctx->vsc_options); 1801 } 1802 1803 (void) pthread_mutex_unlock(&vs_opt_mutex); 1804 } 1805 1806 1807 /* 1808 * vs_icap_make_strvec 1809 * 1810 * Populate a iovec_t from line, where line is a string of 'sep' 1811 * separated fields. Within the copy of line in the iovec_t each 1812 * field will be null terminated with leading & trailing whitespace 1813 * removed. This allows for fast searching. 1814 * 1815 * The iovec_t itself and the data it points to are allocated 1816 * as a single chunk. 1817 */ 1818 static iovec_t * 1819 vs_icap_make_strvec(char *line, const char *sep) 1820 { 1821 iovec_t *vec; 1822 char *tmp, *ctx; 1823 int datalen, len; 1824 1825 datalen = strlen(line) + 1; 1826 len = sizeof (iovec_t) + datalen; 1827 1828 if ((vec = (iovec_t *)calloc(1, len)) == 0) 1829 return (0); 1830 1831 vec->iov_len = len; 1832 vec->iov_base = (char *)vec + sizeof (iovec_t); 1833 (void) strlcpy(vec->iov_base, line, datalen); 1834 1835 /* tokenize data for easier searching */ 1836 for (tmp = strtok_r(vec->iov_base, sep, &ctx); tmp; 1837 tmp = strtok_r(0, sep, &ctx)) { 1838 } 1839 1840 return (vec); 1841 } 1842 1843 1844 /* 1845 * vs_icap_copy_strvec 1846 * 1847 * allocate and copy strvec 1848 */ 1849 static iovec_t * 1850 vs_icap_copy_strvec(iovec_t *from_vec) 1851 { 1852 iovec_t *to_vec; 1853 1854 if ((to_vec = (iovec_t *)calloc(1, from_vec->iov_len)) == 0) 1855 return (0); 1856 1857 bcopy(from_vec, to_vec, from_vec->iov_len); 1858 to_vec->iov_base = (char *)to_vec + sizeof (iovec_t); 1859 1860 return (to_vec); 1861 } 1862 1863 1864 /* 1865 * vs_icap_check_ext 1866 * 1867 * Returns: 1 - if ext in strvec 1868 * 0 - otherwise 1869 */ 1870 static int 1871 vs_icap_check_ext(char *ext, iovec_t *vec) 1872 { 1873 char *p, *end = (char *)vec + vec->iov_len; 1874 1875 for (p = vec->iov_base; p < end; p += strlen(p) + 1) { 1876 if (MATCH(ext, p)) 1877 return (1); 1878 } 1879 1880 return (0); 1881 } 1882 1883 1884 /* 1885 * vs_icap_resp_str 1886 */ 1887 static char * 1888 vs_icap_resp_str(int rc) 1889 { 1890 vs_resp_msg_t *p = icap_resp; 1891 1892 if (rc < 0) 1893 rc = -rc; 1894 1895 while (p->vsm_rc != VS_RESP_UNKNOWN) { 1896 if (p->vsm_rc == rc) 1897 break; 1898 p++; 1899 } 1900 1901 return (p->vsm_msg); 1902 } 1903 1904 1905 /* 1906 * vs_icap_trimspace 1907 * 1908 * Trims whitespace from both the beginning and end of a string. This 1909 * function alters the string buffer in-place. 1910 * 1911 * Whitespaces found at the beginning of the string are eliminated by 1912 * moving forward the start of the string at the first non-whitespace 1913 * character. 1914 * Whitespace found at the end of the string are overwritten with nulls. 1915 * 1916 */ 1917 static void 1918 vs_icap_trimspace(char *buf) 1919 { 1920 char *p = buf; 1921 char *q = buf; 1922 1923 if (buf == 0) 1924 return; 1925 1926 while (*p && isspace(*p)) 1927 ++p; 1928 1929 while ((*q = *p++) != 0) 1930 ++q; 1931 1932 if (q != buf) { 1933 while ((--q, isspace(*q)) != 0) 1934 *q = '\0'; 1935 } 1936 } 1937 1938 1939 /* 1940 * vs_icap_uri_encode 1941 * 1942 * Encode uri data (eg filename) in accordance with RFC 2396 1943 * 'Illegal' characters should be replaced with %hh, where hh is 1944 * the hex value of the character. For example a space would be 1945 * replaced with %20. 1946 * Filenames are all already UTF-8 encoded. Any UTF-8 octects that 1947 * are 'illegal' characters will be encoded as described above. 1948 * 1949 * Paramaters: data - string to be encoded (NULL terminated) 1950 * buf - output buffer (NULL terminated) 1951 * size - size of output buffer 1952 * 1953 * Returns: strlen of encoded data on success 1954 * -1 size on error (contents of buf undefined) 1955 */ 1956 static int 1957 vs_icap_uri_encode(char *buf, int size, char *data) 1958 { 1959 unsigned char *iptr; 1960 char *optr = buf; 1961 int len = strlen(data); 1962 1963 /* modify the data */ 1964 for (iptr = (unsigned char *)data; *iptr; iptr++) { 1965 if (vs_icap_uri_illegal_char(*iptr)) { 1966 if ((len += 2) >= size) 1967 return (-1); 1968 (void) sprintf(optr, "%%%0x", *iptr); 1969 optr += 3; 1970 } else { 1971 if (len >= size) 1972 return (-1); 1973 *optr++ = *iptr; 1974 } 1975 } 1976 1977 *optr = '\0'; 1978 return (len); 1979 } 1980 1981 1982 /* 1983 * vs_icap_uri_illegal_char 1984 * 1985 * The following us-ascii characters (UTF-8 octets) are 'illegal': 1986 * < > # % " { } | \ ^ [ ] ` space, 0x01 -> 0x1F & 0x7F 1987 * All non us-ascii UTF-8 octets ( >= 0x80) are illegal. 1988 * 1989 * Returns: 1 if character is not allowed in a URI 1990 * 0 otherwise 1991 */ 1992 static int 1993 vs_icap_uri_illegal_char(char c) 1994 { 1995 static const char *uri_illegal_chars = "<>#%\" {}|\\^[]`"; 1996 1997 /* us-ascii non printable characters or non us-ascii */ 1998 if ((c <= 0x1F) || (c >= 0x7F)) 1999 return (1); 2000 2001 /* us-ascii dis-allowed characters */ 2002 if (strchr(uri_illegal_chars, c)) 2003 return (1); 2004 2005 return (0); 2006 2007 } 2008