15440Sjm199354 /*
25440Sjm199354 * CDDL HEADER START
35440Sjm199354 *
45440Sjm199354 * The contents of this file are subject to the terms of the
55440Sjm199354 * Common Development and Distribution License (the "License").
65440Sjm199354 * You may not use this file except in compliance with the License.
75440Sjm199354 *
85440Sjm199354 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95440Sjm199354 * or http://www.opensolaris.org/os/licensing.
105440Sjm199354 * See the License for the specific language governing permissions
115440Sjm199354 * and limitations under the License.
125440Sjm199354 *
135440Sjm199354 * When distributing Covered Code, include this CDDL HEADER in each
145440Sjm199354 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155440Sjm199354 * If applicable, add the following below this CDDL HEADER, with the
165440Sjm199354 * fields enclosed by brackets "[]" replaced with your own identifying
175440Sjm199354 * information: Portions Copyright [yyyy] [name of copyright owner]
185440Sjm199354 *
195440Sjm199354 * CDDL HEADER END
205440Sjm199354 */
215440Sjm199354 /*
22*8696SJoyce.McIntosh@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
235440Sjm199354 * Use is subject to license terms.
245440Sjm199354 */
255440Sjm199354
265440Sjm199354 /*
275440Sjm199354 * Description: Module contains supporting functions used by functions
285440Sjm199354 * defined in vs_svc.c. It also contains some internal(static) functions.
295440Sjm199354 */
305440Sjm199354
315440Sjm199354 #include <stdarg.h>
325440Sjm199354 #include <stdio.h>
335440Sjm199354 #include <stdlib.h>
345440Sjm199354 #include <unistd.h>
355440Sjm199354 #include <errno.h>
365440Sjm199354 #include <time.h>
375440Sjm199354 #include <fcntl.h>
385440Sjm199354 #include <syslog.h>
395440Sjm199354 #include <ctype.h>
405440Sjm199354 #include <strings.h>
415440Sjm199354 #include <string.h>
425440Sjm199354 #include <limits.h>
435440Sjm199354 #include <pthread.h>
445440Sjm199354 #include <sys/types.h>
455440Sjm199354 #include <sys/socket.h>
465440Sjm199354 #include <sys/debug.h>
475440Sjm199354 #include <netinet/in.h>
485440Sjm199354 #include <arpa/inet.h>
495440Sjm199354
505440Sjm199354 #include "vs_incl.h"
515440Sjm199354 #include "vs_icap.h"
525440Sjm199354
535440Sjm199354 /* prototypes of local functions */
545440Sjm199354 static int vs_icap_option_request(vs_scan_ctx_t *);
555440Sjm199354 static int vs_icap_send_option_req(vs_scan_ctx_t *);
565440Sjm199354 static int vs_icap_read_option_resp(vs_scan_ctx_t *);
575440Sjm199354
585440Sjm199354 static int vs_icap_respmod_request(vs_scan_ctx_t *);
595440Sjm199354 static int vs_icap_may_preview(vs_scan_ctx_t *);
605440Sjm199354 static char *vs_icap_find_ext(char *);
615440Sjm199354 static int vs_icap_send_preview(vs_scan_ctx_t *);
625440Sjm199354 static int vs_icap_send_respmod_hdr(vs_scan_ctx_t *, int);
635440Sjm199354 static int vs_icap_create_respmod_hdr(vs_scan_ctx_t *, int);
645440Sjm199354 static int vs_icap_uri_encode(char *, int, char *);
655440Sjm199354 static int vs_icap_uri_illegal_char(char);
665440Sjm199354
675440Sjm199354 static int vs_icap_read_respmod_resp(vs_scan_ctx_t *);
685440Sjm199354 static int vs_icap_read_resp_code(vs_scan_ctx_t *);
695440Sjm199354 static int vs_icap_read_hdr(vs_scan_ctx_t *, vs_hdr_t *, int);
705440Sjm199354
715440Sjm199354 static int vs_icap_set_scan_result(vs_scan_ctx_t *);
725440Sjm199354 static int vs_icap_read_encap_hdr(vs_scan_ctx_t *);
735440Sjm199354 static void vs_icap_read_encap_data(vs_scan_ctx_t *);
745440Sjm199354 static int vs_icap_create_repair_file(vs_scan_ctx_t *);
755440Sjm199354 static int vs_icap_read_resp_body(vs_scan_ctx_t *);
765440Sjm199354 static int vs_icap_read_body_chunk(vs_scan_ctx_t *);
775440Sjm199354
785440Sjm199354 static int vs_icap_send_chunk(vs_scan_ctx_t *, int);
795440Sjm199354 static int vs_icap_send_termination(vs_scan_ctx_t *);
805440Sjm199354 static int vs_icap_readline(vs_scan_ctx_t *, char *, int);
815440Sjm199354
825440Sjm199354 static int vs_icap_write(int, char *, int);
835440Sjm199354 static int vs_icap_read(int, char *, int);
845440Sjm199354
855440Sjm199354 /* process options and respmod headers */
865440Sjm199354 static void vs_icap_parse_hdrs(char, char *, char **, char **);
875440Sjm199354 static int vs_icap_opt_value(vs_scan_ctx_t *, int, char *);
885440Sjm199354 static int vs_icap_opt_ext(vs_scan_ctx_t *, int, char *);
895440Sjm199354 static int vs_icap_resp_violations(vs_scan_ctx_t *, int, char *);
905440Sjm199354 static int vs_icap_resp_violation_rec(vs_scan_ctx_t *, int);
915440Sjm199354 static int vs_icap_resp_infection(vs_scan_ctx_t *, int, char *);
925440Sjm199354 static int vs_icap_resp_virus_id(vs_scan_ctx_t *, int, char *);
935440Sjm199354 static int vs_icap_resp_encap(vs_scan_ctx_t *, int, char *);
945440Sjm199354 static int vs_icap_resp_istag(vs_scan_ctx_t *, int, char *);
955440Sjm199354 static void vs_icap_istag_to_scanstamp(char *, vs_scanstamp_t);
965440Sjm199354
975440Sjm199354 /* Utility functions for handling OPTIONS data: vs_options_t */
985440Sjm199354 static void vs_icap_free_options(vs_options_t *);
995440Sjm199354 static void vs_icap_copy_options(vs_options_t *, vs_options_t *);
1005440Sjm199354 static void vs_icap_update_options(vs_scan_ctx_t *);
1015440Sjm199354 static int vs_icap_compare_se(int, char *, int);
1025440Sjm199354
1035440Sjm199354 static iovec_t *vs_icap_make_strvec(char *, const char *);
1045440Sjm199354 static iovec_t *vs_icap_copy_strvec(iovec_t *);
1055440Sjm199354 static int vs_icap_check_ext(char *, iovec_t *);
1065440Sjm199354 static void vs_icap_trimspace(char *);
1075440Sjm199354
1085440Sjm199354 /* icap response message */
1095440Sjm199354 static char *vs_icap_resp_str(int);
1105440Sjm199354
1115440Sjm199354 /*
1125440Sjm199354 * local variables
1135440Sjm199354 */
1145440Sjm199354
1155440Sjm199354 /* option headers - and handler functions */
1165440Sjm199354 vs_hdr_t option_hdrs[] = {
1175440Sjm199354 { VS_OPT_SERVICE, "Service", vs_icap_opt_value},
1185440Sjm199354 { VS_OPT_ISTAG, "ISTag", vs_icap_opt_value},
1195440Sjm199354 { VS_OPT_METHODS, "Methods", vs_icap_opt_value},
1205440Sjm199354 { VS_OPT_ALLOW, "Allow", vs_icap_opt_value},
1215440Sjm199354 { VS_OPT_PREVIEW, "Preview", vs_icap_opt_value},
1225440Sjm199354 { VS_OPT_XFER_PREVIEW, "Transfer-Preview", vs_icap_opt_ext},
1235440Sjm199354 { VS_OPT_XFER_COMPLETE, "Transfer-Complete", vs_icap_opt_ext},
1245440Sjm199354 { VS_OPT_MAX_CONNECTIONS, "Max-Connections", vs_icap_opt_value},
1255440Sjm199354 { VS_OPT_TTL, "Options-TTL", vs_icap_opt_value},
1265440Sjm199354 { VS_OPT_X_DEF_INFO, "X-Definition-Info", vs_icap_opt_value}
1275440Sjm199354 };
1285440Sjm199354
1295440Sjm199354
1305440Sjm199354 /* resp hdrs - and handler functions */
1315440Sjm199354 vs_hdr_t resp_hdrs[] = {
1325440Sjm199354 { VS_RESP_ENCAPSULATED, "Encapsulated", vs_icap_resp_encap},
1335440Sjm199354 { VS_RESP_ISTAG, "ISTag", vs_icap_resp_istag},
1345440Sjm199354 { VS_RESP_X_VIRUS_ID, "X-Virus-ID", vs_icap_resp_virus_id},
1355440Sjm199354 { VS_RESP_X_INFECTION, "X-Infection-Found", vs_icap_resp_infection},
1365440Sjm199354 { VS_RESP_X_VIOLATIONS, "X-Violations-Found", vs_icap_resp_violations}
1375440Sjm199354 };
1385440Sjm199354
1395440Sjm199354 /* ICAP response code to string mappings */
1405440Sjm199354 vs_resp_msg_t icap_resp[] = {
1415440Sjm199354 { VS_RESP_CONTINUE, "Continue"},
1425440Sjm199354 { VS_RESP_OK, "OK"},
1435440Sjm199354 { VS_RESP_CREATED, "Virus Detected and Repaired"},
1445440Sjm199354 { VS_RESP_NO_CONT_NEEDED, "No Content Necessary"},
1455440Sjm199354 { VS_RESP_BAD_REQ, "Bad Request"},
1465440Sjm199354 { VS_RESP_FORBIDDEN, "File Infected and not repaired"},
1475440Sjm199354 { VS_RESP_NOT_FOUND, "URI not found"},
1485440Sjm199354 { VS_RESP_NOT_ALLOWED, "Method not allowed"},
1495440Sjm199354 { VS_RESP_TIMEOUT, "Request timedout"},
1505440Sjm199354 { VS_RESP_INTERNAL_ERR, "Internal server error"},
1515440Sjm199354 { VS_RESP_NOT_IMPL, "Method not implemented"},
1525440Sjm199354 { VS_RESP_SERV_UNAVAIL, "Service unavailable/overloaded"},
1535440Sjm199354 { VS_RESP_ICAP_VER_UNSUPP, "ICAP version not supported"},
1545440Sjm199354 { VS_RESP_SCAN_ERR, "Error scanning file"},
1555440Sjm199354 { VS_RESP_NO_LICENSE, "No AV License"},
1565440Sjm199354 { VS_RESP_RES_UNAVAIL, "Resource unavailable"},
1575440Sjm199354 { VS_RESP_UNKNOWN, "Unknown Error"},
1585440Sjm199354 };
1595440Sjm199354
1605440Sjm199354 static const char *EXT_SEPARATOR = ",";
1615440Sjm199354 static vs_options_t vs_options[VS_SE_MAX];
1625440Sjm199354 static pthread_mutex_t vs_opt_mutex = PTHREAD_MUTEX_INITIALIZER;
1635440Sjm199354
1645440Sjm199354 /*
1655440Sjm199354 * vs_icap_init
1665440Sjm199354 * initialization performed when daemon is loaded
1675440Sjm199354 */
1685440Sjm199354 void
vs_icap_init()1695440Sjm199354 vs_icap_init()
1705440Sjm199354 {
1715440Sjm199354
1725440Sjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
1735440Sjm199354 (void) memset(vs_options, 0, sizeof (vs_options_t));
1745440Sjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
1755440Sjm199354 }
1765440Sjm199354
1775440Sjm199354
1785440Sjm199354 /*
1795440Sjm199354 * vs_icap_fini
1805440Sjm199354 * cleanup performed when daemon is unloaded
1815440Sjm199354 */
1825440Sjm199354 void
vs_icap_fini()1835440Sjm199354 vs_icap_fini()
1845440Sjm199354 {
1855440Sjm199354 int i;
1865440Sjm199354
1875440Sjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
1885440Sjm199354
1895440Sjm199354 for (i = 0; i < VS_SE_MAX; i++)
1905440Sjm199354 vs_icap_free_options(&vs_options[i]);
1915440Sjm199354
1925440Sjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
1935440Sjm199354 }
1945440Sjm199354
1955440Sjm199354
1965440Sjm199354 /*
1975440Sjm199354 * vs_icap_config
1985440Sjm199354 *
1995931Sjm199354 * When a new VSCAN configuration is specified, this will be
2005440Sjm199354 * called per scan engine. If the scan engine host or port has
2015440Sjm199354 * changed delete the vs_options entry for that scan engine.
2025440Sjm199354 */
2035440Sjm199354 void
vs_icap_config(int idx,char * host,int port)2045440Sjm199354 vs_icap_config(int idx, char *host, int port)
2055440Sjm199354 {
2065440Sjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
2075440Sjm199354 if (vs_icap_compare_se(idx, host, port) != 0) {
2085440Sjm199354 vs_icap_free_options(&vs_options[idx]);
2095440Sjm199354 (void) strlcpy(vs_options[idx].vso_host, host,
2105440Sjm199354 sizeof (vs_options[idx].vso_host));
2115440Sjm199354 vs_options[idx].vso_port = port;
2125440Sjm199354 }
2135440Sjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
2145440Sjm199354 }
2155440Sjm199354
2165440Sjm199354
2175440Sjm199354 /*
2185440Sjm199354 * vs_icap_scan_file
2195440Sjm199354 *
2205440Sjm199354 * Create a context (vs_scan_ctx_t) for the scan operation and initialize
2215440Sjm199354 * its options info. If the scan engine connection's IP or port is different
2225440Sjm199354 * from that held in vs_options the vs_options info is old and should
2235440Sjm199354 * be deleted (vs_icap_free_options). Otherwise, copy the vs_options info
2245440Sjm199354 * into the context.
2255440Sjm199354 * file name, size and decsriptor are also copied into the context
2265440Sjm199354 *
2275440Sjm199354 * Handle the ICAP protocol communication with the external Scan Engine to
2285440Sjm199354 * perform the scan
2295440Sjm199354 * - send an OPTIONS request if necessary
2305440Sjm199354 * - send RESPMOD scan request
2315440Sjm199354 * - process the response and save any cleaned data to file
2325440Sjm199354 *
2335440Sjm199354 * Returns: result->vsr_rc
2345440Sjm199354 */
2355440Sjm199354 int
vs_icap_scan_file(vs_eng_ctx_t * eng,char * devname,char * fname,uint64_t fsize,int flags,vs_result_t * result)2366407Sjm199354 vs_icap_scan_file(vs_eng_ctx_t *eng, char *devname, char *fname,
2375440Sjm199354 uint64_t fsize, int flags, vs_result_t *result)
2385440Sjm199354 {
2395440Sjm199354 vs_scan_ctx_t ctx;
2405440Sjm199354 int fd;
2415440Sjm199354
2426407Sjm199354 fd = open(devname, O_RDONLY);
2436407Sjm199354
2446407Sjm199354 /* retry once on ENOENT as /dev link may not be created yet */
2456407Sjm199354 if ((fd == -1) && (errno == ENOENT)) {
2466407Sjm199354 (void) sleep(1);
2476407Sjm199354 fd = open(devname, O_RDONLY);
2486407Sjm199354 }
2496407Sjm199354
2506407Sjm199354 if (fd == -1) {
2516407Sjm199354 syslog(LOG_ERR, "Failed to open device %s - %s",
2526407Sjm199354 devname, strerror(errno));
2535440Sjm199354 result->vsr_rc = VS_RESULT_ERROR;
2545440Sjm199354 return (result->vsr_rc);
2555440Sjm199354 }
2565440Sjm199354
2575440Sjm199354 /* initialize context */
2585440Sjm199354 (void) memset(&ctx, 0, sizeof (vs_scan_ctx_t));
2596407Sjm199354 ctx.vsc_idx = eng->vse_eidx;
2606407Sjm199354 (void) strlcpy(ctx.vsc_host, eng->vse_host, sizeof (ctx.vsc_host));
2616407Sjm199354 ctx.vsc_port = eng->vse_port;
2626407Sjm199354 ctx.vsc_sockfd = eng->vse_sockfd;
2635440Sjm199354 ctx.vsc_fd = fd;
2645440Sjm199354 ctx.vsc_fname = fname;
2655440Sjm199354 ctx.vsc_fsize = fsize;
2665440Sjm199354 ctx.vsc_flags = flags;
2675440Sjm199354 ctx.vsc_result = result;
2685440Sjm199354
2695440Sjm199354 /* Hooks for future saving of repaired data, not yet in use */
2705440Sjm199354 ctx.vsc_flags |= VS_NO_REPAIR;
2715440Sjm199354 ctx.vsc_repair = 0;
2725440Sjm199354 ctx.vsc_repair_fname = NULL;
2735440Sjm199354 ctx.vsc_repair_fd = -1;
2745440Sjm199354
2755440Sjm199354 /* take a copy of vs_options[idx] if they match the SE specified */
2765440Sjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
2775440Sjm199354 if (vs_icap_compare_se(ctx.vsc_idx, ctx.vsc_host, ctx.vsc_port) == 0) {
2785440Sjm199354 vs_icap_copy_options(&ctx.vsc_options,
2795440Sjm199354 &vs_options[ctx.vsc_idx]);
2805440Sjm199354 }
2815440Sjm199354
2825440Sjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
2835440Sjm199354
2845440Sjm199354 /*
2855440Sjm199354 * default the result to scan engine error.
2865440Sjm199354 * Any non scan-engine errors will reset it to VS_RESULT_ERROR
2875440Sjm199354 */
2885440Sjm199354 result->vsr_rc = VS_RESULT_SE_ERROR;
2895440Sjm199354
2905440Sjm199354 /* do the scan */
2915440Sjm199354 if (vs_icap_option_request(&ctx) == 0)
2925440Sjm199354 (void) vs_icap_respmod_request(&ctx);
2935440Sjm199354
2945440Sjm199354 (void) close(fd);
2955440Sjm199354 vs_icap_free_options(&ctx.vsc_options);
2965440Sjm199354 return (result->vsr_rc);
2975440Sjm199354 }
2985440Sjm199354
2995440Sjm199354
3005440Sjm199354 /* ********************************************************************* */
3015440Sjm199354 /* Local Function definitions */
3025440Sjm199354 /* ********************************************************************* */
3035440Sjm199354
3045440Sjm199354 /*
3055440Sjm199354 * vs_icap_option_request
3065440Sjm199354 *
3075440Sjm199354 * Send ICAP options message and await/process the response.
3085440Sjm199354 *
3095440Sjm199354 * The ICAP options request needs to be sent when a connection
3105440Sjm199354 * is first made with the scan engine. Unless the scan engine
3115440Sjm199354 * determines that the options will never expire (which we save
3125440Sjm199354 * as optione_req_time == -1) the request should be resent after
3135440Sjm199354 * the expiry time specified by the icap server.
3145440Sjm199354 *
3155440Sjm199354 * Returns: 0 - success
3165440Sjm199354 * -1 - error
3175440Sjm199354 */
3185440Sjm199354 static int
vs_icap_option_request(vs_scan_ctx_t * ctx)3195440Sjm199354 vs_icap_option_request(vs_scan_ctx_t *ctx)
3205440Sjm199354 {
3215440Sjm199354 if (ctx->vsc_options.vso_req_time != -1 &&
3225440Sjm199354 ((time(0) - ctx->vsc_options.vso_req_time) >
3235440Sjm199354 ctx->vsc_options.vso_ttl)) {
3245440Sjm199354
3255440Sjm199354 if (vs_icap_send_option_req(ctx) < 0)
3265440Sjm199354 return (-1);
3275440Sjm199354
3285440Sjm199354 if (vs_icap_read_option_resp(ctx) < 0)
3295440Sjm199354 return (-1);
3305440Sjm199354
3315440Sjm199354 vs_icap_update_options(ctx);
3325440Sjm199354 }
3335440Sjm199354
3345440Sjm199354 return (0);
3355440Sjm199354 }
3365440Sjm199354
3375440Sjm199354
3385440Sjm199354 /*
3395440Sjm199354 * vs_icap_send_option_req
3405440Sjm199354 *
3415440Sjm199354 * Send an OPTIONS request to the scan engine
3425440Sjm199354 * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME)
3435440Sjm199354 * after the IP address, otherwise it closes the connection.
3445440Sjm199354 *
3455440Sjm199354 * Returns: 0 - success
3465440Sjm199354 * -1 - error
3475440Sjm199354 */
3485440Sjm199354 static int
vs_icap_send_option_req(vs_scan_ctx_t * ctx)3495440Sjm199354 vs_icap_send_option_req(vs_scan_ctx_t *ctx)
3505440Sjm199354 {
3515440Sjm199354 char my_host_name[MAXHOSTNAMELEN];
3525440Sjm199354 int bufsp = VS_BUF_SZ;
3535440Sjm199354 char *buf0 = ctx->vsc_info.vsi_send_buf;
3545440Sjm199354 char *bufp = buf0;
3555440Sjm199354 int tlen;
3565440Sjm199354
3575440Sjm199354 if (gethostname(my_host_name, sizeof (my_host_name)) != 0) {
3585440Sjm199354 /* non SE error */
3595440Sjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
3605440Sjm199354 return (-1);
3615440Sjm199354 }
3625440Sjm199354
3635440Sjm199354 (void) memset(ctx->vsc_info.vsi_send_buf, 0,
3645440Sjm199354 sizeof (ctx->vsc_info.vsi_send_buf));
3655440Sjm199354
3665440Sjm199354 tlen = snprintf(bufp, bufsp, "OPTIONS icap://%s:%d/%s %s\r\n",
3675440Sjm199354 ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER);
3685440Sjm199354 bufp += tlen;
3695440Sjm199354 bufsp -= tlen;
3705440Sjm199354
3715440Sjm199354 tlen = snprintf(bufp, bufsp, "Host: %s\r\n\r\n", my_host_name);
3725440Sjm199354 bufp += tlen;
3735440Sjm199354
3745440Sjm199354 if (vs_icap_write(ctx->vsc_sockfd, buf0, (bufp - buf0)) < 0)
3755440Sjm199354 return (-1);
3765440Sjm199354
3775440Sjm199354 return (0);
3785440Sjm199354 }
3795440Sjm199354
3805440Sjm199354
3815440Sjm199354 /*
3825440Sjm199354 * vs_icap_read_option_resp
3835440Sjm199354 *
3845440Sjm199354 * Returns: 0 - success
3855440Sjm199354 * -1 - error
3865440Sjm199354 */
3875440Sjm199354 static int
vs_icap_read_option_resp(vs_scan_ctx_t * ctx)3885440Sjm199354 vs_icap_read_option_resp(vs_scan_ctx_t *ctx)
3895440Sjm199354 {
3905440Sjm199354 if (vs_icap_read_resp_code(ctx) < 0)
3915440Sjm199354 return (-1);
3925440Sjm199354
3935440Sjm199354 if (ctx->vsc_info.vsi_icap_rc != VS_RESP_OK) {
3945440Sjm199354 syslog(LOG_ERR, "ICAP protocol error "
3955440Sjm199354 "- unexpected option response: %s",
3965440Sjm199354 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc));
3975440Sjm199354 return (-1);
3985440Sjm199354 }
3995440Sjm199354
4005440Sjm199354 if (vs_icap_read_hdr(ctx, option_hdrs, VS_OPT_HDR_MAX) != 0)
4015440Sjm199354 return (-1);
4025440Sjm199354
4035440Sjm199354 if ((ctx->vsc_options.vso_scanstamp[0] == 0) ||
4045440Sjm199354 (ctx->vsc_options.vso_respmod == 0) ||
4055440Sjm199354 (ctx->vsc_options.vso_req_time == 0)) {
4065440Sjm199354 syslog(LOG_ERR, "ICAP protocol error "
4075440Sjm199354 "- missing or invalid option response hdrs");
4085440Sjm199354 return (-1);
4095440Sjm199354 }
4105440Sjm199354
4115440Sjm199354 return (0);
4125440Sjm199354 }
4135440Sjm199354
4145440Sjm199354
4155440Sjm199354 /*
4165440Sjm199354 * vs_icap_respmod_request
4175440Sjm199354 *
4185440Sjm199354 * Send respmod request and receive and process ICAP response.
4195440Sjm199354 * Preview:
4205440Sjm199354 * ICAP allows for an optional "preview" request. In the option negotiation,
4215440Sjm199354 * the server may ask for a list of types to be previewed, or to be sent
4225440Sjm199354 * complete (no preview).
4235440Sjm199354 * This is advisory. It is ok to skip the preview step, as done when the file
4245440Sjm199354 * is smaller than the preview_len.
4255440Sjm199354 * Process Response:
4265440Sjm199354 * - read and parse the RESPMOD response headers
4275440Sjm199354 * - populate the result structure
4285440Sjm199354 * - read any encapsulated response headers
4295440Sjm199354 * - read any encapsulated response body and, if it represents cleaned
4305440Sjm199354 * file data, overwrite the file with it
4315440Sjm199354 *
4325440Sjm199354 * Returns: 0 - success
4335440Sjm199354 * -1 - error
4345440Sjm199354 */
4355440Sjm199354 static int
vs_icap_respmod_request(vs_scan_ctx_t * ctx)4365440Sjm199354 vs_icap_respmod_request(vs_scan_ctx_t *ctx)
4375440Sjm199354 {
4385440Sjm199354 int rv;
4395440Sjm199354 int bytes_sent, send_len;
4405440Sjm199354 uint64_t resid = ctx->vsc_fsize;
4415440Sjm199354
4425440Sjm199354 if (vs_icap_may_preview(ctx)) {
4435440Sjm199354
4445440Sjm199354 if ((rv = vs_icap_send_preview(ctx)) < 0)
4455440Sjm199354 return (-1);
4465440Sjm199354
4475440Sjm199354 if (vs_icap_read_respmod_resp(ctx) < 0)
4485440Sjm199354 return (-1);
4495440Sjm199354
4505440Sjm199354 if (ctx->vsc_info.vsi_icap_rc != VS_RESP_CONTINUE)
4515440Sjm199354 return (0);
4525440Sjm199354
4535440Sjm199354 bytes_sent = rv;
4545440Sjm199354
4555440Sjm199354 /* If > block (VS_BUF_SZ) remains, re-align to block boundary */
4565440Sjm199354 if ((ctx->vsc_fsize - (uint64_t)bytes_sent) > VS_BUF_SZ) {
4575440Sjm199354 send_len = VS_BUF_SZ - bytes_sent;
4585440Sjm199354 if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0)
4595440Sjm199354 return (-1);
4605440Sjm199354 bytes_sent += rv;
4615440Sjm199354 }
4625440Sjm199354
4635440Sjm199354 resid -= (uint64_t)bytes_sent;
4645440Sjm199354
4655440Sjm199354 } else {
4665440Sjm199354
4675440Sjm199354 if (vs_icap_send_respmod_hdr(ctx, 0) < 0)
4685440Sjm199354 return (-1);
4695440Sjm199354 }
4705440Sjm199354
4715440Sjm199354 /* Send the remainder of the file... */
4725440Sjm199354 while (resid) {
4735440Sjm199354 send_len = (resid > VS_BUF_SZ) ? VS_BUF_SZ : resid;
4745440Sjm199354
4755440Sjm199354 if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0)
4765440Sjm199354 return (-1);
4775440Sjm199354
4785440Sjm199354 if (rv == 0)
4795440Sjm199354 break;
4805440Sjm199354
4815440Sjm199354 resid -= (uint64_t)rv;
4825440Sjm199354 }
4835440Sjm199354
4845440Sjm199354 if (vs_icap_send_termination(ctx) < 0)
4855440Sjm199354 return (-1);
4865440Sjm199354
4875440Sjm199354 /* sending of ICAP request complete */
4885440Sjm199354 if (vs_icap_read_respmod_resp(ctx) < 0)
4895440Sjm199354 return (-1);
4905440Sjm199354
4915440Sjm199354 return (0);
4925440Sjm199354 }
4935440Sjm199354
4945440Sjm199354
4955440Sjm199354 /*
4965440Sjm199354 * vs_icap_may_preview
4975440Sjm199354 *
4985440Sjm199354 * Returns: 1 - preview
4995440Sjm199354 * 0 - don't preview
5005440Sjm199354 */
5015440Sjm199354 static int
vs_icap_may_preview(vs_scan_ctx_t * ctx)5025440Sjm199354 vs_icap_may_preview(vs_scan_ctx_t *ctx)
5035440Sjm199354 {
5045440Sjm199354 int in_list = 0;
5055440Sjm199354 char *ext;
5065440Sjm199354 vs_options_t *opts = &ctx->vsc_options;
5075440Sjm199354
5085440Sjm199354 if (opts->vso_xfer_how == VS_PREVIEW_NONE)
5095440Sjm199354 return (0);
5105440Sjm199354
5115440Sjm199354 /* if the file is smaller than the preview size, don't preview */
5125440Sjm199354 if (ctx->vsc_fsize < (uint64_t)ctx->vsc_options.vso_preview_len)
5135440Sjm199354 return (0);
5145440Sjm199354
5155440Sjm199354 switch (opts->vso_xfer_how) {
5165440Sjm199354 case VS_PREVIEW_ALL:
5175440Sjm199354 return (1);
5185440Sjm199354 case VS_PREVIEW_EXCEPT:
5195440Sjm199354 /* Preview everything except types in xfer_complete */
5205440Sjm199354 if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0)
5215440Sjm199354 in_list = vs_icap_check_ext(ext,
5225440Sjm199354 opts->vso_xfer_complete);
5235440Sjm199354 return ((in_list) ? 0 : 1);
5245440Sjm199354 case VS_PREVIEW_LIST:
5255440Sjm199354 /* Preview only types in the the xfer_preview list */
5265440Sjm199354 if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0)
5275440Sjm199354 in_list = vs_icap_check_ext(ext,
5285440Sjm199354 opts->vso_xfer_preview);
5295440Sjm199354 return ((in_list) ? 1 : 0);
5305440Sjm199354 }
5315440Sjm199354
5325440Sjm199354 return (1);
5335440Sjm199354 }
5345440Sjm199354
5355440Sjm199354
5365440Sjm199354 /*
5375440Sjm199354 * vs_icap_find_ext
5385440Sjm199354 *
5395440Sjm199354 * Returns: ptr to file's extension in fname
5405440Sjm199354 * 0 if no extension
5415440Sjm199354 */
5425440Sjm199354 static char *
vs_icap_find_ext(char * fname)5435440Sjm199354 vs_icap_find_ext(char *fname)
5445440Sjm199354 {
5455440Sjm199354 char *last_comp, *ext_str = 0;
5465440Sjm199354
5475440Sjm199354 if ((last_comp = strrchr(fname, '/')) != 0) {
5485440Sjm199354 last_comp++;
5495440Sjm199354 } else {
5505440Sjm199354 last_comp = fname;
5515440Sjm199354 }
5525440Sjm199354
5535440Sjm199354 /* Get file extension */
5545440Sjm199354 if ((ext_str = strrchr(last_comp, '.')) != 0) {
5555440Sjm199354 ext_str++;
5565440Sjm199354 if (strlen(ext_str) == 0)
5575440Sjm199354 ext_str = 0;
5585440Sjm199354 }
5595440Sjm199354
5605440Sjm199354 return (ext_str);
5615440Sjm199354 }
5625440Sjm199354
5635440Sjm199354
5645440Sjm199354 /*
5655440Sjm199354 * vs_icap_send_preview
5665440Sjm199354 *
5675440Sjm199354 * Returns: bytes sent (preview + alignment)
5685440Sjm199354 * -1 - error
5695440Sjm199354 */
5705440Sjm199354 static int
vs_icap_send_preview(vs_scan_ctx_t * ctx)5715440Sjm199354 vs_icap_send_preview(vs_scan_ctx_t *ctx)
5725440Sjm199354 {
5735440Sjm199354 int preview_len = ctx->vsc_options.vso_preview_len;
5745440Sjm199354 int bytes_sent;
5755440Sjm199354
5765440Sjm199354 /* Send a RESPMOD request with "preview" mode. */
5775440Sjm199354 if (vs_icap_send_respmod_hdr(ctx, 'P') < 0)
5785440Sjm199354 return (-1);
5795440Sjm199354
5805440Sjm199354 if ((bytes_sent = vs_icap_send_chunk(ctx, preview_len)) < 0)
5815440Sjm199354 return (-1);
5825440Sjm199354
5835440Sjm199354 if (bytes_sent < preview_len)
5845440Sjm199354 return (-1);
5855440Sjm199354
5865440Sjm199354 if (vs_icap_send_termination(ctx) < 0)
5875440Sjm199354 return (-1);
5885440Sjm199354
5895440Sjm199354 return (bytes_sent);
5905440Sjm199354 }
5915440Sjm199354
5925440Sjm199354
5935440Sjm199354 /*
5945440Sjm199354 * vs_icap_send_respmod_hdr
5955440Sjm199354 *
5965440Sjm199354 * Create and send the RESPMOD request headers to the scan engine.
5975440Sjm199354 *
5985440Sjm199354 * Returns: 0 success
5995440Sjm199354 * < 0 error
6005440Sjm199354 */
6015440Sjm199354 static int
vs_icap_send_respmod_hdr(vs_scan_ctx_t * ctx,int ispreview)6025440Sjm199354 vs_icap_send_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview)
6035440Sjm199354 {
6045440Sjm199354 int len;
6055440Sjm199354
6065440Sjm199354 if ((len = vs_icap_create_respmod_hdr(ctx, ispreview)) == -1) {
6075440Sjm199354 /* non SE error */
6085440Sjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
6095440Sjm199354 return (-1);
6105440Sjm199354 }
6115440Sjm199354
6125440Sjm199354 /* send the headers */
6135440Sjm199354 if (vs_icap_write(ctx->vsc_sockfd,
6145440Sjm199354 ctx->vsc_info.vsi_send_buf, len) < 0) {
6155440Sjm199354 return (-1);
6165440Sjm199354 }
6175440Sjm199354
6185440Sjm199354 return (0);
6195440Sjm199354 }
6205440Sjm199354
6215440Sjm199354
6225440Sjm199354 /*
6235440Sjm199354 * vs_icap_create_respmod_hdr
6245440Sjm199354 *
6255440Sjm199354 * Create the RESPMOD request headers.
6265440Sjm199354 * - RESPMOD, Host, Allow, [Preview], Encapsulated, encapsulated request hdr,
6275440Sjm199354 * encapsulated response hdr
6285440Sjm199354 * Encapsulated data is sent separately subsequent to vs_icap_send_respmod_hdr,
6295440Sjm199354 * via calls to vs_icap_send_chunk.
6305440Sjm199354 *
6315440Sjm199354 * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME)
6325440Sjm199354 * after the IP address, otherwise it closes the connection.
6335440Sjm199354 *
6345440Sjm199354 * Returns: -1 error
6355440Sjm199354 * length of headers data
6365440Sjm199354 */
6375440Sjm199354 static int
vs_icap_create_respmod_hdr(vs_scan_ctx_t * ctx,int ispreview)6385440Sjm199354 vs_icap_create_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview)
6395440Sjm199354 {
6405440Sjm199354 char my_host_name[MAXHOSTNAMELEN];
6415440Sjm199354 int hbufsp = VS_BUF_SZ;
6425440Sjm199354 char *hbuf0 = ctx->vsc_info.vsi_send_buf;
6435440Sjm199354 char *hbufp = hbuf0;
6445440Sjm199354 char *encap_hdr, *encap_off0, *req_hdr, *res_hdr, *res_body;
6455440Sjm199354 int preview_len = ctx->vsc_options.vso_preview_len;
6465440Sjm199354 int tlen;
6475440Sjm199354
6485440Sjm199354 if (gethostname(my_host_name, sizeof (my_host_name)) != 0) {
6495440Sjm199354 /* non SE error */
6505440Sjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
6515440Sjm199354 return (-1);
6525440Sjm199354 }
6535440Sjm199354
6545440Sjm199354 (void) memset(hbufp, 0, hbufsp);
6555440Sjm199354
6565440Sjm199354 /* First the ICAP "request" part. (at offset 0) */
6575440Sjm199354 tlen = snprintf(hbufp, hbufsp, "RESPMOD icap://%s:%d/%s %s\r\n",
6585440Sjm199354 ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER);
6595440Sjm199354 if (tlen >= hbufsp)
6605440Sjm199354 return (-1);
6615440Sjm199354 hbufp += tlen; hbufsp -= tlen;
6625440Sjm199354
6635440Sjm199354 tlen = snprintf(hbufp, hbufsp, "Host: %s\r\n", my_host_name);
6645440Sjm199354 if (tlen >= hbufsp)
6655440Sjm199354 return (-1);
6665440Sjm199354 hbufp += tlen; hbufsp -= tlen;
6675440Sjm199354
6685440Sjm199354 tlen = snprintf(hbufp, hbufsp, "Allow: 204\r\n");
6695440Sjm199354 if (tlen >= hbufsp)
6705440Sjm199354 return (-1);
6715440Sjm199354 hbufp += tlen; hbufsp -= tlen;
6725440Sjm199354
6735440Sjm199354 if (ispreview) {
6745440Sjm199354 tlen = snprintf(hbufp, hbufsp, "Preview: %d\r\n", preview_len);
6755440Sjm199354 if (tlen >= hbufsp)
6765440Sjm199354 return (-1);
6775440Sjm199354 hbufp += tlen; hbufsp -= tlen;
6785440Sjm199354 }
6795440Sjm199354
6805440Sjm199354 /* Reserve space to later insert encapsulation offsets, & blank line */
6815440Sjm199354 encap_hdr = hbufp;
6825440Sjm199354 tlen = snprintf(hbufp, hbufsp, "%*.*s\r\n\r\n",
6835440Sjm199354 VS_ENCAP_SZ, VS_ENCAP_SZ, "");
6845440Sjm199354 if (tlen >= hbufsp)
6855440Sjm199354 return (-1);
6865440Sjm199354 hbufp += tlen; hbufsp -= tlen;
6875440Sjm199354
6885440Sjm199354 /* "offset zero" for the encapsulated parts that follow */
6895440Sjm199354 encap_off0 = hbufp;
6905440Sjm199354
6915440Sjm199354 /* Encapsulated request header (req_hdr) & blank line */
6925440Sjm199354 req_hdr = hbufp;
6935440Sjm199354 tlen = snprintf(hbufp, hbufsp, "GET http://%s", my_host_name);
6945440Sjm199354 if (tlen >= hbufsp)
6955440Sjm199354 return (-1);
6965440Sjm199354 hbufp += tlen; hbufsp -= tlen;
6975440Sjm199354
6985440Sjm199354 tlen = vs_icap_uri_encode(hbufp, hbufsp, ctx->vsc_fname);
6995440Sjm199354 if (tlen < 0)
7005440Sjm199354 return (-1);
7015440Sjm199354 hbufp += tlen; hbufsp -= tlen;
7025440Sjm199354
7035440Sjm199354 tlen = snprintf(hbufp, hbufsp, " HTTP/1.1\r\n\r\n");
7045440Sjm199354 if (tlen >= hbufsp)
7055440Sjm199354 return (-1);
7065440Sjm199354 hbufp += tlen; hbufsp -= tlen;
7075440Sjm199354
7085440Sjm199354 /* Encapsulated response header (res_hdr) & blank line */
7095440Sjm199354 res_hdr = hbufp;
7105440Sjm199354 tlen = snprintf(hbufp, hbufsp, "HTTP/1.1 200 OK\r\n");
7115440Sjm199354 if (tlen >= hbufsp)
7125440Sjm199354 return (-1);
7135440Sjm199354 hbufp += tlen; hbufsp -= tlen;
7145440Sjm199354
7155440Sjm199354 tlen = snprintf(hbufp, hbufsp, "Transfer-Encoding: chunked\r\n\r\n");
7165440Sjm199354 if (tlen >= hbufsp)
7175440Sjm199354 return (-1);
7185440Sjm199354 hbufp += tlen; hbufsp -= tlen;
7195440Sjm199354
7205440Sjm199354 /* response body section - res-body ("chunked data") */
7215440Sjm199354 res_body = hbufp;
7225440Sjm199354
7235440Sjm199354 /* Insert offsets in encap_hdr */
7245440Sjm199354 tlen = snprintf(encap_hdr, VS_ENCAP_SZ, "Encapsulated: "
7255440Sjm199354 "req-hdr=%d, res-hdr=%d, res-body=%d",
7265440Sjm199354 req_hdr - encap_off0, res_hdr - encap_off0, res_body - encap_off0);
7275440Sjm199354 /* undo the null from snprintf */
7285440Sjm199354 encap_hdr[tlen] = ' ';
7295440Sjm199354
7305440Sjm199354 /* return length */
7315440Sjm199354 return (hbufp - hbuf0);
7325440Sjm199354 }
7335440Sjm199354
7345440Sjm199354
7355440Sjm199354 /*
7365440Sjm199354 * vs_icap_read_respmod_resp
7375440Sjm199354 *
7385440Sjm199354 * Used for both preview and final RESMOD response
7395440Sjm199354 */
7405440Sjm199354 static int
vs_icap_read_respmod_resp(vs_scan_ctx_t * ctx)7415440Sjm199354 vs_icap_read_respmod_resp(vs_scan_ctx_t *ctx)
7425440Sjm199354 {
7435440Sjm199354 if (vs_icap_read_resp_code(ctx) < 0)
7445440Sjm199354 return (-1);
7455440Sjm199354
7465440Sjm199354 if (vs_icap_read_hdr(ctx, resp_hdrs, VS_RESP_HDR_MAX) < 0)
7475440Sjm199354 return (-1);
7485440Sjm199354
7495440Sjm199354 if (ctx->vsc_info.vsi_icap_rc == VS_RESP_CONTINUE) {
7505440Sjm199354 /* A VS_RESP_CONTINUE should not have encapsulated data */
7515440Sjm199354 if ((ctx->vsc_info.vsi_res_hdr) ||
7525440Sjm199354 (ctx->vsc_info.vsi_res_body)) {
7535440Sjm199354 syslog(LOG_ERR, "ICAP protocol error -"
7545440Sjm199354 "- encapsulated data in Continue response");
7555440Sjm199354 return (-1);
7565440Sjm199354 }
7575440Sjm199354 } else {
7585440Sjm199354 if (vs_icap_set_scan_result(ctx) < 0)
7595440Sjm199354 return (-1);
7605440Sjm199354
7615440Sjm199354 if (ctx->vsc_info.vsi_res_hdr) {
7625440Sjm199354 if (vs_icap_read_encap_hdr(ctx) < 0)
7635440Sjm199354 return (-1);
7645440Sjm199354 }
7655440Sjm199354
7665440Sjm199354 if (ctx->vsc_info.vsi_res_body)
7675440Sjm199354 vs_icap_read_encap_data(ctx);
7685440Sjm199354 else if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED)
7695440Sjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN;
7705440Sjm199354 }
7715440Sjm199354
7725440Sjm199354 return (0);
7735440Sjm199354 }
7745440Sjm199354
7755440Sjm199354
7765440Sjm199354 /*
7775440Sjm199354 * vs_icap_read_resp_code
7785440Sjm199354 *
7795440Sjm199354 * Get the response code from the icap response messages
7805440Sjm199354 */
7815440Sjm199354 static int
vs_icap_read_resp_code(vs_scan_ctx_t * ctx)7825440Sjm199354 vs_icap_read_resp_code(vs_scan_ctx_t *ctx)
7835440Sjm199354 {
7845440Sjm199354 char *buf = ctx->vsc_info.vsi_recv_buf;
7855440Sjm199354 int retval;
7865440Sjm199354
7875440Sjm199354 /* Break on error or non-blank line. */
7885440Sjm199354 for (;;) {
7895440Sjm199354 (void) memset(buf, '\0', VS_BUF_SZ);
7905440Sjm199354
7915440Sjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
7925440Sjm199354 return (-1);
7935440Sjm199354
7945440Sjm199354 if (retval && buf[0]) {
7955440Sjm199354 if (MATCH(buf, VS_ICAP_VER)) {
7965440Sjm199354 (void) sscanf(buf+8, "%d",
7975440Sjm199354 &ctx->vsc_info.vsi_icap_rc);
7985440Sjm199354 return (0);
7995440Sjm199354 }
8005440Sjm199354
8015440Sjm199354 syslog(LOG_ERR, "ICAP protocol error -"
8025440Sjm199354 "- expected ICAP/1.0, received %s", buf);
8035440Sjm199354
8045440Sjm199354 return (-1);
8055440Sjm199354 }
8065440Sjm199354 }
8075440Sjm199354 }
8085440Sjm199354
8095440Sjm199354
8105440Sjm199354 /*
8115440Sjm199354 * vs_icap_read_hdr
8125440Sjm199354 *
8135440Sjm199354 * Reads all response headers.
8145440Sjm199354 * As each line is read it is parsed and passed to the appropriate handler.
8155440Sjm199354 *
8165440Sjm199354 * Returns: 0 - success
8175440Sjm199354 * -1 - error
8185440Sjm199354 */
8195440Sjm199354 static int
vs_icap_read_hdr(vs_scan_ctx_t * ctx,vs_hdr_t hdrs[],int num_hdrs)8205440Sjm199354 vs_icap_read_hdr(vs_scan_ctx_t *ctx, vs_hdr_t hdrs[], int num_hdrs)
8215440Sjm199354 {
8225440Sjm199354 char *buf = ctx->vsc_info.vsi_recv_buf;
8235440Sjm199354 int i, retval;
8245440Sjm199354 char *name, *val;
8255440Sjm199354
8265440Sjm199354 /* Break on error or blank line. */
8275440Sjm199354 for (;;) {
8285440Sjm199354 (void) memset(buf, '\0', VS_BUF_SZ);
8295440Sjm199354
8305440Sjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
8315440Sjm199354 return (-1);
8325440Sjm199354
8335440Sjm199354 /* Empty line (CR/LF) normal break */
8345440Sjm199354 if ((retval == 0) || (!buf[0]))
8355440Sjm199354 break;
8365440Sjm199354
8375440Sjm199354 vs_icap_parse_hdrs(':', buf, &name, &val);
8385440Sjm199354
8395440Sjm199354 for (i = 0; i < num_hdrs; i++) {
8405440Sjm199354 if (strcmp(name, hdrs[i].vsh_name) == 0) {
8415440Sjm199354 hdrs[i].vsh_func(ctx, hdrs[i].vsh_id, val);
8425440Sjm199354 break;
8435440Sjm199354 }
8445440Sjm199354 }
8455440Sjm199354 }
8465440Sjm199354
8475440Sjm199354 return ((retval >= 0) ? 0 : -1);
8485440Sjm199354 }
8495440Sjm199354
8505440Sjm199354
8515440Sjm199354 /*
8525440Sjm199354 * vs_icap_set_scan_result
8535440Sjm199354 *
8545440Sjm199354 * Sets the vs_result_t vsr_rc from the icap_resp_code and
8555440Sjm199354 * any violation information in vs_result_t
8565440Sjm199354 *
8575440Sjm199354 * Returns: 0 - success
8585440Sjm199354 * -1 - error
8595440Sjm199354 */
8605440Sjm199354 static int
vs_icap_set_scan_result(vs_scan_ctx_t * ctx)8615440Sjm199354 vs_icap_set_scan_result(vs_scan_ctx_t *ctx)
8625440Sjm199354 {
8635440Sjm199354 int i;
8645440Sjm199354 vs_result_t *result = ctx->vsc_result;
8655440Sjm199354
8665440Sjm199354 if (!result->vsr_scanstamp)
8675440Sjm199354 (void) strlcpy(result->vsr_scanstamp,
8685440Sjm199354 ctx->vsc_options.vso_scanstamp, sizeof (vs_scanstamp_t));
8695440Sjm199354
8705440Sjm199354 switch (ctx->vsc_info.vsi_icap_rc) {
8715440Sjm199354 case VS_RESP_NO_CONT_NEEDED:
8725440Sjm199354 result->vsr_rc = VS_RESULT_CLEAN;
8735440Sjm199354 break;
8745440Sjm199354
8755440Sjm199354 case VS_RESP_OK:
8765440Sjm199354 /* if we have no violations , that means all ok */
8775440Sjm199354 if (result->vsr_nviolations == 0) {
8785440Sjm199354 result->vsr_rc = VS_RESULT_CLEAN;
8795440Sjm199354 break;
8805440Sjm199354 }
8815440Sjm199354
8825440Sjm199354 /* Any infections not repaired? */
8835440Sjm199354 result->vsr_rc = VS_RESULT_CLEANED;
8845440Sjm199354 for (i = 0; i < result->vsr_nviolations; i++) {
8855440Sjm199354 if (result->vsr_vrec[i].vr_res !=
8865440Sjm199354 VS_RES_FILE_REPAIRED) {
8875440Sjm199354 result->vsr_rc = VS_RESULT_FORBIDDEN;
8885440Sjm199354 break;
8895440Sjm199354 }
8905440Sjm199354 }
8915440Sjm199354 break;
8925440Sjm199354
8935440Sjm199354 case VS_RESP_CREATED :
8945440Sjm199354 /* file is repaired */
8955440Sjm199354 result->vsr_rc = VS_RESULT_CLEANED;
8965440Sjm199354 break;
8975440Sjm199354
8985440Sjm199354 case VS_RESP_FORBIDDEN:
8995440Sjm199354 /* file is infected and could not be repaired */
9005440Sjm199354 result->vsr_rc = VS_RESULT_FORBIDDEN;
9015440Sjm199354 break;
9025440Sjm199354
9035440Sjm199354 default:
9045440Sjm199354 syslog(LOG_ERR, "ICAP protocol error "
9055440Sjm199354 "- unsupported scan result: %s",
9065440Sjm199354 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc));
9075440Sjm199354 return (-1);
9085440Sjm199354 }
9095440Sjm199354
9105440Sjm199354 return (0);
9115440Sjm199354 }
9125440Sjm199354
9135440Sjm199354
9145440Sjm199354 /*
9155440Sjm199354 * vs_icap_read_encap_hdr
9165440Sjm199354 *
9175440Sjm199354 * Read the encapsulated response header to determine the length of
9185440Sjm199354 * encapsulated data and, in some cases, to detect the infected state
9195440Sjm199354 * of the file.
9205440Sjm199354 *
9215440Sjm199354 * Use of http response code:
9225440Sjm199354 * Trend IWSS does not return virus information in the RESPMOD response
9235440Sjm199354 * headers unless the OPTIONAL "include X_Infection_Found" checkbox is
9245440Sjm199354 * checked and "disable_infected_url_block=yes" is set in intscan.ini.
9255440Sjm199354 * Thus if we haven't already detected the infected/cleaned status
9265440Sjm199354 * (ie if vsr_rc == VS_RESULT_CLEAN) we attempt to detect the
9275440Sjm199354 * infected/cleaned state of a file from a combination of the ICAP and
9285440Sjm199354 * http resp codes.
9295440Sjm199354 * Here are the response code values that Trend IWSS returns:
9305440Sjm199354 * - clean: icap resp = VS_RESP_NO_CONT_NEEDED
9315440Sjm199354 * - quarantine: icap resp = VS_RESP_OK, http resp = VS_RESP_FORBIDDEN
9325440Sjm199354 * - cleaned: icap resp = VS_RESP_OK, http resp = VS_RESP_OK
9335440Sjm199354 * For all other vendors' scan engines (so far) the infected/cleaned
9345440Sjm199354 * state of the file has already been detected from the RESPMOD
9355440Sjm199354 * response headers.
9365440Sjm199354 */
9375440Sjm199354 static int
vs_icap_read_encap_hdr(vs_scan_ctx_t * ctx)9385440Sjm199354 vs_icap_read_encap_hdr(vs_scan_ctx_t *ctx)
9395440Sjm199354 {
9405440Sjm199354 char *buf = ctx->vsc_info.vsi_recv_buf;
9415440Sjm199354 char *name, *value;
9425440Sjm199354 int retval;
9435440Sjm199354
9445440Sjm199354 /* Break on error or blank line. */
9455440Sjm199354 for (;;) {
9465440Sjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
9475440Sjm199354 return (-1);
9485440Sjm199354
9495440Sjm199354 /* Empty line (CR/LF) normal break */
9505440Sjm199354 if ((retval == 0) || (!buf[0]))
9515440Sjm199354 break;
9525440Sjm199354
9535440Sjm199354 if (MATCH(buf, "HTTP/1.1")) {
9545440Sjm199354 (void) sscanf(buf + 8, "%d",
9555440Sjm199354 &ctx->vsc_info.vsi_http_rc);
9565440Sjm199354 ctx->vsc_info.vsi_html_content = B_TRUE;
9575440Sjm199354
9585440Sjm199354 /* if not yet detected infection, interpret http_rc */
9595440Sjm199354 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEAN) {
9605440Sjm199354 if ((ctx->vsc_info.vsi_icap_rc == VS_RESP_OK) &&
9615440Sjm199354 (ctx->vsc_info.vsi_http_rc == VS_RESP_OK)) {
9625440Sjm199354 ctx->vsc_result->vsr_rc =
9635440Sjm199354 VS_RESULT_CLEANED;
9645440Sjm199354 } else {
9655440Sjm199354 ctx->vsc_result->vsr_rc =
9665440Sjm199354 VS_RESULT_FORBIDDEN;
9675440Sjm199354 }
9685440Sjm199354 }
9695440Sjm199354 } else {
9705440Sjm199354 vs_icap_parse_hdrs(':', buf, &name, &value);
9715440Sjm199354 if (name && (MATCH(name, "Content-Length"))) {
9725440Sjm199354 (void) sscanf(value, "%d",
9735440Sjm199354 &ctx->vsc_info.vsi_content_len);
9745440Sjm199354 }
9755440Sjm199354 }
9765440Sjm199354 }
9775440Sjm199354
9785440Sjm199354 return (0);
9795440Sjm199354 }
9805440Sjm199354
9815440Sjm199354
9825440Sjm199354 /*
9835440Sjm199354 * vs_icap_read_encap_data
9845440Sjm199354 *
9855440Sjm199354 * Read the encapsulated response data.
9865440Sjm199354 *
9875440Sjm199354 * If the response data represents cleaned file data (for an infected file)
9885440Sjm199354 * and VS_NO_REPAIR is not set, open repair file to save the reponse body
9895440Sjm199354 * data in. Set the repair flag in the scan context. The repair flag is used
9905440Sjm199354 * during the processing of the response data. If the flag is set then the
9915440Sjm199354 * data is written to file. If any error occurs which invalidates the repaired
9925440Sjm199354 * data file the repair flag gets reset to 0, and the data will be discarded.
9935440Sjm199354 *
9945440Sjm199354 * The result is reset to VS_RESULT_FORBIDDEN until all of the cleaned data
9955440Sjm199354 * has been successfully received and processed. It is then reset to
9965440Sjm199354 * VS_RESULT_CLEANED.
9975440Sjm199354 *
9985440Sjm199354 * If the data doesn't represent cleaned file data, or we cannot (or don't
9995440Sjm199354 * want to) write the cleaned data to file, the data is discarded (repair flag
10005440Sjm199354 * in ctx == 0).
10015440Sjm199354 */
10025440Sjm199354 static void
vs_icap_read_encap_data(vs_scan_ctx_t * ctx)10035440Sjm199354 vs_icap_read_encap_data(vs_scan_ctx_t *ctx)
10045440Sjm199354 {
10055440Sjm199354 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED) {
10065440Sjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN;
10075440Sjm199354
10085440Sjm199354 if (!(ctx->vsc_flags & VS_NO_REPAIR)) {
10095440Sjm199354 if (vs_icap_create_repair_file(ctx) == 0)
10105440Sjm199354 ctx->vsc_repair = B_TRUE;
10115440Sjm199354 }
10125440Sjm199354 }
10135440Sjm199354
10145440Sjm199354 /*
10155440Sjm199354 * vs_icap_read_resp_body handles errors internally;
10165440Sjm199354 * resets ctx->vsc_repair
10175440Sjm199354 */
10185440Sjm199354 (void) vs_icap_read_resp_body(ctx);
10195440Sjm199354
10205440Sjm199354 if (ctx->vsc_repair_fd != -1) {
10215440Sjm199354 (void) close(ctx->vsc_repair_fd);
10225440Sjm199354
10235440Sjm199354 if (ctx->vsc_repair) {
10245440Sjm199354 /* repair file contains the cleaned data */
10255440Sjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_CLEANED;
10265440Sjm199354 } else {
10275440Sjm199354 /* error occured processing data. Remove repair file */
10285440Sjm199354 (void) unlink(ctx->vsc_repair_fname);
10295440Sjm199354 }
10305440Sjm199354 }
10315440Sjm199354 }
10325440Sjm199354
10335440Sjm199354
10345440Sjm199354 /*
10355440Sjm199354 * vs_icap_create_repair_file
10365440Sjm199354 *
10375440Sjm199354 * Create and open a file to save cleaned data in.
10385440Sjm199354 */
10395440Sjm199354 static int
vs_icap_create_repair_file(vs_scan_ctx_t * ctx)10405440Sjm199354 vs_icap_create_repair_file(vs_scan_ctx_t *ctx)
10415440Sjm199354 {
10425440Sjm199354 if (ctx->vsc_repair_fname == NULL)
10435440Sjm199354 return (-1);
10445440Sjm199354
10455440Sjm199354 if ((ctx->vsc_repair_fd = open(ctx->vsc_repair_fname,
10465440Sjm199354 O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0644)) == -1) {
10475440Sjm199354 return (-1);
10485440Sjm199354 }
10495440Sjm199354
10505440Sjm199354 return (0);
10515440Sjm199354 }
10525440Sjm199354
10535440Sjm199354
10545440Sjm199354 /*
10555440Sjm199354 * vs_icap_read_resp_body
10565440Sjm199354 *
10575440Sjm199354 * Repeatedly call vs_icap_read_body_chunk until it returns:
10585440Sjm199354 * 0 indicating that there's no more data to read or
10595440Sjm199354 * -1 indicating a read error -> reset ctx->vsc_repair 0
10605440Sjm199354 *
10615440Sjm199354 * Returns: 0 success
10625440Sjm199354 * -1 error
10635440Sjm199354 */
10645440Sjm199354 static int
vs_icap_read_resp_body(vs_scan_ctx_t * ctx)10655440Sjm199354 vs_icap_read_resp_body(vs_scan_ctx_t *ctx)
10665440Sjm199354 {
10675440Sjm199354 int retval;
10685440Sjm199354
10695440Sjm199354 while ((retval = vs_icap_read_body_chunk(ctx)) > 0)
10705440Sjm199354 ;
10715440Sjm199354
10725440Sjm199354 if (retval < 0)
10735440Sjm199354 ctx->vsc_repair = B_FALSE;
10745440Sjm199354
10755440Sjm199354 return (retval);
10765440Sjm199354 }
10775440Sjm199354
10785440Sjm199354
10795440Sjm199354 /*
10805440Sjm199354 * vs_icap_read_body_chunk
10815440Sjm199354 *
10825440Sjm199354 * Read the chunk size, then read the chunk of data and write the
10835440Sjm199354 * data to file repair_fd (or discard it).
10845440Sjm199354 * If the data cannot be successfully written to file, set repair
10855440Sjm199354 * flag in ctx to 0, and discard all subsequent data.
10865440Sjm199354 *
10875440Sjm199354 * Returns: chunk size
10885440Sjm199354 * -1 on error
10895440Sjm199354 */
10905440Sjm199354 static int
vs_icap_read_body_chunk(vs_scan_ctx_t * ctx)10915440Sjm199354 vs_icap_read_body_chunk(vs_scan_ctx_t *ctx)
10925440Sjm199354 {
10935440Sjm199354 char *lbuf = ctx->vsc_info.vsi_recv_buf;
10945440Sjm199354 unsigned int chunk_size, resid;
10955440Sjm199354 int rsize;
10965440Sjm199354
10975440Sjm199354 /* Read and parse the chunk size. */
10985440Sjm199354 if ((vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0) ||
10995440Sjm199354 (!sscanf(lbuf, "%x", &chunk_size))) {
11005440Sjm199354 return (-1);
11015440Sjm199354 }
11025440Sjm199354
11035440Sjm199354 /* Read and save/discard chunk */
11045440Sjm199354 resid = chunk_size;
11055440Sjm199354 while (resid) {
11065440Sjm199354 rsize = (resid < VS_BUF_SZ) ? resid : VS_BUF_SZ;
11075440Sjm199354
11085440Sjm199354 if ((rsize = vs_icap_read(ctx->vsc_sockfd, lbuf, rsize)) <= 0)
11095440Sjm199354 return (-1);
11105440Sjm199354
11115440Sjm199354 if (ctx->vsc_repair) {
11125440Sjm199354 if (vs_icap_write(ctx->vsc_repair_fd, lbuf, rsize) < 0)
11135440Sjm199354 ctx->vsc_repair = B_FALSE;
11145440Sjm199354 }
11155440Sjm199354
11165440Sjm199354 resid -= rsize;
11175440Sjm199354 }
11185440Sjm199354
11195440Sjm199354 /* Eat one CR/LF after the data */
11205440Sjm199354 if (vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0)
11215440Sjm199354 return (-1);
11225440Sjm199354
11235440Sjm199354 if (lbuf[0]) {
11245440Sjm199354 syslog(LOG_ERR, "ICAP protocol error - expected blank line");
11255440Sjm199354 return (-1);
11265440Sjm199354 }
11275440Sjm199354
11285440Sjm199354 return (chunk_size);
11295440Sjm199354 }
11305440Sjm199354
11315440Sjm199354
11325440Sjm199354 /* *********************************************************************** */
11335440Sjm199354 /* Utility read, write functions */
11345440Sjm199354 /* *********************************************************************** */
11355440Sjm199354
11365440Sjm199354 /*
11375440Sjm199354 * vs_icap_write
11385440Sjm199354 *
11395440Sjm199354 * Return: 0 if all data successfully written
11405440Sjm199354 * -1 otherwise
11415440Sjm199354 */
11425440Sjm199354 static int
vs_icap_write(int fd,char * buf,int buflen)11435440Sjm199354 vs_icap_write(int fd, char *buf, int buflen)
11445440Sjm199354 {
11455440Sjm199354 char *ptr = buf;
11465440Sjm199354 int resid = buflen;
11475440Sjm199354 int bytes_sent = 0;
11485440Sjm199354
11495440Sjm199354 while (resid > 0) {
11505440Sjm199354 errno = 0;
11516407Sjm199354 bytes_sent = write(fd, ptr, resid);
11525440Sjm199354 if (bytes_sent < 0) {
11535440Sjm199354 if (errno == EINTR)
11545440Sjm199354 continue;
11555440Sjm199354 else
11565440Sjm199354 return (-1);
11575440Sjm199354 }
11585440Sjm199354 resid -= bytes_sent;
11595440Sjm199354 ptr += bytes_sent;
11605440Sjm199354 }
11615440Sjm199354
11625440Sjm199354 return (0);
11635440Sjm199354 }
11645440Sjm199354
11655440Sjm199354
11665440Sjm199354 /*
11675440Sjm199354 * vs_icap_read
11685440Sjm199354 *
11695440Sjm199354 * Returns: bytes_read (== len unless EOF hit before len bytes read)
11705440Sjm199354 * -1 error
11715440Sjm199354 */
11725440Sjm199354 static int
vs_icap_read(int fd,char * buf,int len)11735440Sjm199354 vs_icap_read(int fd, char *buf, int len)
11745440Sjm199354 {
11755440Sjm199354 char *ptr = buf;
11765440Sjm199354 int resid = len;
11775440Sjm199354 int bytes_read = 0;
11785440Sjm199354
11795440Sjm199354 while (resid > 0) {
11805440Sjm199354 errno = 0;
11816407Sjm199354 bytes_read = read(fd, ptr, resid);
11825440Sjm199354 if (bytes_read < 0) {
11835440Sjm199354 if (errno == EINTR)
11845440Sjm199354 continue;
11855440Sjm199354 else
11865440Sjm199354 return (-1);
11875440Sjm199354 }
11885440Sjm199354 resid -= bytes_read;
11895440Sjm199354 ptr += bytes_read;
11905440Sjm199354 }
11915440Sjm199354
11925440Sjm199354 return (len - resid);
11935440Sjm199354 }
11945440Sjm199354
11955440Sjm199354
11965440Sjm199354 /*
11975440Sjm199354 * vs_icap_send_chunk
11985440Sjm199354 *
11995440Sjm199354 * Send a "chunk" of file data, containing:
12005440Sjm199354 * - Length (in hex) CR/NL
12015440Sjm199354 * - [optiona data]
12025440Sjm199354 * - CR/NL
12035440Sjm199354 *
12045440Sjm199354 * Returns: data length sent (not including encapsulation)
12055440Sjm199354 * -1 - error
12065440Sjm199354 */
12075440Sjm199354 static int
vs_icap_send_chunk(vs_scan_ctx_t * ctx,int chunk_len)12085440Sjm199354 vs_icap_send_chunk(vs_scan_ctx_t *ctx, int chunk_len)
12095440Sjm199354 {
12105440Sjm199354 char *hdr = ctx->vsc_info.vsi_send_hdr;
12115440Sjm199354 char *dbuf = ctx->vsc_info.vsi_send_buf;
12125440Sjm199354 char *tail;
12135440Sjm199354 char head[VS_HDR_SZ + 1];
12145440Sjm199354 int nread = 0, hlen, tlen = 2;
12155440Sjm199354
12165440Sjm199354 if (chunk_len > VS_BUF_SZ)
12175440Sjm199354 chunk_len = VS_BUF_SZ;
12185440Sjm199354
12195440Sjm199354 /* Read the data. */
12205440Sjm199354 if ((nread = vs_icap_read(ctx->vsc_fd, dbuf, chunk_len)) < 0)
12215440Sjm199354 return (-1);
12225440Sjm199354
12235440Sjm199354 if (nread > 0) {
12245440Sjm199354 /* wrap data in a header and trailer */
12255440Sjm199354 hlen = snprintf(head, sizeof (head), "%x\r\n", nread);
12265440Sjm199354 hdr += (VS_HDR_SZ - hlen);
12275440Sjm199354 (void) memcpy(hdr, head, hlen);
1228*8696SJoyce.McIntosh@Sun.COM tail = dbuf + nread;
12295440Sjm199354 tail[0] = '\r';
12305440Sjm199354 tail[1] = '\n';
12315440Sjm199354
12325440Sjm199354 if (vs_icap_write(ctx->vsc_sockfd, hdr,
12335440Sjm199354 hlen + nread + tlen) < 0) {
12345440Sjm199354 return (-1);
12355440Sjm199354 }
12365440Sjm199354 }
12375440Sjm199354
12385440Sjm199354 return (nread);
12395440Sjm199354 }
12405440Sjm199354
12415440Sjm199354
12425440Sjm199354 /*
12435440Sjm199354 * vs_icap_send_termination
12445440Sjm199354 *
12455440Sjm199354 * Send 0 length termination to scan engine: "0\r\n\r\n"
12465440Sjm199354 *
12475440Sjm199354 * Returns: 0 - success
12485440Sjm199354 * -1 - error
12495440Sjm199354 */
12505440Sjm199354 static int
vs_icap_send_termination(vs_scan_ctx_t * ctx)12515440Sjm199354 vs_icap_send_termination(vs_scan_ctx_t *ctx)
12525440Sjm199354 {
12535440Sjm199354 if (vs_icap_write(ctx->vsc_sockfd, VS_TERMINATION,
12545440Sjm199354 strlen(VS_TERMINATION)) < 0) {
12555440Sjm199354 return (-1);
12565440Sjm199354 }
12575440Sjm199354
12585440Sjm199354 return (0);
12595440Sjm199354 }
12605440Sjm199354
12615440Sjm199354
12625440Sjm199354 /*
12635440Sjm199354 * vs_icap_readline
12645440Sjm199354 *
12655440Sjm199354 * Read a line of response data from the socket. \n indicates end of line.
12665440Sjm199354 *
12675440Sjm199354 * Returns: bytes read
12685440Sjm199354 * -1 - error
12695440Sjm199354 */
12705440Sjm199354 static int
vs_icap_readline(vs_scan_ctx_t * ctx,char * buf,int buflen)12715440Sjm199354 vs_icap_readline(vs_scan_ctx_t *ctx, char *buf, int buflen)
12725440Sjm199354 {
12735440Sjm199354 char c;
12745440Sjm199354 int i, retval;
12755440Sjm199354
12765440Sjm199354 i = 0;
12775440Sjm199354 for (;;) {
12785440Sjm199354 errno = 0;
12795440Sjm199354 retval = recv(ctx->vsc_sockfd, &c, 1, 0);
12805440Sjm199354
12815931Sjm199354 if (retval < 0 && errno == EINTR)
12825931Sjm199354 continue;
12835440Sjm199354
12845931Sjm199354 if (retval <= 0) {
12856407Sjm199354 if (vscand_get_state() != VS_STATE_SHUTDOWN) {
12866407Sjm199354 syslog(LOG_ERR, "Error receiving data from "
12876407Sjm199354 "Scan Engine: %s", strerror(errno));
12886407Sjm199354 }
12895931Sjm199354 return (-1);
12905931Sjm199354 }
12915440Sjm199354
12925440Sjm199354 buf[i++] = c;
12935440Sjm199354 if (c == '\n')
12945440Sjm199354 break;
12955440Sjm199354
12965440Sjm199354 if (i >= (buflen - 2))
12975440Sjm199354 return (-1);
12985440Sjm199354 }
12995440Sjm199354
13005440Sjm199354 buf[i] = '\0';
13015440Sjm199354
13025440Sjm199354 /* remove preceding and trailing whitespace */
13035440Sjm199354 vs_icap_trimspace(buf);
13045440Sjm199354
13055440Sjm199354 return (i);
13065440Sjm199354 }
13075440Sjm199354
13085440Sjm199354
13095440Sjm199354 /* ************************************************************************ */
13105440Sjm199354 /* HEADER processing */
13115440Sjm199354 /* ************************************************************************ */
13125440Sjm199354
13135440Sjm199354 /*
13145440Sjm199354 * vs_icap_parse_hdrs
13155440Sjm199354 *
13165440Sjm199354 * parse an icap hdr line to find name and value
13175440Sjm199354 */
13185440Sjm199354 static void
vs_icap_parse_hdrs(char delimiter,char * line,char ** name,char ** val)13195440Sjm199354 vs_icap_parse_hdrs(char delimiter, char *line, char **name, char **val)
13205440Sjm199354 {
13215440Sjm199354 char *q = line;
13225440Sjm199354 int line_len;
13235440Sjm199354
13245440Sjm199354 /* strip any spaces */
13255440Sjm199354 while (*q == ' ')
13265440Sjm199354 q++;
13275440Sjm199354
13285440Sjm199354 *name = q;
13295440Sjm199354 *val = 0;
13305440Sjm199354
13315440Sjm199354 /* Empty line is normal termination */
13325440Sjm199354 if ((line_len = strlen(line)) == 0)
13335440Sjm199354 return;
13345440Sjm199354
13355440Sjm199354 if ((q = strchr(line, delimiter)) != 0) {
13365440Sjm199354 *q++ = '\0';
13375440Sjm199354 } else {
13385440Sjm199354 q = line + line_len;
13395440Sjm199354 }
13405440Sjm199354
13415440Sjm199354 /* value part follows spaces */
13425440Sjm199354 while (*q == ' ')
13435440Sjm199354 q++;
13445440Sjm199354
13455440Sjm199354 *val = q;
13465440Sjm199354 }
13475440Sjm199354
13485440Sjm199354
13495440Sjm199354 /*
13505440Sjm199354 * vs_icap_resp_violations
13515440Sjm199354 */
13525440Sjm199354 /*ARGSUSED*/
13535440Sjm199354 static int
vs_icap_resp_violations(vs_scan_ctx_t * ctx,int hdr_id,char * line)13545440Sjm199354 vs_icap_resp_violations(vs_scan_ctx_t *ctx, int hdr_id, char *line)
13555440Sjm199354 {
13565440Sjm199354 int i, rv, vcnt;
13575440Sjm199354
13585440Sjm199354 (void) sscanf(line, "%d", &vcnt);
13595440Sjm199354
13605440Sjm199354 ctx->vsc_result->vsr_nviolations =
13615440Sjm199354 (vcnt > VS_MAX_VIOLATIONS) ? VS_MAX_VIOLATIONS : vcnt;
13625440Sjm199354
13635440Sjm199354 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIOLATIONS;
13645440Sjm199354
13655440Sjm199354 for (i = 0; i < vcnt; i++) {
13665440Sjm199354 if ((rv = vs_icap_resp_violation_rec(ctx, i)) < 0)
13675440Sjm199354 return (rv);
13685440Sjm199354
13695440Sjm199354 }
13705440Sjm199354
13715440Sjm199354 return (1);
13725440Sjm199354 }
13735440Sjm199354
13745440Sjm199354
13755440Sjm199354 /*
13765440Sjm199354 * vs_icap_resp_violation_rec
13775440Sjm199354 *
13785440Sjm199354 * take all violation data (up to VS_MAX_VIOLATIONS) and save it
13795440Sjm199354 * in violation_info.
13805440Sjm199354 * each violation has 4 lines of info: doc name, virus name,
13815440Sjm199354 * virus id and resolution
13825440Sjm199354 */
13835440Sjm199354 static int
vs_icap_resp_violation_rec(vs_scan_ctx_t * ctx,int vr_idx)13845440Sjm199354 vs_icap_resp_violation_rec(vs_scan_ctx_t *ctx, int vr_idx)
13855440Sjm199354 {
13865440Sjm199354 int vline;
13875440Sjm199354 int retval = 0;
13885440Sjm199354 char *buf = ctx->vsc_info.vsi_recv_buf;
13895440Sjm199354 vs_vrec_t *vr;
13905440Sjm199354
13915440Sjm199354 if (vr_idx < VS_MAX_VIOLATIONS) {
13925440Sjm199354 vr = &ctx->vsc_result->vsr_vrec[vr_idx];
13935440Sjm199354 } else {
13945440Sjm199354 vr = 0;
13955440Sjm199354 }
13965440Sjm199354
13975440Sjm199354 for (vline = 0; vline < VS_VIOLATION_LINES; vline++) {
13985440Sjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
13995440Sjm199354 return (-1);
14005440Sjm199354
14015440Sjm199354 /* empty line? */
14025440Sjm199354 if ((retval == 0) || (!buf[0]))
14035440Sjm199354 break;
14045440Sjm199354
14055440Sjm199354 if (vr) {
14065440Sjm199354 switch (vline) {
14075440Sjm199354 case 0: /* doc name */
14085440Sjm199354 break;
14095440Sjm199354 case 1: /* Threat Description */
14105440Sjm199354 (void) strlcpy(vr->vr_desc, buf,
14115440Sjm199354 VS_DESCRIPTION_MAX);
14125440Sjm199354 break;
14135440Sjm199354 case 2: /* Problem ID */
14145440Sjm199354 (void) sscanf(buf, "%d", &vr->vr_id);
14155440Sjm199354 break;
14165440Sjm199354 case 3: /* Resolution */
14175440Sjm199354 (void) sscanf(buf, "%d", &vr->vr_res);
14185440Sjm199354 break;
14195440Sjm199354 }
14205440Sjm199354 }
14215440Sjm199354 }
14225440Sjm199354
14235440Sjm199354 return (1);
14245440Sjm199354 }
14255440Sjm199354
14265440Sjm199354
14275440Sjm199354 /*
14285440Sjm199354 * vs_icap_opt_value
14295440Sjm199354 * given an icap options hdr string, process value
14305440Sjm199354 */
14315440Sjm199354 static int
vs_icap_opt_value(vs_scan_ctx_t * ctx,int hdr_id,char * line)14325440Sjm199354 vs_icap_opt_value(vs_scan_ctx_t *ctx, int hdr_id, char *line)
14335440Sjm199354 {
14345440Sjm199354 int x;
14355440Sjm199354 long val;
14365440Sjm199354 char *end;
14375440Sjm199354
14385440Sjm199354 switch (hdr_id) {
14395440Sjm199354 case VS_OPT_PREVIEW:
14405440Sjm199354 (void) sscanf(line, "%d", &x);
14415440Sjm199354 if (x < VS_MIN_PREVIEW_LEN)
14425440Sjm199354 x = VS_MIN_PREVIEW_LEN;
14435440Sjm199354 if (x > VS_BUF_SZ)
14445440Sjm199354 x = VS_BUF_SZ;
14455440Sjm199354 ctx->vsc_options.vso_preview_len = x;
14465440Sjm199354 break;
14475440Sjm199354
14485440Sjm199354 case VS_OPT_TTL:
14495440Sjm199354 if (*line == 0) {
14505440Sjm199354 ctx->vsc_options.vso_req_time = -1;
14515440Sjm199354 break;
14525440Sjm199354 }
14535440Sjm199354
14545440Sjm199354 val = strtol(line, &end, 10);
14555440Sjm199354 if ((end != (line + strlen(line))) || (val < 0))
14565440Sjm199354 break;
14575440Sjm199354
14585440Sjm199354 ctx->vsc_options.vso_ttl = val;
14595440Sjm199354 ctx->vsc_options.vso_req_time = time(0);
14605440Sjm199354 break;
14615440Sjm199354
14625440Sjm199354 case VS_OPT_ALLOW:
14635440Sjm199354 (void) sscanf(line, "%d", &ctx->vsc_options.vso_allow);
14645440Sjm199354 break;
14655440Sjm199354
14665440Sjm199354 case VS_OPT_SERVICE:
14675440Sjm199354 (void) strlcpy(ctx->vsc_options.vso_service, line,
14685440Sjm199354 VS_SERVICE_SZ);
14695440Sjm199354 break;
14705440Sjm199354
14715440Sjm199354 case VS_OPT_X_DEF_INFO:
14725440Sjm199354 (void) strlcpy(ctx->vsc_options.vso_defninfo, line,
14735440Sjm199354 VS_DEFN_SZ);
14745440Sjm199354 break;
14755440Sjm199354
14765440Sjm199354 case VS_OPT_METHODS:
14775440Sjm199354 if (strstr(line, "RESPMOD") != NULL)
14785440Sjm199354 ctx->vsc_options.vso_respmod = 1;
14795440Sjm199354 break;
14805440Sjm199354
14815440Sjm199354 case VS_OPT_ISTAG:
14825440Sjm199354 vs_icap_istag_to_scanstamp(line,
14835440Sjm199354 ctx->vsc_options.vso_scanstamp);
14845440Sjm199354 break;
14855440Sjm199354
14865440Sjm199354 default:
14875440Sjm199354 break;
14885440Sjm199354
14895440Sjm199354 }
14905440Sjm199354
14915440Sjm199354 return (1);
14925440Sjm199354 }
14935440Sjm199354
14945440Sjm199354
14955440Sjm199354 /*
14965440Sjm199354 * vs_icap_resp_istag
14975440Sjm199354 *
14985440Sjm199354 * Called to handle ISTAG when received in RESPMOD response.
14995440Sjm199354 * - populate result->vsr_scanstamp from istag
15005440Sjm199354 * - update the scanstamp in vs_options and log the update.
15015440Sjm199354 */
15025440Sjm199354 /*ARGSUSED*/
15035440Sjm199354 static int
vs_icap_resp_istag(vs_scan_ctx_t * ctx,int hdr_id,char * line)15045440Sjm199354 vs_icap_resp_istag(vs_scan_ctx_t *ctx, int hdr_id, char *line)
15055440Sjm199354 {
15065440Sjm199354 vs_icap_istag_to_scanstamp(line, ctx->vsc_result->vsr_scanstamp);
15075440Sjm199354
15085440Sjm199354 /* update the scanstamp in vs_options */
15095440Sjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
15105440Sjm199354 if (vs_icap_compare_se(ctx->vsc_idx,
15115440Sjm199354 ctx->vsc_host, ctx->vsc_port) == 0) {
15125440Sjm199354 if (strcmp(vs_options[ctx->vsc_idx].vso_scanstamp,
15135440Sjm199354 ctx->vsc_result->vsr_scanstamp) != 0) {
15145440Sjm199354 (void) strlcpy(vs_options[ctx->vsc_idx].vso_scanstamp,
15155440Sjm199354 ctx->vsc_result->vsr_scanstamp,
15165440Sjm199354 sizeof (vs_scanstamp_t));
15175440Sjm199354 }
15185440Sjm199354 }
15195440Sjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
15205440Sjm199354
15215440Sjm199354 return (1);
15225440Sjm199354 }
15235440Sjm199354
15245440Sjm199354
15255440Sjm199354 /*
15265440Sjm199354 * vs_icap_istag_to_scanstamp
15275440Sjm199354 *
15285440Sjm199354 * Copies istag into scanstamp, stripping leading and trailing
15295440Sjm199354 * quotes '"' from istag. If the istag is invalid (too long)
15305440Sjm199354 * scanstamp will be left unchanged.
15315440Sjm199354 *
15325440Sjm199354 * vs_scanstamp_t is defined to be large enough to hold the
15335440Sjm199354 * istag plus a null terminator.
15345440Sjm199354 */
15355440Sjm199354 static void
vs_icap_istag_to_scanstamp(char * istag,vs_scanstamp_t scanstamp)15365440Sjm199354 vs_icap_istag_to_scanstamp(char *istag, vs_scanstamp_t scanstamp)
15375440Sjm199354 {
15385440Sjm199354 char *p = istag;
15395440Sjm199354 int len;
15405440Sjm199354
15415440Sjm199354 /* eliminate preceding '"' */
15425440Sjm199354 if (p[0] == '"')
15435440Sjm199354 ++p;
15445440Sjm199354
15455440Sjm199354 /* eliminate trailing '"' */
15465440Sjm199354 len = strlen(p);
15475440Sjm199354 if (p[len - 1] == '"')
15485440Sjm199354 --len;
15495440Sjm199354
15505440Sjm199354 if (len < sizeof (vs_scanstamp_t))
15515440Sjm199354 (void) strlcpy(scanstamp, p, len + 1);
15525440Sjm199354 }
15535440Sjm199354
15545440Sjm199354
15555440Sjm199354 /*
15565440Sjm199354 * vs_icap_opt_ext
15575440Sjm199354 *
15585440Sjm199354 * read the transfer preview / transfer complete headers to
15595440Sjm199354 * determine which file types can be previewed
15605440Sjm199354 */
15615440Sjm199354 static int
vs_icap_opt_ext(vs_scan_ctx_t * ctx,int hdr_id,char * line)15625440Sjm199354 vs_icap_opt_ext(vs_scan_ctx_t *ctx, int hdr_id, char *line)
15635440Sjm199354 {
15645440Sjm199354 vs_options_t *opt = &ctx->vsc_options;
15655440Sjm199354
15665440Sjm199354 switch (hdr_id) {
15675440Sjm199354 case VS_OPT_XFER_PREVIEW:
15685440Sjm199354 if (opt->vso_xfer_preview) {
15695440Sjm199354 free(opt->vso_xfer_preview);
15705440Sjm199354 opt->vso_xfer_preview = 0;
15715440Sjm199354 }
15725440Sjm199354 if (strstr(line, "*")) {
15735440Sjm199354 opt->vso_xfer_how = VS_PREVIEW_ALL;
15745440Sjm199354 } else {
15755440Sjm199354 opt->vso_xfer_preview = vs_icap_make_strvec
15765440Sjm199354 (line, EXT_SEPARATOR);
15775440Sjm199354 opt->vso_xfer_how = VS_PREVIEW_LIST;
15785440Sjm199354 }
15795440Sjm199354 break;
15805440Sjm199354
15815440Sjm199354 case VS_OPT_XFER_COMPLETE :
15825440Sjm199354 if (opt->vso_xfer_complete) {
15835440Sjm199354 free(opt->vso_xfer_complete);
15845440Sjm199354 opt->vso_xfer_complete = 0;
15855440Sjm199354 }
15865440Sjm199354 if (strstr(line, "*")) {
15875440Sjm199354 opt->vso_xfer_how = VS_PREVIEW_NONE;
15885440Sjm199354 } else {
15895440Sjm199354 opt->vso_xfer_complete = vs_icap_make_strvec
15905440Sjm199354 (line, EXT_SEPARATOR);
15915440Sjm199354 opt->vso_xfer_how = VS_PREVIEW_EXCEPT;
15925440Sjm199354 }
15935440Sjm199354 break;
15945440Sjm199354 default:
15955440Sjm199354 break;
15965440Sjm199354 }
15975440Sjm199354
15985440Sjm199354 return (1);
15995440Sjm199354 }
16005440Sjm199354
16015440Sjm199354
16025440Sjm199354 /*
16035440Sjm199354 * vs_icap_resp_infection
16045440Sjm199354 *
16055440Sjm199354 * read the type, resolution and threat description for each
16065440Sjm199354 * reported violation and save in ctx->vsc_result
16075440Sjm199354 */
16085440Sjm199354 /*ARGSUSED*/
16095440Sjm199354 static int
vs_icap_resp_infection(vs_scan_ctx_t * ctx,int hdr_id,char * line)16105440Sjm199354 vs_icap_resp_infection(vs_scan_ctx_t *ctx, int hdr_id, char *line)
16115440Sjm199354 {
16125440Sjm199354 char *name, *val;
16135440Sjm199354 int i, got = 0;
16145440Sjm199354 int type = 0, res = 0;
16155440Sjm199354 char *desc = 0;
16165440Sjm199354 vs_vrec_t *vr = 0;
16175440Sjm199354
16185440Sjm199354 for (i = 0; i < VS_INFECTION_FIELDS; i++) {
16195440Sjm199354 vs_icap_parse_hdrs('=', line, &name, &val);
16205440Sjm199354
16215440Sjm199354 switch (i) {
16225440Sjm199354 case 0:
16235440Sjm199354 if (MATCH(name, "Type")) {
16245440Sjm199354 (void) sscanf(val, "%d", &type);
16255440Sjm199354 got++;
16265440Sjm199354 }
16275440Sjm199354 break;
16285440Sjm199354 case 1:
16295440Sjm199354 if (MATCH(name, "Resolution")) {
16305440Sjm199354 (void) sscanf(val, "%d", &res);
16315440Sjm199354 got++;
16325440Sjm199354 }
16335440Sjm199354 break;
16345440Sjm199354 case 2:
16355440Sjm199354 if (MATCH(name, "Threat")) {
16365440Sjm199354 desc = val;
16375440Sjm199354 got++;
16385440Sjm199354 }
16395440Sjm199354 break;
16405440Sjm199354 default :
16415440Sjm199354 break;
16425440Sjm199354 }
16435440Sjm199354
16445440Sjm199354 if ((line = strstr(val, ";")))
16455440Sjm199354 line++;
16465440Sjm199354 }
16475440Sjm199354
16485440Sjm199354 if (got != VS_INFECTION_FIELDS)
16495440Sjm199354 return (0);
16505440Sjm199354
16515440Sjm199354 /*
16525440Sjm199354 * We may have info from an X-Violations-Found record, (which provides
16535440Sjm199354 * more complete information). If so, don't destroy what we have.
16545440Sjm199354 */
16555440Sjm199354 if ((ctx->vsc_result->vsr_nviolations == 0) ||
16565440Sjm199354 (ctx->vsc_info.vsi_threat_hdr < VS_RESP_X_INFECTION)) {
16575440Sjm199354 vr = &ctx->vsc_result->vsr_vrec[0];
16585440Sjm199354 vr->vr_id = type;
16595440Sjm199354 vr->vr_res = res;
16605440Sjm199354 (void) strlcpy(vr->vr_desc, desc, VS_DESCRIPTION_MAX);
16615440Sjm199354 ctx->vsc_result->vsr_nviolations = 1;
16625440Sjm199354
16635440Sjm199354 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_INFECTION;
16645440Sjm199354 }
16655440Sjm199354
16665440Sjm199354 return (1);
16675440Sjm199354 }
16685440Sjm199354
16695440Sjm199354
16705440Sjm199354 /*
16715440Sjm199354 * vs_icap_resp_virus_id
16725440Sjm199354 *
16735440Sjm199354 * X-Virus-ID is defined as being a shorter alternative to X-Infection-Found.
16745440Sjm199354 * If we already have virus information, from either X-Infection-Found or
16755440Sjm199354 * X-Violations-Found, it will be more complete, so don't overwrite it with
16765440Sjm199354 * the info from X-Virus-ID.
16775440Sjm199354 */
16785440Sjm199354 /*ARGSUSED*/
16795440Sjm199354 static int
vs_icap_resp_virus_id(vs_scan_ctx_t * ctx,int hdr_id,char * line)16805440Sjm199354 vs_icap_resp_virus_id(vs_scan_ctx_t *ctx, int hdr_id, char *line)
16815440Sjm199354 {
16825440Sjm199354 vs_vrec_t *vr = 0;
16835440Sjm199354
16845440Sjm199354 if (ctx->vsc_result->vsr_nviolations == 0) {
16855440Sjm199354 vr = &ctx->vsc_result->vsr_vrec[0];
16865440Sjm199354 vr->vr_id = 0;
16875440Sjm199354 vr->vr_res = 0;
16885440Sjm199354 (void) strlcpy(vr->vr_desc, line, VS_DESCRIPTION_MAX);
16895440Sjm199354 ctx->vsc_result->vsr_nviolations = 1;
16905440Sjm199354
16915440Sjm199354 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIRUS_ID;
16925440Sjm199354 }
16935440Sjm199354
16945440Sjm199354 return (1);
16955440Sjm199354 }
16965440Sjm199354
16975440Sjm199354
16985440Sjm199354 /*
16995440Sjm199354 * vs_icap_resp_encap
17005440Sjm199354 *
17015440Sjm199354 * get the encapsulated header info
17025440Sjm199354 */
17035440Sjm199354 /*ARGSUSED*/
17045440Sjm199354 static int
vs_icap_resp_encap(vs_scan_ctx_t * ctx,int hdr_id,char * line)17055440Sjm199354 vs_icap_resp_encap(vs_scan_ctx_t *ctx, int hdr_id, char *line)
17065440Sjm199354 {
17075440Sjm199354 if (strstr(line, "res-hdr"))
17085440Sjm199354 ctx->vsc_info.vsi_res_hdr = B_TRUE;
17095440Sjm199354
17105440Sjm199354 if (strstr(line, "res-body"))
17115440Sjm199354 ctx->vsc_info.vsi_res_body = B_TRUE;
17125440Sjm199354
17135440Sjm199354 return (1);
17145440Sjm199354 }
17155440Sjm199354
17165440Sjm199354
17175440Sjm199354 /*
17185440Sjm199354 * Utility functions for handling OPTIONS data: vs_options_t
17195440Sjm199354 */
17205440Sjm199354
17215440Sjm199354 /*
17225440Sjm199354 * vs_icap_compare_scanstamp
17235440Sjm199354 * compare scanstamp with that stored for engine idx
17245440Sjm199354 *
17255440Sjm199354 * Returns: 0 - if equal
17265440Sjm199354 */
17275440Sjm199354 int
vs_icap_compare_scanstamp(int idx,vs_scanstamp_t scanstamp)17285440Sjm199354 vs_icap_compare_scanstamp(int idx, vs_scanstamp_t scanstamp)
17295440Sjm199354 {
17305440Sjm199354 int rc;
17315440Sjm199354
17325440Sjm199354 if (!scanstamp || scanstamp[0] == '\0')
17335440Sjm199354 return (-1);
17345440Sjm199354
17355440Sjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
17365440Sjm199354 rc = strcmp(scanstamp, vs_options[idx].vso_scanstamp);
17375440Sjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
17385440Sjm199354
17395440Sjm199354 return (rc);
17405440Sjm199354 }
17415440Sjm199354
17425440Sjm199354
17435440Sjm199354 /*
17445440Sjm199354 * vs_icap_compare_se
17455440Sjm199354 * compare host and port with that stored for engine idx
17465440Sjm199354 *
17475440Sjm199354 * Returns: 0 - if equal
17485440Sjm199354 */
17495440Sjm199354 static int
vs_icap_compare_se(int idx,char * host,int port)17505440Sjm199354 vs_icap_compare_se(int idx, char *host, int port)
17515440Sjm199354 {
17525440Sjm199354 if (vs_options[idx].vso_port != port)
17535440Sjm199354 return (-1);
17545440Sjm199354
17555440Sjm199354 if (strcmp(vs_options[idx].vso_host, host) != 0)
17565440Sjm199354 return (-1);
17575440Sjm199354
17585440Sjm199354 return (0);
17595440Sjm199354 }
17605440Sjm199354
17615440Sjm199354
17625440Sjm199354 /*
17635440Sjm199354 * vs_icap_free_options
17645440Sjm199354 *
17655440Sjm199354 * Free dynamic parts of vs_options_t: xfer_preview, xfer_complete
17665440Sjm199354 */
17675440Sjm199354 static void
vs_icap_free_options(vs_options_t * options)17685440Sjm199354 vs_icap_free_options(vs_options_t *options)
17695440Sjm199354 {
17705440Sjm199354 if (options->vso_xfer_preview)
17715440Sjm199354 free(options->vso_xfer_preview);
17725440Sjm199354
17735440Sjm199354 if (options->vso_xfer_complete)
17745440Sjm199354 free(options->vso_xfer_complete);
17755440Sjm199354
17765440Sjm199354 (void) memset(options, 0, sizeof (vs_options_t));
17775440Sjm199354 }
17785440Sjm199354
17795440Sjm199354
17805440Sjm199354 /*
17815440Sjm199354 * vs_icap_copy_options
17825440Sjm199354 */
17835440Sjm199354 void
vs_icap_copy_options(vs_options_t * to_opt,vs_options_t * from_opt)17845440Sjm199354 vs_icap_copy_options(vs_options_t *to_opt, vs_options_t *from_opt)
17855440Sjm199354 {
17865440Sjm199354 *to_opt = *from_opt;
17875440Sjm199354
17885440Sjm199354 if (from_opt->vso_xfer_preview) {
17895440Sjm199354 to_opt->vso_xfer_preview =
17905440Sjm199354 vs_icap_copy_strvec(from_opt->vso_xfer_preview);
17915440Sjm199354 }
17925440Sjm199354
17935440Sjm199354 if (from_opt->vso_xfer_complete) {
17945440Sjm199354 to_opt->vso_xfer_complete =
17955440Sjm199354 vs_icap_copy_strvec(from_opt->vso_xfer_complete);
17965440Sjm199354 }
17975440Sjm199354 }
17985440Sjm199354
17995440Sjm199354
18005440Sjm199354 /*
18015440Sjm199354 * vs_icap_update_options
18025440Sjm199354 */
18035440Sjm199354 static void
vs_icap_update_options(vs_scan_ctx_t * ctx)18045440Sjm199354 vs_icap_update_options(vs_scan_ctx_t *ctx)
18055440Sjm199354 {
18065440Sjm199354 int idx = ctx->vsc_idx;
18075440Sjm199354
18085440Sjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
18095440Sjm199354
18105440Sjm199354 if (vs_icap_compare_se(idx, ctx->vsc_host, ctx->vsc_port) == 0) {
18115440Sjm199354 vs_icap_free_options(&vs_options[idx]);
18125440Sjm199354 vs_icap_copy_options(&vs_options[idx], &ctx->vsc_options);
18135440Sjm199354 }
18145440Sjm199354
18155440Sjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
18165440Sjm199354 }
18175440Sjm199354
18185440Sjm199354
18195440Sjm199354 /*
18205440Sjm199354 * vs_icap_make_strvec
18215440Sjm199354 *
18225440Sjm199354 * Populate a iovec_t from line, where line is a string of 'sep'
18235440Sjm199354 * separated fields. Within the copy of line in the iovec_t each
18245440Sjm199354 * field will be null terminated with leading & trailing whitespace
18255440Sjm199354 * removed. This allows for fast searching.
18265440Sjm199354 *
18275440Sjm199354 * The iovec_t itself and the data it points to are allocated
18285440Sjm199354 * as a single chunk.
18295440Sjm199354 */
18305440Sjm199354 static iovec_t *
vs_icap_make_strvec(char * line,const char * sep)18315440Sjm199354 vs_icap_make_strvec(char *line, const char *sep)
18325440Sjm199354 {
18335440Sjm199354 iovec_t *vec;
18345440Sjm199354 char *tmp, *ctx;
18355440Sjm199354 int datalen, len;
18365440Sjm199354
18375440Sjm199354 datalen = strlen(line) + 1;
18385440Sjm199354 len = sizeof (iovec_t) + datalen;
18395440Sjm199354
18405440Sjm199354 if ((vec = (iovec_t *)calloc(1, len)) == 0)
18415440Sjm199354 return (0);
18425440Sjm199354
18435440Sjm199354 vec->iov_len = len;
18445440Sjm199354 vec->iov_base = (char *)vec + sizeof (iovec_t);
18455440Sjm199354 (void) strlcpy(vec->iov_base, line, datalen);
18465440Sjm199354
18475440Sjm199354 /* tokenize data for easier searching */
18485440Sjm199354 for (tmp = strtok_r(vec->iov_base, sep, &ctx); tmp;
18495440Sjm199354 tmp = strtok_r(0, sep, &ctx)) {
18505440Sjm199354 }
18515440Sjm199354
18525440Sjm199354 return (vec);
18535440Sjm199354 }
18545440Sjm199354
18555440Sjm199354
18565440Sjm199354 /*
18575440Sjm199354 * vs_icap_copy_strvec
18585440Sjm199354 *
18595440Sjm199354 * allocate and copy strvec
18605440Sjm199354 */
18615440Sjm199354 static iovec_t *
vs_icap_copy_strvec(iovec_t * from_vec)18625440Sjm199354 vs_icap_copy_strvec(iovec_t *from_vec)
18635440Sjm199354 {
18645440Sjm199354 iovec_t *to_vec;
18655440Sjm199354
18665440Sjm199354 if ((to_vec = (iovec_t *)calloc(1, from_vec->iov_len)) == 0)
18675440Sjm199354 return (0);
18685440Sjm199354
18695440Sjm199354 bcopy(from_vec, to_vec, from_vec->iov_len);
18705440Sjm199354 to_vec->iov_base = (char *)to_vec + sizeof (iovec_t);
18715440Sjm199354
18725440Sjm199354 return (to_vec);
18735440Sjm199354 }
18745440Sjm199354
18755440Sjm199354
18765440Sjm199354 /*
18775440Sjm199354 * vs_icap_check_ext
18785440Sjm199354 *
18795440Sjm199354 * Returns: 1 - if ext in strvec
18805440Sjm199354 * 0 - otherwise
18815440Sjm199354 */
18825440Sjm199354 static int
vs_icap_check_ext(char * ext,iovec_t * vec)18835440Sjm199354 vs_icap_check_ext(char *ext, iovec_t *vec)
18845440Sjm199354 {
18855440Sjm199354 char *p, *end = (char *)vec + vec->iov_len;
18865440Sjm199354
18875440Sjm199354 for (p = vec->iov_base; p < end; p += strlen(p) + 1) {
18885440Sjm199354 if (MATCH(ext, p))
18895440Sjm199354 return (1);
18905440Sjm199354 }
18915440Sjm199354
18925440Sjm199354 return (0);
18935440Sjm199354 }
18945440Sjm199354
18955440Sjm199354
18965440Sjm199354 /*
18975440Sjm199354 * vs_icap_resp_str
18985440Sjm199354 */
18995440Sjm199354 static char *
vs_icap_resp_str(int rc)19005440Sjm199354 vs_icap_resp_str(int rc)
19015440Sjm199354 {
19025440Sjm199354 vs_resp_msg_t *p = icap_resp;
19035440Sjm199354
19045440Sjm199354 if (rc < 0)
19055440Sjm199354 rc = -rc;
19065440Sjm199354
19075440Sjm199354 while (p->vsm_rc != VS_RESP_UNKNOWN) {
19085440Sjm199354 if (p->vsm_rc == rc)
19095440Sjm199354 break;
19105440Sjm199354 p++;
19115440Sjm199354 }
19125440Sjm199354
19135440Sjm199354 return (p->vsm_msg);
19145440Sjm199354 }
19155440Sjm199354
19165440Sjm199354
19175440Sjm199354 /*
19185440Sjm199354 * vs_icap_trimspace
19195440Sjm199354 *
19205440Sjm199354 * Trims whitespace from both the beginning and end of a string. This
19215440Sjm199354 * function alters the string buffer in-place.
19225440Sjm199354 *
19235440Sjm199354 * Whitespaces found at the beginning of the string are eliminated by
19245440Sjm199354 * moving forward the start of the string at the first non-whitespace
19255440Sjm199354 * character.
19265440Sjm199354 * Whitespace found at the end of the string are overwritten with nulls.
19275440Sjm199354 *
19285440Sjm199354 */
19295440Sjm199354 static void
vs_icap_trimspace(char * buf)19305440Sjm199354 vs_icap_trimspace(char *buf)
19315440Sjm199354 {
19325440Sjm199354 char *p = buf;
19335440Sjm199354 char *q = buf;
19345440Sjm199354
19355440Sjm199354 if (buf == 0)
19365440Sjm199354 return;
19375440Sjm199354
19385440Sjm199354 while (*p && isspace(*p))
19395440Sjm199354 ++p;
19405440Sjm199354
19415440Sjm199354 while ((*q = *p++) != 0)
19425440Sjm199354 ++q;
19435440Sjm199354
19445440Sjm199354 if (q != buf) {
19455440Sjm199354 while ((--q, isspace(*q)) != 0)
19465440Sjm199354 *q = '\0';
19475440Sjm199354 }
19485440Sjm199354 }
19495440Sjm199354
19505440Sjm199354
19515440Sjm199354 /*
19525440Sjm199354 * vs_icap_uri_encode
19535440Sjm199354 *
19545440Sjm199354 * Encode uri data (eg filename) in accordance with RFC 2396
19555440Sjm199354 * 'Illegal' characters should be replaced with %hh, where hh is
19565440Sjm199354 * the hex value of the character. For example a space would be
19575440Sjm199354 * replaced with %20.
19585440Sjm199354 * Filenames are all already UTF-8 encoded. Any UTF-8 octects that
19595440Sjm199354 * are 'illegal' characters will be encoded as described above.
19605440Sjm199354 *
19615440Sjm199354 * Paramaters: data - string to be encoded (NULL terminated)
19625440Sjm199354 * buf - output buffer (NULL terminated)
19635440Sjm199354 * size - size of output buffer
19645440Sjm199354 *
19655440Sjm199354 * Returns: strlen of encoded data on success
19665440Sjm199354 * -1 size on error (contents of buf undefined)
19675440Sjm199354 */
19685440Sjm199354 static int
vs_icap_uri_encode(char * buf,int size,char * data)19695440Sjm199354 vs_icap_uri_encode(char *buf, int size, char *data)
19705440Sjm199354 {
19715440Sjm199354 unsigned char *iptr;
19725440Sjm199354 char *optr = buf;
19735440Sjm199354 int len = strlen(data);
19745440Sjm199354
19755440Sjm199354 /* modify the data */
19765440Sjm199354 for (iptr = (unsigned char *)data; *iptr; iptr++) {
19775440Sjm199354 if (vs_icap_uri_illegal_char(*iptr)) {
19785440Sjm199354 if ((len += 2) >= size)
19795440Sjm199354 return (-1);
19805440Sjm199354 (void) sprintf(optr, "%%%0x", *iptr);
19815440Sjm199354 optr += 3;
19825440Sjm199354 } else {
19835440Sjm199354 if (len >= size)
19845440Sjm199354 return (-1);
19855440Sjm199354 *optr++ = *iptr;
19865440Sjm199354 }
19875440Sjm199354 }
19885440Sjm199354
19895440Sjm199354 *optr = '\0';
19905440Sjm199354 return (len);
19915440Sjm199354 }
19925440Sjm199354
19935440Sjm199354
19945440Sjm199354 /*
19955440Sjm199354 * vs_icap_uri_illegal_char
19965440Sjm199354 *
19975440Sjm199354 * The following us-ascii characters (UTF-8 octets) are 'illegal':
19985440Sjm199354 * < > # % " { } | \ ^ [ ] ` space, 0x01 -> 0x1F & 0x7F
19995440Sjm199354 * All non us-ascii UTF-8 octets ( >= 0x80) are illegal.
20005440Sjm199354 *
20015440Sjm199354 * Returns: 1 if character is not allowed in a URI
20025440Sjm199354 * 0 otherwise
20035440Sjm199354 */
20045440Sjm199354 static int
vs_icap_uri_illegal_char(char c)20055440Sjm199354 vs_icap_uri_illegal_char(char c)
20065440Sjm199354 {
20075440Sjm199354 static const char *uri_illegal_chars = "<>#%\" {}|\\^[]`";
20085440Sjm199354
20095440Sjm199354 /* us-ascii non printable characters or non us-ascii */
20105440Sjm199354 if ((c <= 0x1F) || (c >= 0x7F))
20115440Sjm199354 return (1);
20125440Sjm199354
20135440Sjm199354 /* us-ascii dis-allowed characters */
20145440Sjm199354 if (strchr(uri_illegal_chars, c))
20155440Sjm199354 return (1);
20165440Sjm199354
20175440Sjm199354 return (0);
20185440Sjm199354
20195440Sjm199354 }
2020