1911106dfSjm199354 /*
2911106dfSjm199354 * CDDL HEADER START
3911106dfSjm199354 *
4911106dfSjm199354 * The contents of this file are subject to the terms of the
5911106dfSjm199354 * Common Development and Distribution License (the "License").
6911106dfSjm199354 * You may not use this file except in compliance with the License.
7911106dfSjm199354 *
8911106dfSjm199354 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9911106dfSjm199354 * or http://www.opensolaris.org/os/licensing.
10911106dfSjm199354 * See the License for the specific language governing permissions
11911106dfSjm199354 * and limitations under the License.
12911106dfSjm199354 *
13911106dfSjm199354 * When distributing Covered Code, include this CDDL HEADER in each
14911106dfSjm199354 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15911106dfSjm199354 * If applicable, add the following below this CDDL HEADER, with the
16911106dfSjm199354 * fields enclosed by brackets "[]" replaced with your own identifying
17911106dfSjm199354 * information: Portions Copyright [yyyy] [name of copyright owner]
18911106dfSjm199354 *
19911106dfSjm199354 * CDDL HEADER END
20911106dfSjm199354 */
21911106dfSjm199354 /*
22*db1a607eSjoyce mcintosh * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23911106dfSjm199354 * Use is subject to license terms.
24911106dfSjm199354 */
25911106dfSjm199354
26911106dfSjm199354 /*
27911106dfSjm199354 * Description: Module contains supporting functions used by functions
28911106dfSjm199354 * defined in vs_svc.c. It also contains some internal(static) functions.
29911106dfSjm199354 */
30911106dfSjm199354
31911106dfSjm199354 #include <stdarg.h>
32911106dfSjm199354 #include <stdio.h>
33911106dfSjm199354 #include <stdlib.h>
34911106dfSjm199354 #include <unistd.h>
35911106dfSjm199354 #include <errno.h>
36911106dfSjm199354 #include <time.h>
37911106dfSjm199354 #include <fcntl.h>
38911106dfSjm199354 #include <syslog.h>
39911106dfSjm199354 #include <ctype.h>
40911106dfSjm199354 #include <strings.h>
41911106dfSjm199354 #include <string.h>
42911106dfSjm199354 #include <limits.h>
43911106dfSjm199354 #include <pthread.h>
44911106dfSjm199354 #include <sys/types.h>
45911106dfSjm199354 #include <sys/socket.h>
46911106dfSjm199354 #include <sys/debug.h>
47911106dfSjm199354 #include <netinet/in.h>
48911106dfSjm199354 #include <arpa/inet.h>
49911106dfSjm199354
50911106dfSjm199354 #include "vs_incl.h"
51911106dfSjm199354 #include "vs_icap.h"
52911106dfSjm199354
53911106dfSjm199354 /* prototypes of local functions */
54911106dfSjm199354 static int vs_icap_option_request(vs_scan_ctx_t *);
55911106dfSjm199354 static int vs_icap_send_option_req(vs_scan_ctx_t *);
56911106dfSjm199354 static int vs_icap_read_option_resp(vs_scan_ctx_t *);
57911106dfSjm199354
58911106dfSjm199354 static int vs_icap_respmod_request(vs_scan_ctx_t *);
59911106dfSjm199354 static int vs_icap_may_preview(vs_scan_ctx_t *);
60911106dfSjm199354 static char *vs_icap_find_ext(char *);
61911106dfSjm199354 static int vs_icap_send_preview(vs_scan_ctx_t *);
62911106dfSjm199354 static int vs_icap_send_respmod_hdr(vs_scan_ctx_t *, int);
63911106dfSjm199354 static int vs_icap_create_respmod_hdr(vs_scan_ctx_t *, int);
64911106dfSjm199354 static int vs_icap_uri_encode(char *, int, char *);
65911106dfSjm199354 static int vs_icap_uri_illegal_char(char);
66911106dfSjm199354
67911106dfSjm199354 static int vs_icap_read_respmod_resp(vs_scan_ctx_t *);
68911106dfSjm199354 static int vs_icap_read_resp_code(vs_scan_ctx_t *);
69911106dfSjm199354 static int vs_icap_read_hdr(vs_scan_ctx_t *, vs_hdr_t *, int);
70911106dfSjm199354
71911106dfSjm199354 static int vs_icap_set_scan_result(vs_scan_ctx_t *);
72911106dfSjm199354 static int vs_icap_read_encap_hdr(vs_scan_ctx_t *);
73911106dfSjm199354 static void vs_icap_read_encap_data(vs_scan_ctx_t *);
74911106dfSjm199354 static int vs_icap_create_repair_file(vs_scan_ctx_t *);
75911106dfSjm199354 static int vs_icap_read_resp_body(vs_scan_ctx_t *);
76911106dfSjm199354 static int vs_icap_read_body_chunk(vs_scan_ctx_t *);
77911106dfSjm199354
78911106dfSjm199354 static int vs_icap_send_chunk(vs_scan_ctx_t *, int);
79911106dfSjm199354 static int vs_icap_send_termination(vs_scan_ctx_t *);
80911106dfSjm199354 static int vs_icap_readline(vs_scan_ctx_t *, char *, int);
81911106dfSjm199354
82911106dfSjm199354 static int vs_icap_write(int, char *, int);
83911106dfSjm199354 static int vs_icap_read(int, char *, int);
84911106dfSjm199354
85911106dfSjm199354 /* process options and respmod headers */
86911106dfSjm199354 static void vs_icap_parse_hdrs(char, char *, char **, char **);
87911106dfSjm199354 static int vs_icap_opt_value(vs_scan_ctx_t *, int, char *);
88911106dfSjm199354 static int vs_icap_opt_ext(vs_scan_ctx_t *, int, char *);
89911106dfSjm199354 static int vs_icap_resp_violations(vs_scan_ctx_t *, int, char *);
90911106dfSjm199354 static int vs_icap_resp_violation_rec(vs_scan_ctx_t *, int);
91911106dfSjm199354 static int vs_icap_resp_infection(vs_scan_ctx_t *, int, char *);
92911106dfSjm199354 static int vs_icap_resp_virus_id(vs_scan_ctx_t *, int, char *);
93911106dfSjm199354 static int vs_icap_resp_encap(vs_scan_ctx_t *, int, char *);
94911106dfSjm199354 static int vs_icap_resp_istag(vs_scan_ctx_t *, int, char *);
95911106dfSjm199354 static void vs_icap_istag_to_scanstamp(char *, vs_scanstamp_t);
96911106dfSjm199354
97911106dfSjm199354 /* Utility functions for handling OPTIONS data: vs_options_t */
98911106dfSjm199354 static void vs_icap_free_options(vs_options_t *);
99911106dfSjm199354 static void vs_icap_copy_options(vs_options_t *, vs_options_t *);
100911106dfSjm199354 static void vs_icap_update_options(vs_scan_ctx_t *);
101911106dfSjm199354 static int vs_icap_compare_se(int, char *, int);
102911106dfSjm199354
103911106dfSjm199354 static iovec_t *vs_icap_make_strvec(char *, const char *);
104911106dfSjm199354 static iovec_t *vs_icap_copy_strvec(iovec_t *);
105911106dfSjm199354 static int vs_icap_check_ext(char *, iovec_t *);
106911106dfSjm199354 static void vs_icap_trimspace(char *);
107911106dfSjm199354
108911106dfSjm199354 /* icap response message */
109911106dfSjm199354 static char *vs_icap_resp_str(int);
110911106dfSjm199354
111911106dfSjm199354 /*
112911106dfSjm199354 * local variables
113911106dfSjm199354 */
114911106dfSjm199354
115911106dfSjm199354 /* option headers - and handler functions */
116911106dfSjm199354 vs_hdr_t option_hdrs[] = {
117911106dfSjm199354 { VS_OPT_SERVICE, "Service", vs_icap_opt_value},
118911106dfSjm199354 { VS_OPT_ISTAG, "ISTag", vs_icap_opt_value},
119911106dfSjm199354 { VS_OPT_METHODS, "Methods", vs_icap_opt_value},
120911106dfSjm199354 { VS_OPT_ALLOW, "Allow", vs_icap_opt_value},
121911106dfSjm199354 { VS_OPT_PREVIEW, "Preview", vs_icap_opt_value},
122911106dfSjm199354 { VS_OPT_XFER_PREVIEW, "Transfer-Preview", vs_icap_opt_ext},
123911106dfSjm199354 { VS_OPT_XFER_COMPLETE, "Transfer-Complete", vs_icap_opt_ext},
124911106dfSjm199354 { VS_OPT_MAX_CONNECTIONS, "Max-Connections", vs_icap_opt_value},
125911106dfSjm199354 { VS_OPT_TTL, "Options-TTL", vs_icap_opt_value},
126911106dfSjm199354 { VS_OPT_X_DEF_INFO, "X-Definition-Info", vs_icap_opt_value}
127911106dfSjm199354 };
128911106dfSjm199354
129911106dfSjm199354
130911106dfSjm199354 /* resp hdrs - and handler functions */
131911106dfSjm199354 vs_hdr_t resp_hdrs[] = {
132911106dfSjm199354 { VS_RESP_ENCAPSULATED, "Encapsulated", vs_icap_resp_encap},
133911106dfSjm199354 { VS_RESP_ISTAG, "ISTag", vs_icap_resp_istag},
134911106dfSjm199354 { VS_RESP_X_VIRUS_ID, "X-Virus-ID", vs_icap_resp_virus_id},
135911106dfSjm199354 { VS_RESP_X_INFECTION, "X-Infection-Found", vs_icap_resp_infection},
136911106dfSjm199354 { VS_RESP_X_VIOLATIONS, "X-Violations-Found", vs_icap_resp_violations}
137911106dfSjm199354 };
138911106dfSjm199354
139911106dfSjm199354 /* ICAP response code to string mappings */
140911106dfSjm199354 vs_resp_msg_t icap_resp[] = {
141911106dfSjm199354 { VS_RESP_CONTINUE, "Continue"},
142911106dfSjm199354 { VS_RESP_OK, "OK"},
143911106dfSjm199354 { VS_RESP_CREATED, "Virus Detected and Repaired"},
144911106dfSjm199354 { VS_RESP_NO_CONT_NEEDED, "No Content Necessary"},
145911106dfSjm199354 { VS_RESP_BAD_REQ, "Bad Request"},
146911106dfSjm199354 { VS_RESP_FORBIDDEN, "File Infected and not repaired"},
147911106dfSjm199354 { VS_RESP_NOT_FOUND, "URI not found"},
148911106dfSjm199354 { VS_RESP_NOT_ALLOWED, "Method not allowed"},
149911106dfSjm199354 { VS_RESP_TIMEOUT, "Request timedout"},
150911106dfSjm199354 { VS_RESP_INTERNAL_ERR, "Internal server error"},
151911106dfSjm199354 { VS_RESP_NOT_IMPL, "Method not implemented"},
152911106dfSjm199354 { VS_RESP_SERV_UNAVAIL, "Service unavailable/overloaded"},
153911106dfSjm199354 { VS_RESP_ICAP_VER_UNSUPP, "ICAP version not supported"},
154911106dfSjm199354 { VS_RESP_SCAN_ERR, "Error scanning file"},
155911106dfSjm199354 { VS_RESP_NO_LICENSE, "No AV License"},
156911106dfSjm199354 { VS_RESP_RES_UNAVAIL, "Resource unavailable"},
157911106dfSjm199354 { VS_RESP_UNKNOWN, "Unknown Error"},
158911106dfSjm199354 };
159911106dfSjm199354
160911106dfSjm199354 static const char *EXT_SEPARATOR = ",";
161911106dfSjm199354 static vs_options_t vs_options[VS_SE_MAX];
162911106dfSjm199354 static pthread_mutex_t vs_opt_mutex = PTHREAD_MUTEX_INITIALIZER;
163911106dfSjm199354
164911106dfSjm199354 /*
165911106dfSjm199354 * vs_icap_init
166911106dfSjm199354 * initialization performed when daemon is loaded
167911106dfSjm199354 */
168911106dfSjm199354 void
vs_icap_init()169911106dfSjm199354 vs_icap_init()
170911106dfSjm199354 {
171911106dfSjm199354
172911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
173911106dfSjm199354 (void) memset(vs_options, 0, sizeof (vs_options_t));
174911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
175911106dfSjm199354 }
176911106dfSjm199354
177911106dfSjm199354
178911106dfSjm199354 /*
179911106dfSjm199354 * vs_icap_fini
180911106dfSjm199354 * cleanup performed when daemon is unloaded
181911106dfSjm199354 */
182911106dfSjm199354 void
vs_icap_fini()183911106dfSjm199354 vs_icap_fini()
184911106dfSjm199354 {
185911106dfSjm199354 int i;
186911106dfSjm199354
187911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
188911106dfSjm199354
189911106dfSjm199354 for (i = 0; i < VS_SE_MAX; i++)
190911106dfSjm199354 vs_icap_free_options(&vs_options[i]);
191911106dfSjm199354
192911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
193911106dfSjm199354 }
194911106dfSjm199354
195911106dfSjm199354
196911106dfSjm199354 /*
197911106dfSjm199354 * vs_icap_config
198911106dfSjm199354 *
19953c11029Sjm199354 * When a new VSCAN configuration is specified, this will be
200911106dfSjm199354 * called per scan engine. If the scan engine host or port has
201911106dfSjm199354 * changed delete the vs_options entry for that scan engine.
202911106dfSjm199354 */
203911106dfSjm199354 void
vs_icap_config(int idx,char * host,int port)204911106dfSjm199354 vs_icap_config(int idx, char *host, int port)
205911106dfSjm199354 {
206911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
207911106dfSjm199354 if (vs_icap_compare_se(idx, host, port) != 0) {
208911106dfSjm199354 vs_icap_free_options(&vs_options[idx]);
209911106dfSjm199354 (void) strlcpy(vs_options[idx].vso_host, host,
210911106dfSjm199354 sizeof (vs_options[idx].vso_host));
211911106dfSjm199354 vs_options[idx].vso_port = port;
212911106dfSjm199354 }
213911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
214911106dfSjm199354 }
215911106dfSjm199354
216911106dfSjm199354
217911106dfSjm199354 /*
218911106dfSjm199354 * vs_icap_scan_file
219911106dfSjm199354 *
220911106dfSjm199354 * Create a context (vs_scan_ctx_t) for the scan operation and initialize
221911106dfSjm199354 * its options info. If the scan engine connection's IP or port is different
222911106dfSjm199354 * from that held in vs_options the vs_options info is old and should
223911106dfSjm199354 * be deleted (vs_icap_free_options). Otherwise, copy the vs_options info
224911106dfSjm199354 * into the context.
225911106dfSjm199354 * file name, size and decsriptor are also copied into the context
226911106dfSjm199354 *
227911106dfSjm199354 * Handle the ICAP protocol communication with the external Scan Engine to
228911106dfSjm199354 * perform the scan
229911106dfSjm199354 * - send an OPTIONS request if necessary
230911106dfSjm199354 * - send RESPMOD scan request
231911106dfSjm199354 * - process the response and save any cleaned data to file
232911106dfSjm199354 *
233911106dfSjm199354 * Returns: result->vsr_rc
234911106dfSjm199354 */
235911106dfSjm199354 int
vs_icap_scan_file(vs_eng_ctx_t * eng,char * devname,char * fname,uint64_t fsize,int flags,vs_result_t * result)236bfc848c6Sjm199354 vs_icap_scan_file(vs_eng_ctx_t *eng, char *devname, char *fname,
237911106dfSjm199354 uint64_t fsize, int flags, vs_result_t *result)
238911106dfSjm199354 {
239911106dfSjm199354 vs_scan_ctx_t ctx;
240911106dfSjm199354 int fd;
241911106dfSjm199354
242bfc848c6Sjm199354 fd = open(devname, O_RDONLY);
243bfc848c6Sjm199354
244bfc848c6Sjm199354 /* retry once on ENOENT as /dev link may not be created yet */
245bfc848c6Sjm199354 if ((fd == -1) && (errno == ENOENT)) {
246bfc848c6Sjm199354 (void) sleep(1);
247bfc848c6Sjm199354 fd = open(devname, O_RDONLY);
248bfc848c6Sjm199354 }
249bfc848c6Sjm199354
250bfc848c6Sjm199354 if (fd == -1) {
251bfc848c6Sjm199354 syslog(LOG_ERR, "Failed to open device %s - %s",
252bfc848c6Sjm199354 devname, strerror(errno));
253911106dfSjm199354 result->vsr_rc = VS_RESULT_ERROR;
254911106dfSjm199354 return (result->vsr_rc);
255911106dfSjm199354 }
256911106dfSjm199354
257911106dfSjm199354 /* initialize context */
258911106dfSjm199354 (void) memset(&ctx, 0, sizeof (vs_scan_ctx_t));
259bfc848c6Sjm199354 ctx.vsc_idx = eng->vse_eidx;
260bfc848c6Sjm199354 (void) strlcpy(ctx.vsc_host, eng->vse_host, sizeof (ctx.vsc_host));
261bfc848c6Sjm199354 ctx.vsc_port = eng->vse_port;
262bfc848c6Sjm199354 ctx.vsc_sockfd = eng->vse_sockfd;
263911106dfSjm199354 ctx.vsc_fd = fd;
264911106dfSjm199354 ctx.vsc_fname = fname;
265911106dfSjm199354 ctx.vsc_fsize = fsize;
266911106dfSjm199354 ctx.vsc_flags = flags;
267911106dfSjm199354 ctx.vsc_result = result;
268911106dfSjm199354
269911106dfSjm199354 /* Hooks for future saving of repaired data, not yet in use */
270911106dfSjm199354 ctx.vsc_flags |= VS_NO_REPAIR;
271911106dfSjm199354 ctx.vsc_repair = 0;
272911106dfSjm199354 ctx.vsc_repair_fname = NULL;
273911106dfSjm199354 ctx.vsc_repair_fd = -1;
274911106dfSjm199354
275911106dfSjm199354 /* take a copy of vs_options[idx] if they match the SE specified */
276911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
277911106dfSjm199354 if (vs_icap_compare_se(ctx.vsc_idx, ctx.vsc_host, ctx.vsc_port) == 0) {
278911106dfSjm199354 vs_icap_copy_options(&ctx.vsc_options,
279911106dfSjm199354 &vs_options[ctx.vsc_idx]);
280911106dfSjm199354 }
281911106dfSjm199354
282911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
283911106dfSjm199354
284911106dfSjm199354 /*
285911106dfSjm199354 * default the result to scan engine error.
286911106dfSjm199354 * Any non scan-engine errors will reset it to VS_RESULT_ERROR
287911106dfSjm199354 */
288911106dfSjm199354 result->vsr_rc = VS_RESULT_SE_ERROR;
289911106dfSjm199354
290911106dfSjm199354 /* do the scan */
291911106dfSjm199354 if (vs_icap_option_request(&ctx) == 0)
292911106dfSjm199354 (void) vs_icap_respmod_request(&ctx);
293911106dfSjm199354
294911106dfSjm199354 (void) close(fd);
295911106dfSjm199354 vs_icap_free_options(&ctx.vsc_options);
296911106dfSjm199354 return (result->vsr_rc);
297911106dfSjm199354 }
298911106dfSjm199354
299911106dfSjm199354
300911106dfSjm199354 /* ********************************************************************* */
301911106dfSjm199354 /* Local Function definitions */
302911106dfSjm199354 /* ********************************************************************* */
303911106dfSjm199354
304911106dfSjm199354 /*
305911106dfSjm199354 * vs_icap_option_request
306911106dfSjm199354 *
307911106dfSjm199354 * Send ICAP options message and await/process the response.
308911106dfSjm199354 *
309911106dfSjm199354 * The ICAP options request needs to be sent when a connection
310911106dfSjm199354 * is first made with the scan engine. Unless the scan engine
311911106dfSjm199354 * determines that the options will never expire (which we save
312911106dfSjm199354 * as optione_req_time == -1) the request should be resent after
313911106dfSjm199354 * the expiry time specified by the icap server.
314911106dfSjm199354 *
315911106dfSjm199354 * Returns: 0 - success
316911106dfSjm199354 * -1 - error
317911106dfSjm199354 */
318911106dfSjm199354 static int
vs_icap_option_request(vs_scan_ctx_t * ctx)319911106dfSjm199354 vs_icap_option_request(vs_scan_ctx_t *ctx)
320911106dfSjm199354 {
321911106dfSjm199354 if (ctx->vsc_options.vso_req_time != -1 &&
322911106dfSjm199354 ((time(0) - ctx->vsc_options.vso_req_time) >
323911106dfSjm199354 ctx->vsc_options.vso_ttl)) {
324911106dfSjm199354
325911106dfSjm199354 if (vs_icap_send_option_req(ctx) < 0)
326911106dfSjm199354 return (-1);
327911106dfSjm199354
328911106dfSjm199354 if (vs_icap_read_option_resp(ctx) < 0)
329911106dfSjm199354 return (-1);
330911106dfSjm199354
331911106dfSjm199354 vs_icap_update_options(ctx);
332911106dfSjm199354 }
333911106dfSjm199354
334911106dfSjm199354 return (0);
335911106dfSjm199354 }
336911106dfSjm199354
337911106dfSjm199354
338911106dfSjm199354 /*
339911106dfSjm199354 * vs_icap_send_option_req
340911106dfSjm199354 *
341911106dfSjm199354 * Send an OPTIONS request to the scan engine
342911106dfSjm199354 * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME)
343911106dfSjm199354 * after the IP address, otherwise it closes the connection.
344911106dfSjm199354 *
345911106dfSjm199354 * Returns: 0 - success
346911106dfSjm199354 * -1 - error
347911106dfSjm199354 */
348911106dfSjm199354 static int
vs_icap_send_option_req(vs_scan_ctx_t * ctx)349911106dfSjm199354 vs_icap_send_option_req(vs_scan_ctx_t *ctx)
350911106dfSjm199354 {
351911106dfSjm199354 char my_host_name[MAXHOSTNAMELEN];
352911106dfSjm199354 int bufsp = VS_BUF_SZ;
353911106dfSjm199354 char *buf0 = ctx->vsc_info.vsi_send_buf;
354911106dfSjm199354 char *bufp = buf0;
355911106dfSjm199354 int tlen;
356911106dfSjm199354
357911106dfSjm199354 if (gethostname(my_host_name, sizeof (my_host_name)) != 0) {
358911106dfSjm199354 /* non SE error */
359911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
360911106dfSjm199354 return (-1);
361911106dfSjm199354 }
362911106dfSjm199354
363911106dfSjm199354 (void) memset(ctx->vsc_info.vsi_send_buf, 0,
364911106dfSjm199354 sizeof (ctx->vsc_info.vsi_send_buf));
365911106dfSjm199354
366911106dfSjm199354 tlen = snprintf(bufp, bufsp, "OPTIONS icap://%s:%d/%s %s\r\n",
367911106dfSjm199354 ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER);
368911106dfSjm199354 bufp += tlen;
369911106dfSjm199354 bufsp -= tlen;
370911106dfSjm199354
371911106dfSjm199354 tlen = snprintf(bufp, bufsp, "Host: %s\r\n\r\n", my_host_name);
372911106dfSjm199354 bufp += tlen;
373911106dfSjm199354
374911106dfSjm199354 if (vs_icap_write(ctx->vsc_sockfd, buf0, (bufp - buf0)) < 0)
375911106dfSjm199354 return (-1);
376911106dfSjm199354
377911106dfSjm199354 return (0);
378911106dfSjm199354 }
379911106dfSjm199354
380911106dfSjm199354
381911106dfSjm199354 /*
382911106dfSjm199354 * vs_icap_read_option_resp
383911106dfSjm199354 *
384911106dfSjm199354 * Returns: 0 - success
385911106dfSjm199354 * -1 - error
386911106dfSjm199354 */
387911106dfSjm199354 static int
vs_icap_read_option_resp(vs_scan_ctx_t * ctx)388911106dfSjm199354 vs_icap_read_option_resp(vs_scan_ctx_t *ctx)
389911106dfSjm199354 {
390911106dfSjm199354 if (vs_icap_read_resp_code(ctx) < 0)
391911106dfSjm199354 return (-1);
392911106dfSjm199354
393911106dfSjm199354 if (ctx->vsc_info.vsi_icap_rc != VS_RESP_OK) {
394911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error "
395911106dfSjm199354 "- unexpected option response: %s",
396911106dfSjm199354 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc));
397911106dfSjm199354 return (-1);
398911106dfSjm199354 }
399911106dfSjm199354
400911106dfSjm199354 if (vs_icap_read_hdr(ctx, option_hdrs, VS_OPT_HDR_MAX) != 0)
401911106dfSjm199354 return (-1);
402911106dfSjm199354
403911106dfSjm199354 if ((ctx->vsc_options.vso_scanstamp[0] == 0) ||
404911106dfSjm199354 (ctx->vsc_options.vso_respmod == 0) ||
405911106dfSjm199354 (ctx->vsc_options.vso_req_time == 0)) {
406911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error "
407911106dfSjm199354 "- missing or invalid option response hdrs");
408911106dfSjm199354 return (-1);
409911106dfSjm199354 }
410911106dfSjm199354
411911106dfSjm199354 return (0);
412911106dfSjm199354 }
413911106dfSjm199354
414911106dfSjm199354
415911106dfSjm199354 /*
416911106dfSjm199354 * vs_icap_respmod_request
417911106dfSjm199354 *
418911106dfSjm199354 * Send respmod request and receive and process ICAP response.
419911106dfSjm199354 * Preview:
420911106dfSjm199354 * ICAP allows for an optional "preview" request. In the option negotiation,
421911106dfSjm199354 * the server may ask for a list of types to be previewed, or to be sent
422911106dfSjm199354 * complete (no preview).
423911106dfSjm199354 * This is advisory. It is ok to skip the preview step, as done when the file
424911106dfSjm199354 * is smaller than the preview_len.
425911106dfSjm199354 * Process Response:
426911106dfSjm199354 * - read and parse the RESPMOD response headers
427911106dfSjm199354 * - populate the result structure
428911106dfSjm199354 * - read any encapsulated response headers
429911106dfSjm199354 * - read any encapsulated response body and, if it represents cleaned
430911106dfSjm199354 * file data, overwrite the file with it
431911106dfSjm199354 *
432911106dfSjm199354 * Returns: 0 - success
433911106dfSjm199354 * -1 - error
434911106dfSjm199354 */
435911106dfSjm199354 static int
vs_icap_respmod_request(vs_scan_ctx_t * ctx)436911106dfSjm199354 vs_icap_respmod_request(vs_scan_ctx_t *ctx)
437911106dfSjm199354 {
438911106dfSjm199354 int rv;
439911106dfSjm199354 int bytes_sent, send_len;
440911106dfSjm199354 uint64_t resid = ctx->vsc_fsize;
441911106dfSjm199354
442911106dfSjm199354 if (vs_icap_may_preview(ctx)) {
443911106dfSjm199354
444911106dfSjm199354 if ((rv = vs_icap_send_preview(ctx)) < 0)
445911106dfSjm199354 return (-1);
446911106dfSjm199354
447911106dfSjm199354 if (vs_icap_read_respmod_resp(ctx) < 0)
448911106dfSjm199354 return (-1);
449911106dfSjm199354
450911106dfSjm199354 if (ctx->vsc_info.vsi_icap_rc != VS_RESP_CONTINUE)
451911106dfSjm199354 return (0);
452911106dfSjm199354
453911106dfSjm199354 bytes_sent = rv;
454911106dfSjm199354
455911106dfSjm199354 /* If > block (VS_BUF_SZ) remains, re-align to block boundary */
456911106dfSjm199354 if ((ctx->vsc_fsize - (uint64_t)bytes_sent) > VS_BUF_SZ) {
457911106dfSjm199354 send_len = VS_BUF_SZ - bytes_sent;
458911106dfSjm199354 if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0)
459911106dfSjm199354 return (-1);
460911106dfSjm199354 bytes_sent += rv;
461911106dfSjm199354 }
462911106dfSjm199354
463911106dfSjm199354 resid -= (uint64_t)bytes_sent;
464911106dfSjm199354
465911106dfSjm199354 } else {
466911106dfSjm199354
467911106dfSjm199354 if (vs_icap_send_respmod_hdr(ctx, 0) < 0)
468911106dfSjm199354 return (-1);
469911106dfSjm199354 }
470911106dfSjm199354
471911106dfSjm199354 /* Send the remainder of the file... */
472911106dfSjm199354 while (resid) {
473911106dfSjm199354 send_len = (resid > VS_BUF_SZ) ? VS_BUF_SZ : resid;
474911106dfSjm199354
475911106dfSjm199354 if ((rv = vs_icap_send_chunk(ctx, send_len)) < 0)
476911106dfSjm199354 return (-1);
477911106dfSjm199354
478911106dfSjm199354 if (rv == 0)
479911106dfSjm199354 break;
480911106dfSjm199354
481911106dfSjm199354 resid -= (uint64_t)rv;
482911106dfSjm199354 }
483911106dfSjm199354
484911106dfSjm199354 if (vs_icap_send_termination(ctx) < 0)
485911106dfSjm199354 return (-1);
486911106dfSjm199354
487911106dfSjm199354 /* sending of ICAP request complete */
488911106dfSjm199354 if (vs_icap_read_respmod_resp(ctx) < 0)
489911106dfSjm199354 return (-1);
490911106dfSjm199354
491911106dfSjm199354 return (0);
492911106dfSjm199354 }
493911106dfSjm199354
494911106dfSjm199354
495911106dfSjm199354 /*
496911106dfSjm199354 * vs_icap_may_preview
497911106dfSjm199354 *
498911106dfSjm199354 * Returns: 1 - preview
499911106dfSjm199354 * 0 - don't preview
500911106dfSjm199354 */
501911106dfSjm199354 static int
vs_icap_may_preview(vs_scan_ctx_t * ctx)502911106dfSjm199354 vs_icap_may_preview(vs_scan_ctx_t *ctx)
503911106dfSjm199354 {
504911106dfSjm199354 int in_list = 0;
505911106dfSjm199354 char *ext;
506911106dfSjm199354 vs_options_t *opts = &ctx->vsc_options;
507911106dfSjm199354
508911106dfSjm199354 if (opts->vso_xfer_how == VS_PREVIEW_NONE)
509911106dfSjm199354 return (0);
510911106dfSjm199354
511911106dfSjm199354 /* if the file is smaller than the preview size, don't preview */
512911106dfSjm199354 if (ctx->vsc_fsize < (uint64_t)ctx->vsc_options.vso_preview_len)
513911106dfSjm199354 return (0);
514911106dfSjm199354
515911106dfSjm199354 switch (opts->vso_xfer_how) {
516911106dfSjm199354 case VS_PREVIEW_ALL:
517911106dfSjm199354 return (1);
518911106dfSjm199354 case VS_PREVIEW_EXCEPT:
519911106dfSjm199354 /* Preview everything except types in xfer_complete */
520911106dfSjm199354 if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0)
521911106dfSjm199354 in_list = vs_icap_check_ext(ext,
522911106dfSjm199354 opts->vso_xfer_complete);
523911106dfSjm199354 return ((in_list) ? 0 : 1);
524911106dfSjm199354 case VS_PREVIEW_LIST:
525911106dfSjm199354 /* Preview only types in the the xfer_preview list */
526911106dfSjm199354 if ((ext = vs_icap_find_ext(ctx->vsc_fname)) != 0)
527911106dfSjm199354 in_list = vs_icap_check_ext(ext,
528911106dfSjm199354 opts->vso_xfer_preview);
529911106dfSjm199354 return ((in_list) ? 1 : 0);
530911106dfSjm199354 }
531911106dfSjm199354
532911106dfSjm199354 return (1);
533911106dfSjm199354 }
534911106dfSjm199354
535911106dfSjm199354
536911106dfSjm199354 /*
537911106dfSjm199354 * vs_icap_find_ext
538911106dfSjm199354 *
539911106dfSjm199354 * Returns: ptr to file's extension in fname
540911106dfSjm199354 * 0 if no extension
541911106dfSjm199354 */
542911106dfSjm199354 static char *
vs_icap_find_ext(char * fname)543911106dfSjm199354 vs_icap_find_ext(char *fname)
544911106dfSjm199354 {
545911106dfSjm199354 char *last_comp, *ext_str = 0;
546911106dfSjm199354
547911106dfSjm199354 if ((last_comp = strrchr(fname, '/')) != 0) {
548911106dfSjm199354 last_comp++;
549911106dfSjm199354 } else {
550911106dfSjm199354 last_comp = fname;
551911106dfSjm199354 }
552911106dfSjm199354
553911106dfSjm199354 /* Get file extension */
554911106dfSjm199354 if ((ext_str = strrchr(last_comp, '.')) != 0) {
555911106dfSjm199354 ext_str++;
556911106dfSjm199354 if (strlen(ext_str) == 0)
557911106dfSjm199354 ext_str = 0;
558911106dfSjm199354 }
559911106dfSjm199354
560911106dfSjm199354 return (ext_str);
561911106dfSjm199354 }
562911106dfSjm199354
563911106dfSjm199354
564911106dfSjm199354 /*
565911106dfSjm199354 * vs_icap_send_preview
566911106dfSjm199354 *
567911106dfSjm199354 * Returns: bytes sent (preview + alignment)
568911106dfSjm199354 * -1 - error
569911106dfSjm199354 */
570911106dfSjm199354 static int
vs_icap_send_preview(vs_scan_ctx_t * ctx)571911106dfSjm199354 vs_icap_send_preview(vs_scan_ctx_t *ctx)
572911106dfSjm199354 {
573911106dfSjm199354 int preview_len = ctx->vsc_options.vso_preview_len;
574911106dfSjm199354 int bytes_sent;
575911106dfSjm199354
576911106dfSjm199354 /* Send a RESPMOD request with "preview" mode. */
577911106dfSjm199354 if (vs_icap_send_respmod_hdr(ctx, 'P') < 0)
578911106dfSjm199354 return (-1);
579911106dfSjm199354
580911106dfSjm199354 if ((bytes_sent = vs_icap_send_chunk(ctx, preview_len)) < 0)
581911106dfSjm199354 return (-1);
582911106dfSjm199354
583911106dfSjm199354 if (bytes_sent < preview_len)
584911106dfSjm199354 return (-1);
585911106dfSjm199354
586911106dfSjm199354 if (vs_icap_send_termination(ctx) < 0)
587911106dfSjm199354 return (-1);
588911106dfSjm199354
589911106dfSjm199354 return (bytes_sent);
590911106dfSjm199354 }
591911106dfSjm199354
592911106dfSjm199354
593911106dfSjm199354 /*
594911106dfSjm199354 * vs_icap_send_respmod_hdr
595911106dfSjm199354 *
596911106dfSjm199354 * Create and send the RESPMOD request headers to the scan engine.
597911106dfSjm199354 *
598911106dfSjm199354 * Returns: 0 success
599911106dfSjm199354 * < 0 error
600911106dfSjm199354 */
601911106dfSjm199354 static int
vs_icap_send_respmod_hdr(vs_scan_ctx_t * ctx,int ispreview)602911106dfSjm199354 vs_icap_send_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview)
603911106dfSjm199354 {
604911106dfSjm199354 int len;
605911106dfSjm199354
606911106dfSjm199354 if ((len = vs_icap_create_respmod_hdr(ctx, ispreview)) == -1) {
607911106dfSjm199354 /* non SE error */
608911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
609911106dfSjm199354 return (-1);
610911106dfSjm199354 }
611911106dfSjm199354
612911106dfSjm199354 /* send the headers */
613911106dfSjm199354 if (vs_icap_write(ctx->vsc_sockfd,
614911106dfSjm199354 ctx->vsc_info.vsi_send_buf, len) < 0) {
615911106dfSjm199354 return (-1);
616911106dfSjm199354 }
617911106dfSjm199354
618911106dfSjm199354 return (0);
619911106dfSjm199354 }
620911106dfSjm199354
621911106dfSjm199354
622911106dfSjm199354 /*
623911106dfSjm199354 * vs_icap_create_respmod_hdr
624911106dfSjm199354 *
625911106dfSjm199354 * Create the RESPMOD request headers.
626911106dfSjm199354 * - RESPMOD, Host, Allow, [Preview], Encapsulated, encapsulated request hdr,
627911106dfSjm199354 * encapsulated response hdr
628911106dfSjm199354 * Encapsulated data is sent separately subsequent to vs_icap_send_respmod_hdr,
629911106dfSjm199354 * via calls to vs_icap_send_chunk.
630911106dfSjm199354 *
631911106dfSjm199354 * The Symantec ICAP server REQUIRES the resource name (VS_SERVICE_NAME)
632911106dfSjm199354 * after the IP address, otherwise it closes the connection.
633911106dfSjm199354 *
634911106dfSjm199354 * Returns: -1 error
635911106dfSjm199354 * length of headers data
636911106dfSjm199354 */
637911106dfSjm199354 static int
vs_icap_create_respmod_hdr(vs_scan_ctx_t * ctx,int ispreview)638911106dfSjm199354 vs_icap_create_respmod_hdr(vs_scan_ctx_t *ctx, int ispreview)
639911106dfSjm199354 {
640911106dfSjm199354 char my_host_name[MAXHOSTNAMELEN];
641911106dfSjm199354 int hbufsp = VS_BUF_SZ;
642911106dfSjm199354 char *hbuf0 = ctx->vsc_info.vsi_send_buf;
643911106dfSjm199354 char *hbufp = hbuf0;
644911106dfSjm199354 char *encap_hdr, *encap_off0, *req_hdr, *res_hdr, *res_body;
645911106dfSjm199354 int preview_len = ctx->vsc_options.vso_preview_len;
646911106dfSjm199354 int tlen;
647911106dfSjm199354
648911106dfSjm199354 if (gethostname(my_host_name, sizeof (my_host_name)) != 0) {
649911106dfSjm199354 /* non SE error */
650911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_ERROR;
651911106dfSjm199354 return (-1);
652911106dfSjm199354 }
653911106dfSjm199354
654911106dfSjm199354 (void) memset(hbufp, 0, hbufsp);
655911106dfSjm199354
656911106dfSjm199354 /* First the ICAP "request" part. (at offset 0) */
657911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "RESPMOD icap://%s:%d/%s %s\r\n",
658911106dfSjm199354 ctx->vsc_host, ctx->vsc_port, VS_SERVICE_NAME, VS_ICAP_VER);
659911106dfSjm199354 if (tlen >= hbufsp)
660911106dfSjm199354 return (-1);
661911106dfSjm199354 hbufp += tlen; hbufsp -= tlen;
662911106dfSjm199354
663911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "Host: %s\r\n", my_host_name);
664911106dfSjm199354 if (tlen >= hbufsp)
665911106dfSjm199354 return (-1);
666911106dfSjm199354 hbufp += tlen; hbufsp -= tlen;
667911106dfSjm199354
668911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "Allow: 204\r\n");
669911106dfSjm199354 if (tlen >= hbufsp)
670911106dfSjm199354 return (-1);
671911106dfSjm199354 hbufp += tlen; hbufsp -= tlen;
672911106dfSjm199354
673911106dfSjm199354 if (ispreview) {
674911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "Preview: %d\r\n", preview_len);
675911106dfSjm199354 if (tlen >= hbufsp)
676911106dfSjm199354 return (-1);
677911106dfSjm199354 hbufp += tlen; hbufsp -= tlen;
678911106dfSjm199354 }
679911106dfSjm199354
680911106dfSjm199354 /* Reserve space to later insert encapsulation offsets, & blank line */
681911106dfSjm199354 encap_hdr = hbufp;
682911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "%*.*s\r\n\r\n",
683911106dfSjm199354 VS_ENCAP_SZ, VS_ENCAP_SZ, "");
684911106dfSjm199354 if (tlen >= hbufsp)
685911106dfSjm199354 return (-1);
686911106dfSjm199354 hbufp += tlen; hbufsp -= tlen;
687911106dfSjm199354
688911106dfSjm199354 /* "offset zero" for the encapsulated parts that follow */
689911106dfSjm199354 encap_off0 = hbufp;
690911106dfSjm199354
691911106dfSjm199354 /* Encapsulated request header (req_hdr) & blank line */
692911106dfSjm199354 req_hdr = hbufp;
693911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "GET http://%s", my_host_name);
694911106dfSjm199354 if (tlen >= hbufsp)
695911106dfSjm199354 return (-1);
696911106dfSjm199354 hbufp += tlen; hbufsp -= tlen;
697911106dfSjm199354
698911106dfSjm199354 tlen = vs_icap_uri_encode(hbufp, hbufsp, ctx->vsc_fname);
699911106dfSjm199354 if (tlen < 0)
700911106dfSjm199354 return (-1);
701911106dfSjm199354 hbufp += tlen; hbufsp -= tlen;
702911106dfSjm199354
703911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, " HTTP/1.1\r\n\r\n");
704911106dfSjm199354 if (tlen >= hbufsp)
705911106dfSjm199354 return (-1);
706911106dfSjm199354 hbufp += tlen; hbufsp -= tlen;
707911106dfSjm199354
708911106dfSjm199354 /* Encapsulated response header (res_hdr) & blank line */
709911106dfSjm199354 res_hdr = hbufp;
710911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "HTTP/1.1 200 OK\r\n");
711911106dfSjm199354 if (tlen >= hbufsp)
712911106dfSjm199354 return (-1);
713911106dfSjm199354 hbufp += tlen; hbufsp -= tlen;
714911106dfSjm199354
715911106dfSjm199354 tlen = snprintf(hbufp, hbufsp, "Transfer-Encoding: chunked\r\n\r\n");
716911106dfSjm199354 if (tlen >= hbufsp)
717911106dfSjm199354 return (-1);
718911106dfSjm199354 hbufp += tlen; hbufsp -= tlen;
719911106dfSjm199354
720911106dfSjm199354 /* response body section - res-body ("chunked data") */
721911106dfSjm199354 res_body = hbufp;
722911106dfSjm199354
723911106dfSjm199354 /* Insert offsets in encap_hdr */
724911106dfSjm199354 tlen = snprintf(encap_hdr, VS_ENCAP_SZ, "Encapsulated: "
725911106dfSjm199354 "req-hdr=%d, res-hdr=%d, res-body=%d",
726911106dfSjm199354 req_hdr - encap_off0, res_hdr - encap_off0, res_body - encap_off0);
727911106dfSjm199354 /* undo the null from snprintf */
728911106dfSjm199354 encap_hdr[tlen] = ' ';
729911106dfSjm199354
730911106dfSjm199354 /* return length */
731911106dfSjm199354 return (hbufp - hbuf0);
732911106dfSjm199354 }
733911106dfSjm199354
734911106dfSjm199354
735911106dfSjm199354 /*
736911106dfSjm199354 * vs_icap_read_respmod_resp
737911106dfSjm199354 *
738911106dfSjm199354 * Used for both preview and final RESMOD response
739911106dfSjm199354 */
740911106dfSjm199354 static int
vs_icap_read_respmod_resp(vs_scan_ctx_t * ctx)741911106dfSjm199354 vs_icap_read_respmod_resp(vs_scan_ctx_t *ctx)
742911106dfSjm199354 {
743911106dfSjm199354 if (vs_icap_read_resp_code(ctx) < 0)
744911106dfSjm199354 return (-1);
745911106dfSjm199354
746911106dfSjm199354 if (vs_icap_read_hdr(ctx, resp_hdrs, VS_RESP_HDR_MAX) < 0)
747911106dfSjm199354 return (-1);
748911106dfSjm199354
749911106dfSjm199354 if (ctx->vsc_info.vsi_icap_rc == VS_RESP_CONTINUE) {
750911106dfSjm199354 /* A VS_RESP_CONTINUE should not have encapsulated data */
751911106dfSjm199354 if ((ctx->vsc_info.vsi_res_hdr) ||
752911106dfSjm199354 (ctx->vsc_info.vsi_res_body)) {
753911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error -"
754911106dfSjm199354 "- encapsulated data in Continue response");
755911106dfSjm199354 return (-1);
756911106dfSjm199354 }
757911106dfSjm199354 } else {
758911106dfSjm199354 if (vs_icap_set_scan_result(ctx) < 0)
759911106dfSjm199354 return (-1);
760911106dfSjm199354
761911106dfSjm199354 if (ctx->vsc_info.vsi_res_hdr) {
762911106dfSjm199354 if (vs_icap_read_encap_hdr(ctx) < 0)
763911106dfSjm199354 return (-1);
764911106dfSjm199354 }
765911106dfSjm199354
766911106dfSjm199354 if (ctx->vsc_info.vsi_res_body)
767911106dfSjm199354 vs_icap_read_encap_data(ctx);
768911106dfSjm199354 else if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED)
769911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN;
770911106dfSjm199354 }
771911106dfSjm199354
772911106dfSjm199354 return (0);
773911106dfSjm199354 }
774911106dfSjm199354
775911106dfSjm199354
776911106dfSjm199354 /*
777911106dfSjm199354 * vs_icap_read_resp_code
778911106dfSjm199354 *
779911106dfSjm199354 * Get the response code from the icap response messages
780911106dfSjm199354 */
781911106dfSjm199354 static int
vs_icap_read_resp_code(vs_scan_ctx_t * ctx)782911106dfSjm199354 vs_icap_read_resp_code(vs_scan_ctx_t *ctx)
783911106dfSjm199354 {
784911106dfSjm199354 char *buf = ctx->vsc_info.vsi_recv_buf;
785911106dfSjm199354 int retval;
786911106dfSjm199354
787911106dfSjm199354 /* Break on error or non-blank line. */
788911106dfSjm199354 for (;;) {
789911106dfSjm199354 (void) memset(buf, '\0', VS_BUF_SZ);
790911106dfSjm199354
791911106dfSjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
792911106dfSjm199354 return (-1);
793911106dfSjm199354
794911106dfSjm199354 if (retval && buf[0]) {
795911106dfSjm199354 if (MATCH(buf, VS_ICAP_VER)) {
796911106dfSjm199354 (void) sscanf(buf+8, "%d",
797911106dfSjm199354 &ctx->vsc_info.vsi_icap_rc);
798911106dfSjm199354 return (0);
799911106dfSjm199354 }
800911106dfSjm199354
801911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error -"
802911106dfSjm199354 "- expected ICAP/1.0, received %s", buf);
803911106dfSjm199354
804911106dfSjm199354 return (-1);
805911106dfSjm199354 }
806911106dfSjm199354 }
807911106dfSjm199354 }
808911106dfSjm199354
809911106dfSjm199354
810911106dfSjm199354 /*
811911106dfSjm199354 * vs_icap_read_hdr
812911106dfSjm199354 *
813911106dfSjm199354 * Reads all response headers.
814911106dfSjm199354 * As each line is read it is parsed and passed to the appropriate handler.
815911106dfSjm199354 *
816911106dfSjm199354 * Returns: 0 - success
817911106dfSjm199354 * -1 - error
818911106dfSjm199354 */
819911106dfSjm199354 static int
vs_icap_read_hdr(vs_scan_ctx_t * ctx,vs_hdr_t hdrs[],int num_hdrs)820911106dfSjm199354 vs_icap_read_hdr(vs_scan_ctx_t *ctx, vs_hdr_t hdrs[], int num_hdrs)
821911106dfSjm199354 {
822911106dfSjm199354 char *buf = ctx->vsc_info.vsi_recv_buf;
823911106dfSjm199354 int i, retval;
824911106dfSjm199354 char *name, *val;
825911106dfSjm199354
826911106dfSjm199354 /* Break on error or blank line. */
827911106dfSjm199354 for (;;) {
828911106dfSjm199354 (void) memset(buf, '\0', VS_BUF_SZ);
829911106dfSjm199354
830911106dfSjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
831911106dfSjm199354 return (-1);
832911106dfSjm199354
833911106dfSjm199354 /* Empty line (CR/LF) normal break */
834911106dfSjm199354 if ((retval == 0) || (!buf[0]))
835911106dfSjm199354 break;
836911106dfSjm199354
837911106dfSjm199354 vs_icap_parse_hdrs(':', buf, &name, &val);
838911106dfSjm199354
839911106dfSjm199354 for (i = 0; i < num_hdrs; i++) {
840911106dfSjm199354 if (strcmp(name, hdrs[i].vsh_name) == 0) {
841911106dfSjm199354 hdrs[i].vsh_func(ctx, hdrs[i].vsh_id, val);
842911106dfSjm199354 break;
843911106dfSjm199354 }
844911106dfSjm199354 }
845911106dfSjm199354 }
846911106dfSjm199354
847911106dfSjm199354 return ((retval >= 0) ? 0 : -1);
848911106dfSjm199354 }
849911106dfSjm199354
850911106dfSjm199354
851911106dfSjm199354 /*
852911106dfSjm199354 * vs_icap_set_scan_result
853911106dfSjm199354 *
854911106dfSjm199354 * Sets the vs_result_t vsr_rc from the icap_resp_code and
855911106dfSjm199354 * any violation information in vs_result_t
856911106dfSjm199354 *
857911106dfSjm199354 * Returns: 0 - success
858911106dfSjm199354 * -1 - error
859911106dfSjm199354 */
860911106dfSjm199354 static int
vs_icap_set_scan_result(vs_scan_ctx_t * ctx)861911106dfSjm199354 vs_icap_set_scan_result(vs_scan_ctx_t *ctx)
862911106dfSjm199354 {
863911106dfSjm199354 int i;
864911106dfSjm199354 vs_result_t *result = ctx->vsc_result;
865911106dfSjm199354
866911106dfSjm199354 if (!result->vsr_scanstamp)
867911106dfSjm199354 (void) strlcpy(result->vsr_scanstamp,
868911106dfSjm199354 ctx->vsc_options.vso_scanstamp, sizeof (vs_scanstamp_t));
869911106dfSjm199354
870911106dfSjm199354 switch (ctx->vsc_info.vsi_icap_rc) {
871911106dfSjm199354 case VS_RESP_NO_CONT_NEEDED:
872911106dfSjm199354 result->vsr_rc = VS_RESULT_CLEAN;
873911106dfSjm199354 break;
874911106dfSjm199354
875911106dfSjm199354 case VS_RESP_OK:
876911106dfSjm199354 /* if we have no violations , that means all ok */
877911106dfSjm199354 if (result->vsr_nviolations == 0) {
878911106dfSjm199354 result->vsr_rc = VS_RESULT_CLEAN;
879911106dfSjm199354 break;
880911106dfSjm199354 }
881911106dfSjm199354
882911106dfSjm199354 /* Any infections not repaired? */
883911106dfSjm199354 result->vsr_rc = VS_RESULT_CLEANED;
884911106dfSjm199354 for (i = 0; i < result->vsr_nviolations; i++) {
885911106dfSjm199354 if (result->vsr_vrec[i].vr_res !=
886911106dfSjm199354 VS_RES_FILE_REPAIRED) {
887911106dfSjm199354 result->vsr_rc = VS_RESULT_FORBIDDEN;
888911106dfSjm199354 break;
889911106dfSjm199354 }
890911106dfSjm199354 }
891911106dfSjm199354 break;
892911106dfSjm199354
893911106dfSjm199354 case VS_RESP_CREATED :
894911106dfSjm199354 /* file is repaired */
895911106dfSjm199354 result->vsr_rc = VS_RESULT_CLEANED;
896911106dfSjm199354 break;
897911106dfSjm199354
898911106dfSjm199354 case VS_RESP_FORBIDDEN:
899911106dfSjm199354 /* file is infected and could not be repaired */
900911106dfSjm199354 result->vsr_rc = VS_RESULT_FORBIDDEN;
901911106dfSjm199354 break;
902911106dfSjm199354
903911106dfSjm199354 default:
904911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error "
905911106dfSjm199354 "- unsupported scan result: %s",
906911106dfSjm199354 vs_icap_resp_str(ctx->vsc_info.vsi_icap_rc));
907911106dfSjm199354 return (-1);
908911106dfSjm199354 }
909911106dfSjm199354
910911106dfSjm199354 return (0);
911911106dfSjm199354 }
912911106dfSjm199354
913911106dfSjm199354
914911106dfSjm199354 /*
915911106dfSjm199354 * vs_icap_read_encap_hdr
916911106dfSjm199354 *
917911106dfSjm199354 * Read the encapsulated response header to determine the length of
918911106dfSjm199354 * encapsulated data and, in some cases, to detect the infected state
919911106dfSjm199354 * of the file.
920911106dfSjm199354 *
921911106dfSjm199354 * Use of http response code:
922911106dfSjm199354 * Trend IWSS does not return virus information in the RESPMOD response
923911106dfSjm199354 * headers unless the OPTIONAL "include X_Infection_Found" checkbox is
924911106dfSjm199354 * checked and "disable_infected_url_block=yes" is set in intscan.ini.
925911106dfSjm199354 * Thus if we haven't already detected the infected/cleaned status
926911106dfSjm199354 * (ie if vsr_rc == VS_RESULT_CLEAN) we attempt to detect the
927911106dfSjm199354 * infected/cleaned state of a file from a combination of the ICAP and
928911106dfSjm199354 * http resp codes.
929911106dfSjm199354 * Here are the response code values that Trend IWSS returns:
930911106dfSjm199354 * - clean: icap resp = VS_RESP_NO_CONT_NEEDED
931911106dfSjm199354 * - quarantine: icap resp = VS_RESP_OK, http resp = VS_RESP_FORBIDDEN
932911106dfSjm199354 * - cleaned: icap resp = VS_RESP_OK, http resp = VS_RESP_OK
933911106dfSjm199354 * For all other vendors' scan engines (so far) the infected/cleaned
934911106dfSjm199354 * state of the file has already been detected from the RESPMOD
935911106dfSjm199354 * response headers.
936911106dfSjm199354 */
937911106dfSjm199354 static int
vs_icap_read_encap_hdr(vs_scan_ctx_t * ctx)938911106dfSjm199354 vs_icap_read_encap_hdr(vs_scan_ctx_t *ctx)
939911106dfSjm199354 {
940911106dfSjm199354 char *buf = ctx->vsc_info.vsi_recv_buf;
941911106dfSjm199354 char *name, *value;
942911106dfSjm199354 int retval;
943911106dfSjm199354
944911106dfSjm199354 /* Break on error or blank line. */
945911106dfSjm199354 for (;;) {
946911106dfSjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
947911106dfSjm199354 return (-1);
948911106dfSjm199354
949911106dfSjm199354 /* Empty line (CR/LF) normal break */
950911106dfSjm199354 if ((retval == 0) || (!buf[0]))
951911106dfSjm199354 break;
952911106dfSjm199354
953911106dfSjm199354 if (MATCH(buf, "HTTP/1.1")) {
954911106dfSjm199354 (void) sscanf(buf + 8, "%d",
955911106dfSjm199354 &ctx->vsc_info.vsi_http_rc);
956911106dfSjm199354 ctx->vsc_info.vsi_html_content = B_TRUE;
957911106dfSjm199354
958911106dfSjm199354 /* if not yet detected infection, interpret http_rc */
959911106dfSjm199354 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEAN) {
960911106dfSjm199354 if ((ctx->vsc_info.vsi_icap_rc == VS_RESP_OK) &&
961911106dfSjm199354 (ctx->vsc_info.vsi_http_rc == VS_RESP_OK)) {
962911106dfSjm199354 ctx->vsc_result->vsr_rc =
963911106dfSjm199354 VS_RESULT_CLEANED;
964911106dfSjm199354 } else {
965911106dfSjm199354 ctx->vsc_result->vsr_rc =
966911106dfSjm199354 VS_RESULT_FORBIDDEN;
967911106dfSjm199354 }
968911106dfSjm199354 }
969911106dfSjm199354 } else {
970911106dfSjm199354 vs_icap_parse_hdrs(':', buf, &name, &value);
971911106dfSjm199354 if (name && (MATCH(name, "Content-Length"))) {
972911106dfSjm199354 (void) sscanf(value, "%d",
973911106dfSjm199354 &ctx->vsc_info.vsi_content_len);
974911106dfSjm199354 }
975911106dfSjm199354 }
976911106dfSjm199354 }
977911106dfSjm199354
978911106dfSjm199354 return (0);
979911106dfSjm199354 }
980911106dfSjm199354
981911106dfSjm199354
982911106dfSjm199354 /*
983911106dfSjm199354 * vs_icap_read_encap_data
984911106dfSjm199354 *
985911106dfSjm199354 * Read the encapsulated response data.
986911106dfSjm199354 *
987911106dfSjm199354 * If the response data represents cleaned file data (for an infected file)
988911106dfSjm199354 * and VS_NO_REPAIR is not set, open repair file to save the reponse body
989911106dfSjm199354 * data in. Set the repair flag in the scan context. The repair flag is used
990911106dfSjm199354 * during the processing of the response data. If the flag is set then the
991911106dfSjm199354 * data is written to file. If any error occurs which invalidates the repaired
992911106dfSjm199354 * data file the repair flag gets reset to 0, and the data will be discarded.
993911106dfSjm199354 *
994911106dfSjm199354 * The result is reset to VS_RESULT_FORBIDDEN until all of the cleaned data
995911106dfSjm199354 * has been successfully received and processed. It is then reset to
996911106dfSjm199354 * VS_RESULT_CLEANED.
997911106dfSjm199354 *
998911106dfSjm199354 * If the data doesn't represent cleaned file data, or we cannot (or don't
999911106dfSjm199354 * want to) write the cleaned data to file, the data is discarded (repair flag
1000911106dfSjm199354 * in ctx == 0).
1001911106dfSjm199354 */
1002911106dfSjm199354 static void
vs_icap_read_encap_data(vs_scan_ctx_t * ctx)1003911106dfSjm199354 vs_icap_read_encap_data(vs_scan_ctx_t *ctx)
1004911106dfSjm199354 {
1005911106dfSjm199354 if (ctx->vsc_result->vsr_rc == VS_RESULT_CLEANED) {
1006911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_FORBIDDEN;
1007911106dfSjm199354
1008911106dfSjm199354 if (!(ctx->vsc_flags & VS_NO_REPAIR)) {
1009911106dfSjm199354 if (vs_icap_create_repair_file(ctx) == 0)
1010911106dfSjm199354 ctx->vsc_repair = B_TRUE;
1011911106dfSjm199354 }
1012911106dfSjm199354 }
1013911106dfSjm199354
1014911106dfSjm199354 /*
1015911106dfSjm199354 * vs_icap_read_resp_body handles errors internally;
1016911106dfSjm199354 * resets ctx->vsc_repair
1017911106dfSjm199354 */
1018911106dfSjm199354 (void) vs_icap_read_resp_body(ctx);
1019911106dfSjm199354
1020911106dfSjm199354 if (ctx->vsc_repair_fd != -1) {
1021911106dfSjm199354 (void) close(ctx->vsc_repair_fd);
1022911106dfSjm199354
1023911106dfSjm199354 if (ctx->vsc_repair) {
1024911106dfSjm199354 /* repair file contains the cleaned data */
1025911106dfSjm199354 ctx->vsc_result->vsr_rc = VS_RESULT_CLEANED;
1026911106dfSjm199354 } else {
1027911106dfSjm199354 /* error occured processing data. Remove repair file */
1028911106dfSjm199354 (void) unlink(ctx->vsc_repair_fname);
1029911106dfSjm199354 }
1030911106dfSjm199354 }
1031911106dfSjm199354 }
1032911106dfSjm199354
1033911106dfSjm199354
1034911106dfSjm199354 /*
1035911106dfSjm199354 * vs_icap_create_repair_file
1036911106dfSjm199354 *
1037911106dfSjm199354 * Create and open a file to save cleaned data in.
1038911106dfSjm199354 */
1039911106dfSjm199354 static int
vs_icap_create_repair_file(vs_scan_ctx_t * ctx)1040911106dfSjm199354 vs_icap_create_repair_file(vs_scan_ctx_t *ctx)
1041911106dfSjm199354 {
1042911106dfSjm199354 if (ctx->vsc_repair_fname == NULL)
1043911106dfSjm199354 return (-1);
1044911106dfSjm199354
1045911106dfSjm199354 if ((ctx->vsc_repair_fd = open(ctx->vsc_repair_fname,
1046911106dfSjm199354 O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0644)) == -1) {
1047911106dfSjm199354 return (-1);
1048911106dfSjm199354 }
1049911106dfSjm199354
1050911106dfSjm199354 return (0);
1051911106dfSjm199354 }
1052911106dfSjm199354
1053911106dfSjm199354
1054911106dfSjm199354 /*
1055911106dfSjm199354 * vs_icap_read_resp_body
1056911106dfSjm199354 *
1057911106dfSjm199354 * Repeatedly call vs_icap_read_body_chunk until it returns:
1058911106dfSjm199354 * 0 indicating that there's no more data to read or
1059911106dfSjm199354 * -1 indicating a read error -> reset ctx->vsc_repair 0
1060911106dfSjm199354 *
1061911106dfSjm199354 * Returns: 0 success
1062911106dfSjm199354 * -1 error
1063911106dfSjm199354 */
1064911106dfSjm199354 static int
vs_icap_read_resp_body(vs_scan_ctx_t * ctx)1065911106dfSjm199354 vs_icap_read_resp_body(vs_scan_ctx_t *ctx)
1066911106dfSjm199354 {
1067911106dfSjm199354 int retval;
1068911106dfSjm199354
1069911106dfSjm199354 while ((retval = vs_icap_read_body_chunk(ctx)) > 0)
1070911106dfSjm199354 ;
1071911106dfSjm199354
1072911106dfSjm199354 if (retval < 0)
1073911106dfSjm199354 ctx->vsc_repair = B_FALSE;
1074911106dfSjm199354
1075911106dfSjm199354 return (retval);
1076911106dfSjm199354 }
1077911106dfSjm199354
1078911106dfSjm199354
1079911106dfSjm199354 /*
1080911106dfSjm199354 * vs_icap_read_body_chunk
1081911106dfSjm199354 *
1082911106dfSjm199354 * Read the chunk size, then read the chunk of data and write the
1083911106dfSjm199354 * data to file repair_fd (or discard it).
1084911106dfSjm199354 * If the data cannot be successfully written to file, set repair
1085911106dfSjm199354 * flag in ctx to 0, and discard all subsequent data.
1086911106dfSjm199354 *
1087911106dfSjm199354 * Returns: chunk size
1088911106dfSjm199354 * -1 on error
1089911106dfSjm199354 */
1090911106dfSjm199354 static int
vs_icap_read_body_chunk(vs_scan_ctx_t * ctx)1091911106dfSjm199354 vs_icap_read_body_chunk(vs_scan_ctx_t *ctx)
1092911106dfSjm199354 {
1093911106dfSjm199354 char *lbuf = ctx->vsc_info.vsi_recv_buf;
1094911106dfSjm199354 unsigned int chunk_size, resid;
1095911106dfSjm199354 int rsize;
1096911106dfSjm199354
1097911106dfSjm199354 /* Read and parse the chunk size. */
1098911106dfSjm199354 if ((vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0) ||
1099911106dfSjm199354 (!sscanf(lbuf, "%x", &chunk_size))) {
1100911106dfSjm199354 return (-1);
1101911106dfSjm199354 }
1102911106dfSjm199354
1103911106dfSjm199354 /* Read and save/discard chunk */
1104911106dfSjm199354 resid = chunk_size;
1105911106dfSjm199354 while (resid) {
1106911106dfSjm199354 rsize = (resid < VS_BUF_SZ) ? resid : VS_BUF_SZ;
1107911106dfSjm199354
1108911106dfSjm199354 if ((rsize = vs_icap_read(ctx->vsc_sockfd, lbuf, rsize)) <= 0)
1109911106dfSjm199354 return (-1);
1110911106dfSjm199354
1111911106dfSjm199354 if (ctx->vsc_repair) {
1112911106dfSjm199354 if (vs_icap_write(ctx->vsc_repair_fd, lbuf, rsize) < 0)
1113911106dfSjm199354 ctx->vsc_repair = B_FALSE;
1114911106dfSjm199354 }
1115911106dfSjm199354
1116911106dfSjm199354 resid -= rsize;
1117911106dfSjm199354 }
1118911106dfSjm199354
1119911106dfSjm199354 /* Eat one CR/LF after the data */
1120911106dfSjm199354 if (vs_icap_readline(ctx, lbuf, VS_BUF_SZ) < 0)
1121911106dfSjm199354 return (-1);
1122911106dfSjm199354
1123911106dfSjm199354 if (lbuf[0]) {
1124911106dfSjm199354 syslog(LOG_ERR, "ICAP protocol error - expected blank line");
1125911106dfSjm199354 return (-1);
1126911106dfSjm199354 }
1127911106dfSjm199354
1128911106dfSjm199354 return (chunk_size);
1129911106dfSjm199354 }
1130911106dfSjm199354
1131911106dfSjm199354
1132911106dfSjm199354 /* *********************************************************************** */
1133911106dfSjm199354 /* Utility read, write functions */
1134911106dfSjm199354 /* *********************************************************************** */
1135911106dfSjm199354
1136911106dfSjm199354 /*
1137911106dfSjm199354 * vs_icap_write
1138911106dfSjm199354 *
1139911106dfSjm199354 * Return: 0 if all data successfully written
1140911106dfSjm199354 * -1 otherwise
1141911106dfSjm199354 */
1142911106dfSjm199354 static int
vs_icap_write(int fd,char * buf,int buflen)1143911106dfSjm199354 vs_icap_write(int fd, char *buf, int buflen)
1144911106dfSjm199354 {
1145911106dfSjm199354 char *ptr = buf;
1146911106dfSjm199354 int resid = buflen;
1147911106dfSjm199354 int bytes_sent = 0;
1148911106dfSjm199354
1149911106dfSjm199354 while (resid > 0) {
1150911106dfSjm199354 errno = 0;
1151bfc848c6Sjm199354 bytes_sent = write(fd, ptr, resid);
1152911106dfSjm199354 if (bytes_sent < 0) {
1153911106dfSjm199354 if (errno == EINTR)
1154911106dfSjm199354 continue;
1155911106dfSjm199354 else
1156911106dfSjm199354 return (-1);
1157911106dfSjm199354 }
1158911106dfSjm199354 resid -= bytes_sent;
1159911106dfSjm199354 ptr += bytes_sent;
1160911106dfSjm199354 }
1161911106dfSjm199354
1162911106dfSjm199354 return (0);
1163911106dfSjm199354 }
1164911106dfSjm199354
1165911106dfSjm199354
1166911106dfSjm199354 /*
1167911106dfSjm199354 * vs_icap_read
1168911106dfSjm199354 *
1169911106dfSjm199354 * Returns: bytes_read (== len unless EOF hit before len bytes read)
1170911106dfSjm199354 * -1 error
1171911106dfSjm199354 */
1172911106dfSjm199354 static int
vs_icap_read(int fd,char * buf,int len)1173911106dfSjm199354 vs_icap_read(int fd, char *buf, int len)
1174911106dfSjm199354 {
1175911106dfSjm199354 char *ptr = buf;
1176911106dfSjm199354 int resid = len;
1177911106dfSjm199354 int bytes_read = 0;
1178911106dfSjm199354
1179911106dfSjm199354 while (resid > 0) {
1180911106dfSjm199354 errno = 0;
1181bfc848c6Sjm199354 bytes_read = read(fd, ptr, resid);
1182911106dfSjm199354 if (bytes_read < 0) {
1183911106dfSjm199354 if (errno == EINTR)
1184911106dfSjm199354 continue;
1185911106dfSjm199354 else
1186911106dfSjm199354 return (-1);
1187911106dfSjm199354 }
1188911106dfSjm199354 resid -= bytes_read;
1189911106dfSjm199354 ptr += bytes_read;
1190911106dfSjm199354 }
1191911106dfSjm199354
1192911106dfSjm199354 return (len - resid);
1193911106dfSjm199354 }
1194911106dfSjm199354
1195911106dfSjm199354
1196911106dfSjm199354 /*
1197911106dfSjm199354 * vs_icap_send_chunk
1198911106dfSjm199354 *
1199911106dfSjm199354 * Send a "chunk" of file data, containing:
1200911106dfSjm199354 * - Length (in hex) CR/NL
1201911106dfSjm199354 * - [optiona data]
1202911106dfSjm199354 * - CR/NL
1203911106dfSjm199354 *
1204911106dfSjm199354 * Returns: data length sent (not including encapsulation)
1205911106dfSjm199354 * -1 - error
1206911106dfSjm199354 */
1207911106dfSjm199354 static int
vs_icap_send_chunk(vs_scan_ctx_t * ctx,int chunk_len)1208911106dfSjm199354 vs_icap_send_chunk(vs_scan_ctx_t *ctx, int chunk_len)
1209911106dfSjm199354 {
1210911106dfSjm199354 char *hdr = ctx->vsc_info.vsi_send_hdr;
1211911106dfSjm199354 char *dbuf = ctx->vsc_info.vsi_send_buf;
1212911106dfSjm199354 char *tail;
1213911106dfSjm199354 char head[VS_HDR_SZ + 1];
1214911106dfSjm199354 int nread = 0, hlen, tlen = 2;
1215911106dfSjm199354
1216911106dfSjm199354 if (chunk_len > VS_BUF_SZ)
1217911106dfSjm199354 chunk_len = VS_BUF_SZ;
1218911106dfSjm199354
1219911106dfSjm199354 /* Read the data. */
1220911106dfSjm199354 if ((nread = vs_icap_read(ctx->vsc_fd, dbuf, chunk_len)) < 0)
1221911106dfSjm199354 return (-1);
1222911106dfSjm199354
1223911106dfSjm199354 if (nread > 0) {
1224911106dfSjm199354 /* wrap data in a header and trailer */
1225911106dfSjm199354 hlen = snprintf(head, sizeof (head), "%x\r\n", nread);
1226911106dfSjm199354 hdr += (VS_HDR_SZ - hlen);
1227911106dfSjm199354 (void) memcpy(hdr, head, hlen);
1228*db1a607eSjoyce mcintosh tail = dbuf + nread;
1229911106dfSjm199354 tail[0] = '\r';
1230911106dfSjm199354 tail[1] = '\n';
1231911106dfSjm199354
1232911106dfSjm199354 if (vs_icap_write(ctx->vsc_sockfd, hdr,
1233911106dfSjm199354 hlen + nread + tlen) < 0) {
1234911106dfSjm199354 return (-1);
1235911106dfSjm199354 }
1236911106dfSjm199354 }
1237911106dfSjm199354
1238911106dfSjm199354 return (nread);
1239911106dfSjm199354 }
1240911106dfSjm199354
1241911106dfSjm199354
1242911106dfSjm199354 /*
1243911106dfSjm199354 * vs_icap_send_termination
1244911106dfSjm199354 *
1245911106dfSjm199354 * Send 0 length termination to scan engine: "0\r\n\r\n"
1246911106dfSjm199354 *
1247911106dfSjm199354 * Returns: 0 - success
1248911106dfSjm199354 * -1 - error
1249911106dfSjm199354 */
1250911106dfSjm199354 static int
vs_icap_send_termination(vs_scan_ctx_t * ctx)1251911106dfSjm199354 vs_icap_send_termination(vs_scan_ctx_t *ctx)
1252911106dfSjm199354 {
1253911106dfSjm199354 if (vs_icap_write(ctx->vsc_sockfd, VS_TERMINATION,
1254911106dfSjm199354 strlen(VS_TERMINATION)) < 0) {
1255911106dfSjm199354 return (-1);
1256911106dfSjm199354 }
1257911106dfSjm199354
1258911106dfSjm199354 return (0);
1259911106dfSjm199354 }
1260911106dfSjm199354
1261911106dfSjm199354
1262911106dfSjm199354 /*
1263911106dfSjm199354 * vs_icap_readline
1264911106dfSjm199354 *
1265911106dfSjm199354 * Read a line of response data from the socket. \n indicates end of line.
1266911106dfSjm199354 *
1267911106dfSjm199354 * Returns: bytes read
1268911106dfSjm199354 * -1 - error
1269911106dfSjm199354 */
1270911106dfSjm199354 static int
vs_icap_readline(vs_scan_ctx_t * ctx,char * buf,int buflen)1271911106dfSjm199354 vs_icap_readline(vs_scan_ctx_t *ctx, char *buf, int buflen)
1272911106dfSjm199354 {
1273911106dfSjm199354 char c;
1274911106dfSjm199354 int i, retval;
1275911106dfSjm199354
1276911106dfSjm199354 i = 0;
1277911106dfSjm199354 for (;;) {
1278911106dfSjm199354 errno = 0;
1279911106dfSjm199354 retval = recv(ctx->vsc_sockfd, &c, 1, 0);
1280911106dfSjm199354
128153c11029Sjm199354 if (retval < 0 && errno == EINTR)
1282911106dfSjm199354 continue;
1283911106dfSjm199354
128453c11029Sjm199354 if (retval <= 0) {
1285bfc848c6Sjm199354 if (vscand_get_state() != VS_STATE_SHUTDOWN) {
1286bfc848c6Sjm199354 syslog(LOG_ERR, "Error receiving data from "
1287bfc848c6Sjm199354 "Scan Engine: %s", strerror(errno));
1288bfc848c6Sjm199354 }
128953c11029Sjm199354 return (-1);
129053c11029Sjm199354 }
129153c11029Sjm199354
1292911106dfSjm199354 buf[i++] = c;
1293911106dfSjm199354 if (c == '\n')
1294911106dfSjm199354 break;
1295911106dfSjm199354
1296911106dfSjm199354 if (i >= (buflen - 2))
1297911106dfSjm199354 return (-1);
1298911106dfSjm199354 }
1299911106dfSjm199354
1300911106dfSjm199354 buf[i] = '\0';
1301911106dfSjm199354
1302911106dfSjm199354 /* remove preceding and trailing whitespace */
1303911106dfSjm199354 vs_icap_trimspace(buf);
1304911106dfSjm199354
1305911106dfSjm199354 return (i);
1306911106dfSjm199354 }
1307911106dfSjm199354
1308911106dfSjm199354
1309911106dfSjm199354 /* ************************************************************************ */
1310911106dfSjm199354 /* HEADER processing */
1311911106dfSjm199354 /* ************************************************************************ */
1312911106dfSjm199354
1313911106dfSjm199354 /*
1314911106dfSjm199354 * vs_icap_parse_hdrs
1315911106dfSjm199354 *
1316911106dfSjm199354 * parse an icap hdr line to find name and value
1317911106dfSjm199354 */
1318911106dfSjm199354 static void
vs_icap_parse_hdrs(char delimiter,char * line,char ** name,char ** val)1319911106dfSjm199354 vs_icap_parse_hdrs(char delimiter, char *line, char **name, char **val)
1320911106dfSjm199354 {
1321911106dfSjm199354 char *q = line;
1322911106dfSjm199354 int line_len;
1323911106dfSjm199354
1324911106dfSjm199354 /* strip any spaces */
1325911106dfSjm199354 while (*q == ' ')
1326911106dfSjm199354 q++;
1327911106dfSjm199354
1328911106dfSjm199354 *name = q;
1329911106dfSjm199354 *val = 0;
1330911106dfSjm199354
1331911106dfSjm199354 /* Empty line is normal termination */
1332911106dfSjm199354 if ((line_len = strlen(line)) == 0)
1333911106dfSjm199354 return;
1334911106dfSjm199354
1335911106dfSjm199354 if ((q = strchr(line, delimiter)) != 0) {
1336911106dfSjm199354 *q++ = '\0';
1337911106dfSjm199354 } else {
1338911106dfSjm199354 q = line + line_len;
1339911106dfSjm199354 }
1340911106dfSjm199354
1341911106dfSjm199354 /* value part follows spaces */
1342911106dfSjm199354 while (*q == ' ')
1343911106dfSjm199354 q++;
1344911106dfSjm199354
1345911106dfSjm199354 *val = q;
1346911106dfSjm199354 }
1347911106dfSjm199354
1348911106dfSjm199354
1349911106dfSjm199354 /*
1350911106dfSjm199354 * vs_icap_resp_violations
1351911106dfSjm199354 */
1352911106dfSjm199354 /*ARGSUSED*/
1353911106dfSjm199354 static int
vs_icap_resp_violations(vs_scan_ctx_t * ctx,int hdr_id,char * line)1354911106dfSjm199354 vs_icap_resp_violations(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1355911106dfSjm199354 {
1356911106dfSjm199354 int i, rv, vcnt;
1357911106dfSjm199354
1358911106dfSjm199354 (void) sscanf(line, "%d", &vcnt);
1359911106dfSjm199354
1360911106dfSjm199354 ctx->vsc_result->vsr_nviolations =
1361911106dfSjm199354 (vcnt > VS_MAX_VIOLATIONS) ? VS_MAX_VIOLATIONS : vcnt;
1362911106dfSjm199354
1363911106dfSjm199354 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIOLATIONS;
1364911106dfSjm199354
1365911106dfSjm199354 for (i = 0; i < vcnt; i++) {
1366911106dfSjm199354 if ((rv = vs_icap_resp_violation_rec(ctx, i)) < 0)
1367911106dfSjm199354 return (rv);
1368911106dfSjm199354
1369911106dfSjm199354 }
1370911106dfSjm199354
1371911106dfSjm199354 return (1);
1372911106dfSjm199354 }
1373911106dfSjm199354
1374911106dfSjm199354
1375911106dfSjm199354 /*
1376911106dfSjm199354 * vs_icap_resp_violation_rec
1377911106dfSjm199354 *
1378911106dfSjm199354 * take all violation data (up to VS_MAX_VIOLATIONS) and save it
1379911106dfSjm199354 * in violation_info.
1380911106dfSjm199354 * each violation has 4 lines of info: doc name, virus name,
1381911106dfSjm199354 * virus id and resolution
1382911106dfSjm199354 */
1383911106dfSjm199354 static int
vs_icap_resp_violation_rec(vs_scan_ctx_t * ctx,int vr_idx)1384911106dfSjm199354 vs_icap_resp_violation_rec(vs_scan_ctx_t *ctx, int vr_idx)
1385911106dfSjm199354 {
1386911106dfSjm199354 int vline;
1387911106dfSjm199354 int retval = 0;
1388911106dfSjm199354 char *buf = ctx->vsc_info.vsi_recv_buf;
1389911106dfSjm199354 vs_vrec_t *vr;
1390911106dfSjm199354
1391911106dfSjm199354 if (vr_idx < VS_MAX_VIOLATIONS) {
1392911106dfSjm199354 vr = &ctx->vsc_result->vsr_vrec[vr_idx];
1393911106dfSjm199354 } else {
1394911106dfSjm199354 vr = 0;
1395911106dfSjm199354 }
1396911106dfSjm199354
1397911106dfSjm199354 for (vline = 0; vline < VS_VIOLATION_LINES; vline++) {
1398911106dfSjm199354 if ((retval = vs_icap_readline(ctx, buf, VS_BUF_SZ)) < 0)
1399911106dfSjm199354 return (-1);
1400911106dfSjm199354
1401911106dfSjm199354 /* empty line? */
1402911106dfSjm199354 if ((retval == 0) || (!buf[0]))
1403911106dfSjm199354 break;
1404911106dfSjm199354
1405911106dfSjm199354 if (vr) {
1406911106dfSjm199354 switch (vline) {
1407911106dfSjm199354 case 0: /* doc name */
1408911106dfSjm199354 break;
1409911106dfSjm199354 case 1: /* Threat Description */
1410911106dfSjm199354 (void) strlcpy(vr->vr_desc, buf,
1411911106dfSjm199354 VS_DESCRIPTION_MAX);
1412911106dfSjm199354 break;
1413911106dfSjm199354 case 2: /* Problem ID */
1414911106dfSjm199354 (void) sscanf(buf, "%d", &vr->vr_id);
1415911106dfSjm199354 break;
1416911106dfSjm199354 case 3: /* Resolution */
1417911106dfSjm199354 (void) sscanf(buf, "%d", &vr->vr_res);
1418911106dfSjm199354 break;
1419911106dfSjm199354 }
1420911106dfSjm199354 }
1421911106dfSjm199354 }
1422911106dfSjm199354
1423911106dfSjm199354 return (1);
1424911106dfSjm199354 }
1425911106dfSjm199354
1426911106dfSjm199354
1427911106dfSjm199354 /*
1428911106dfSjm199354 * vs_icap_opt_value
1429911106dfSjm199354 * given an icap options hdr string, process value
1430911106dfSjm199354 */
1431911106dfSjm199354 static int
vs_icap_opt_value(vs_scan_ctx_t * ctx,int hdr_id,char * line)1432911106dfSjm199354 vs_icap_opt_value(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1433911106dfSjm199354 {
1434911106dfSjm199354 int x;
1435911106dfSjm199354 long val;
1436911106dfSjm199354 char *end;
1437911106dfSjm199354
1438911106dfSjm199354 switch (hdr_id) {
1439911106dfSjm199354 case VS_OPT_PREVIEW:
1440911106dfSjm199354 (void) sscanf(line, "%d", &x);
1441911106dfSjm199354 if (x < VS_MIN_PREVIEW_LEN)
1442911106dfSjm199354 x = VS_MIN_PREVIEW_LEN;
1443911106dfSjm199354 if (x > VS_BUF_SZ)
1444911106dfSjm199354 x = VS_BUF_SZ;
1445911106dfSjm199354 ctx->vsc_options.vso_preview_len = x;
1446911106dfSjm199354 break;
1447911106dfSjm199354
1448911106dfSjm199354 case VS_OPT_TTL:
1449911106dfSjm199354 if (*line == 0) {
1450911106dfSjm199354 ctx->vsc_options.vso_req_time = -1;
1451911106dfSjm199354 break;
1452911106dfSjm199354 }
1453911106dfSjm199354
1454911106dfSjm199354 val = strtol(line, &end, 10);
1455911106dfSjm199354 if ((end != (line + strlen(line))) || (val < 0))
1456911106dfSjm199354 break;
1457911106dfSjm199354
1458911106dfSjm199354 ctx->vsc_options.vso_ttl = val;
1459911106dfSjm199354 ctx->vsc_options.vso_req_time = time(0);
1460911106dfSjm199354 break;
1461911106dfSjm199354
1462911106dfSjm199354 case VS_OPT_ALLOW:
1463911106dfSjm199354 (void) sscanf(line, "%d", &ctx->vsc_options.vso_allow);
1464911106dfSjm199354 break;
1465911106dfSjm199354
1466911106dfSjm199354 case VS_OPT_SERVICE:
1467911106dfSjm199354 (void) strlcpy(ctx->vsc_options.vso_service, line,
1468911106dfSjm199354 VS_SERVICE_SZ);
1469911106dfSjm199354 break;
1470911106dfSjm199354
1471911106dfSjm199354 case VS_OPT_X_DEF_INFO:
1472911106dfSjm199354 (void) strlcpy(ctx->vsc_options.vso_defninfo, line,
1473911106dfSjm199354 VS_DEFN_SZ);
1474911106dfSjm199354 break;
1475911106dfSjm199354
1476911106dfSjm199354 case VS_OPT_METHODS:
1477911106dfSjm199354 if (strstr(line, "RESPMOD") != NULL)
1478911106dfSjm199354 ctx->vsc_options.vso_respmod = 1;
1479911106dfSjm199354 break;
1480911106dfSjm199354
1481911106dfSjm199354 case VS_OPT_ISTAG:
1482911106dfSjm199354 vs_icap_istag_to_scanstamp(line,
1483911106dfSjm199354 ctx->vsc_options.vso_scanstamp);
1484911106dfSjm199354 break;
1485911106dfSjm199354
1486911106dfSjm199354 default:
1487911106dfSjm199354 break;
1488911106dfSjm199354
1489911106dfSjm199354 }
1490911106dfSjm199354
1491911106dfSjm199354 return (1);
1492911106dfSjm199354 }
1493911106dfSjm199354
1494911106dfSjm199354
1495911106dfSjm199354 /*
1496911106dfSjm199354 * vs_icap_resp_istag
1497911106dfSjm199354 *
1498911106dfSjm199354 * Called to handle ISTAG when received in RESPMOD response.
1499911106dfSjm199354 * - populate result->vsr_scanstamp from istag
1500911106dfSjm199354 * - update the scanstamp in vs_options and log the update.
1501911106dfSjm199354 */
1502911106dfSjm199354 /*ARGSUSED*/
1503911106dfSjm199354 static int
vs_icap_resp_istag(vs_scan_ctx_t * ctx,int hdr_id,char * line)1504911106dfSjm199354 vs_icap_resp_istag(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1505911106dfSjm199354 {
1506911106dfSjm199354 vs_icap_istag_to_scanstamp(line, ctx->vsc_result->vsr_scanstamp);
1507911106dfSjm199354
1508911106dfSjm199354 /* update the scanstamp in vs_options */
1509911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
1510911106dfSjm199354 if (vs_icap_compare_se(ctx->vsc_idx,
1511911106dfSjm199354 ctx->vsc_host, ctx->vsc_port) == 0) {
1512911106dfSjm199354 if (strcmp(vs_options[ctx->vsc_idx].vso_scanstamp,
1513911106dfSjm199354 ctx->vsc_result->vsr_scanstamp) != 0) {
1514911106dfSjm199354 (void) strlcpy(vs_options[ctx->vsc_idx].vso_scanstamp,
1515911106dfSjm199354 ctx->vsc_result->vsr_scanstamp,
1516911106dfSjm199354 sizeof (vs_scanstamp_t));
1517911106dfSjm199354 }
1518911106dfSjm199354 }
1519911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
1520911106dfSjm199354
1521911106dfSjm199354 return (1);
1522911106dfSjm199354 }
1523911106dfSjm199354
1524911106dfSjm199354
1525911106dfSjm199354 /*
1526911106dfSjm199354 * vs_icap_istag_to_scanstamp
1527911106dfSjm199354 *
1528911106dfSjm199354 * Copies istag into scanstamp, stripping leading and trailing
1529911106dfSjm199354 * quotes '"' from istag. If the istag is invalid (too long)
1530911106dfSjm199354 * scanstamp will be left unchanged.
1531911106dfSjm199354 *
1532911106dfSjm199354 * vs_scanstamp_t is defined to be large enough to hold the
1533911106dfSjm199354 * istag plus a null terminator.
1534911106dfSjm199354 */
1535911106dfSjm199354 static void
vs_icap_istag_to_scanstamp(char * istag,vs_scanstamp_t scanstamp)1536911106dfSjm199354 vs_icap_istag_to_scanstamp(char *istag, vs_scanstamp_t scanstamp)
1537911106dfSjm199354 {
1538911106dfSjm199354 char *p = istag;
1539911106dfSjm199354 int len;
1540911106dfSjm199354
1541911106dfSjm199354 /* eliminate preceding '"' */
1542911106dfSjm199354 if (p[0] == '"')
1543911106dfSjm199354 ++p;
1544911106dfSjm199354
1545911106dfSjm199354 /* eliminate trailing '"' */
1546911106dfSjm199354 len = strlen(p);
1547911106dfSjm199354 if (p[len - 1] == '"')
1548911106dfSjm199354 --len;
1549911106dfSjm199354
1550911106dfSjm199354 if (len < sizeof (vs_scanstamp_t))
1551911106dfSjm199354 (void) strlcpy(scanstamp, p, len + 1);
1552911106dfSjm199354 }
1553911106dfSjm199354
1554911106dfSjm199354
1555911106dfSjm199354 /*
1556911106dfSjm199354 * vs_icap_opt_ext
1557911106dfSjm199354 *
1558911106dfSjm199354 * read the transfer preview / transfer complete headers to
1559911106dfSjm199354 * determine which file types can be previewed
1560911106dfSjm199354 */
1561911106dfSjm199354 static int
vs_icap_opt_ext(vs_scan_ctx_t * ctx,int hdr_id,char * line)1562911106dfSjm199354 vs_icap_opt_ext(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1563911106dfSjm199354 {
1564911106dfSjm199354 vs_options_t *opt = &ctx->vsc_options;
1565911106dfSjm199354
1566911106dfSjm199354 switch (hdr_id) {
1567911106dfSjm199354 case VS_OPT_XFER_PREVIEW:
1568911106dfSjm199354 if (opt->vso_xfer_preview) {
1569911106dfSjm199354 free(opt->vso_xfer_preview);
1570911106dfSjm199354 opt->vso_xfer_preview = 0;
1571911106dfSjm199354 }
1572911106dfSjm199354 if (strstr(line, "*")) {
1573911106dfSjm199354 opt->vso_xfer_how = VS_PREVIEW_ALL;
1574911106dfSjm199354 } else {
1575911106dfSjm199354 opt->vso_xfer_preview = vs_icap_make_strvec
1576911106dfSjm199354 (line, EXT_SEPARATOR);
1577911106dfSjm199354 opt->vso_xfer_how = VS_PREVIEW_LIST;
1578911106dfSjm199354 }
1579911106dfSjm199354 break;
1580911106dfSjm199354
1581911106dfSjm199354 case VS_OPT_XFER_COMPLETE :
1582911106dfSjm199354 if (opt->vso_xfer_complete) {
1583911106dfSjm199354 free(opt->vso_xfer_complete);
1584911106dfSjm199354 opt->vso_xfer_complete = 0;
1585911106dfSjm199354 }
1586911106dfSjm199354 if (strstr(line, "*")) {
1587911106dfSjm199354 opt->vso_xfer_how = VS_PREVIEW_NONE;
1588911106dfSjm199354 } else {
1589911106dfSjm199354 opt->vso_xfer_complete = vs_icap_make_strvec
1590911106dfSjm199354 (line, EXT_SEPARATOR);
1591911106dfSjm199354 opt->vso_xfer_how = VS_PREVIEW_EXCEPT;
1592911106dfSjm199354 }
1593911106dfSjm199354 break;
1594911106dfSjm199354 default:
1595911106dfSjm199354 break;
1596911106dfSjm199354 }
1597911106dfSjm199354
1598911106dfSjm199354 return (1);
1599911106dfSjm199354 }
1600911106dfSjm199354
1601911106dfSjm199354
1602911106dfSjm199354 /*
1603911106dfSjm199354 * vs_icap_resp_infection
1604911106dfSjm199354 *
1605911106dfSjm199354 * read the type, resolution and threat description for each
1606911106dfSjm199354 * reported violation and save in ctx->vsc_result
1607911106dfSjm199354 */
1608911106dfSjm199354 /*ARGSUSED*/
1609911106dfSjm199354 static int
vs_icap_resp_infection(vs_scan_ctx_t * ctx,int hdr_id,char * line)1610911106dfSjm199354 vs_icap_resp_infection(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1611911106dfSjm199354 {
1612911106dfSjm199354 char *name, *val;
1613911106dfSjm199354 int i, got = 0;
1614911106dfSjm199354 int type = 0, res = 0;
1615911106dfSjm199354 char *desc = 0;
1616911106dfSjm199354 vs_vrec_t *vr = 0;
1617911106dfSjm199354
1618911106dfSjm199354 for (i = 0; i < VS_INFECTION_FIELDS; i++) {
1619911106dfSjm199354 vs_icap_parse_hdrs('=', line, &name, &val);
1620911106dfSjm199354
1621911106dfSjm199354 switch (i) {
1622911106dfSjm199354 case 0:
1623911106dfSjm199354 if (MATCH(name, "Type")) {
1624911106dfSjm199354 (void) sscanf(val, "%d", &type);
1625911106dfSjm199354 got++;
1626911106dfSjm199354 }
1627911106dfSjm199354 break;
1628911106dfSjm199354 case 1:
1629911106dfSjm199354 if (MATCH(name, "Resolution")) {
1630911106dfSjm199354 (void) sscanf(val, "%d", &res);
1631911106dfSjm199354 got++;
1632911106dfSjm199354 }
1633911106dfSjm199354 break;
1634911106dfSjm199354 case 2:
1635911106dfSjm199354 if (MATCH(name, "Threat")) {
1636911106dfSjm199354 desc = val;
1637911106dfSjm199354 got++;
1638911106dfSjm199354 }
1639911106dfSjm199354 break;
1640911106dfSjm199354 default :
1641911106dfSjm199354 break;
1642911106dfSjm199354 }
1643911106dfSjm199354
1644911106dfSjm199354 if ((line = strstr(val, ";")))
1645911106dfSjm199354 line++;
1646911106dfSjm199354 }
1647911106dfSjm199354
1648911106dfSjm199354 if (got != VS_INFECTION_FIELDS)
1649911106dfSjm199354 return (0);
1650911106dfSjm199354
1651911106dfSjm199354 /*
1652911106dfSjm199354 * We may have info from an X-Violations-Found record, (which provides
1653911106dfSjm199354 * more complete information). If so, don't destroy what we have.
1654911106dfSjm199354 */
1655911106dfSjm199354 if ((ctx->vsc_result->vsr_nviolations == 0) ||
1656911106dfSjm199354 (ctx->vsc_info.vsi_threat_hdr < VS_RESP_X_INFECTION)) {
1657911106dfSjm199354 vr = &ctx->vsc_result->vsr_vrec[0];
1658911106dfSjm199354 vr->vr_id = type;
1659911106dfSjm199354 vr->vr_res = res;
1660911106dfSjm199354 (void) strlcpy(vr->vr_desc, desc, VS_DESCRIPTION_MAX);
1661911106dfSjm199354 ctx->vsc_result->vsr_nviolations = 1;
1662911106dfSjm199354
1663911106dfSjm199354 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_INFECTION;
1664911106dfSjm199354 }
1665911106dfSjm199354
1666911106dfSjm199354 return (1);
1667911106dfSjm199354 }
1668911106dfSjm199354
1669911106dfSjm199354
1670911106dfSjm199354 /*
1671911106dfSjm199354 * vs_icap_resp_virus_id
1672911106dfSjm199354 *
1673911106dfSjm199354 * X-Virus-ID is defined as being a shorter alternative to X-Infection-Found.
1674911106dfSjm199354 * If we already have virus information, from either X-Infection-Found or
1675911106dfSjm199354 * X-Violations-Found, it will be more complete, so don't overwrite it with
1676911106dfSjm199354 * the info from X-Virus-ID.
1677911106dfSjm199354 */
1678911106dfSjm199354 /*ARGSUSED*/
1679911106dfSjm199354 static int
vs_icap_resp_virus_id(vs_scan_ctx_t * ctx,int hdr_id,char * line)1680911106dfSjm199354 vs_icap_resp_virus_id(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1681911106dfSjm199354 {
1682911106dfSjm199354 vs_vrec_t *vr = 0;
1683911106dfSjm199354
1684911106dfSjm199354 if (ctx->vsc_result->vsr_nviolations == 0) {
1685911106dfSjm199354 vr = &ctx->vsc_result->vsr_vrec[0];
1686911106dfSjm199354 vr->vr_id = 0;
1687911106dfSjm199354 vr->vr_res = 0;
1688911106dfSjm199354 (void) strlcpy(vr->vr_desc, line, VS_DESCRIPTION_MAX);
1689911106dfSjm199354 ctx->vsc_result->vsr_nviolations = 1;
1690911106dfSjm199354
1691911106dfSjm199354 ctx->vsc_info.vsi_threat_hdr = VS_RESP_X_VIRUS_ID;
1692911106dfSjm199354 }
1693911106dfSjm199354
1694911106dfSjm199354 return (1);
1695911106dfSjm199354 }
1696911106dfSjm199354
1697911106dfSjm199354
1698911106dfSjm199354 /*
1699911106dfSjm199354 * vs_icap_resp_encap
1700911106dfSjm199354 *
1701911106dfSjm199354 * get the encapsulated header info
1702911106dfSjm199354 */
1703911106dfSjm199354 /*ARGSUSED*/
1704911106dfSjm199354 static int
vs_icap_resp_encap(vs_scan_ctx_t * ctx,int hdr_id,char * line)1705911106dfSjm199354 vs_icap_resp_encap(vs_scan_ctx_t *ctx, int hdr_id, char *line)
1706911106dfSjm199354 {
1707911106dfSjm199354 if (strstr(line, "res-hdr"))
1708911106dfSjm199354 ctx->vsc_info.vsi_res_hdr = B_TRUE;
1709911106dfSjm199354
1710911106dfSjm199354 if (strstr(line, "res-body"))
1711911106dfSjm199354 ctx->vsc_info.vsi_res_body = B_TRUE;
1712911106dfSjm199354
1713911106dfSjm199354 return (1);
1714911106dfSjm199354 }
1715911106dfSjm199354
1716911106dfSjm199354
1717911106dfSjm199354 /*
1718911106dfSjm199354 * Utility functions for handling OPTIONS data: vs_options_t
1719911106dfSjm199354 */
1720911106dfSjm199354
1721911106dfSjm199354 /*
1722911106dfSjm199354 * vs_icap_compare_scanstamp
1723911106dfSjm199354 * compare scanstamp with that stored for engine idx
1724911106dfSjm199354 *
1725911106dfSjm199354 * Returns: 0 - if equal
1726911106dfSjm199354 */
1727911106dfSjm199354 int
vs_icap_compare_scanstamp(int idx,vs_scanstamp_t scanstamp)1728911106dfSjm199354 vs_icap_compare_scanstamp(int idx, vs_scanstamp_t scanstamp)
1729911106dfSjm199354 {
1730911106dfSjm199354 int rc;
1731911106dfSjm199354
1732911106dfSjm199354 if (!scanstamp || scanstamp[0] == '\0')
1733911106dfSjm199354 return (-1);
1734911106dfSjm199354
1735911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
1736911106dfSjm199354 rc = strcmp(scanstamp, vs_options[idx].vso_scanstamp);
1737911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
1738911106dfSjm199354
1739911106dfSjm199354 return (rc);
1740911106dfSjm199354 }
1741911106dfSjm199354
1742911106dfSjm199354
1743911106dfSjm199354 /*
1744911106dfSjm199354 * vs_icap_compare_se
1745911106dfSjm199354 * compare host and port with that stored for engine idx
1746911106dfSjm199354 *
1747911106dfSjm199354 * Returns: 0 - if equal
1748911106dfSjm199354 */
1749911106dfSjm199354 static int
vs_icap_compare_se(int idx,char * host,int port)1750911106dfSjm199354 vs_icap_compare_se(int idx, char *host, int port)
1751911106dfSjm199354 {
1752911106dfSjm199354 if (vs_options[idx].vso_port != port)
1753911106dfSjm199354 return (-1);
1754911106dfSjm199354
1755911106dfSjm199354 if (strcmp(vs_options[idx].vso_host, host) != 0)
1756911106dfSjm199354 return (-1);
1757911106dfSjm199354
1758911106dfSjm199354 return (0);
1759911106dfSjm199354 }
1760911106dfSjm199354
1761911106dfSjm199354
1762911106dfSjm199354 /*
1763911106dfSjm199354 * vs_icap_free_options
1764911106dfSjm199354 *
1765911106dfSjm199354 * Free dynamic parts of vs_options_t: xfer_preview, xfer_complete
1766911106dfSjm199354 */
1767911106dfSjm199354 static void
vs_icap_free_options(vs_options_t * options)1768911106dfSjm199354 vs_icap_free_options(vs_options_t *options)
1769911106dfSjm199354 {
1770911106dfSjm199354 if (options->vso_xfer_preview)
1771911106dfSjm199354 free(options->vso_xfer_preview);
1772911106dfSjm199354
1773911106dfSjm199354 if (options->vso_xfer_complete)
1774911106dfSjm199354 free(options->vso_xfer_complete);
1775911106dfSjm199354
1776911106dfSjm199354 (void) memset(options, 0, sizeof (vs_options_t));
1777911106dfSjm199354 }
1778911106dfSjm199354
1779911106dfSjm199354
1780911106dfSjm199354 /*
1781911106dfSjm199354 * vs_icap_copy_options
1782911106dfSjm199354 */
1783911106dfSjm199354 void
vs_icap_copy_options(vs_options_t * to_opt,vs_options_t * from_opt)1784911106dfSjm199354 vs_icap_copy_options(vs_options_t *to_opt, vs_options_t *from_opt)
1785911106dfSjm199354 {
1786911106dfSjm199354 *to_opt = *from_opt;
1787911106dfSjm199354
1788911106dfSjm199354 if (from_opt->vso_xfer_preview) {
1789911106dfSjm199354 to_opt->vso_xfer_preview =
1790911106dfSjm199354 vs_icap_copy_strvec(from_opt->vso_xfer_preview);
1791911106dfSjm199354 }
1792911106dfSjm199354
1793911106dfSjm199354 if (from_opt->vso_xfer_complete) {
1794911106dfSjm199354 to_opt->vso_xfer_complete =
1795911106dfSjm199354 vs_icap_copy_strvec(from_opt->vso_xfer_complete);
1796911106dfSjm199354 }
1797911106dfSjm199354 }
1798911106dfSjm199354
1799911106dfSjm199354
1800911106dfSjm199354 /*
1801911106dfSjm199354 * vs_icap_update_options
1802911106dfSjm199354 */
1803911106dfSjm199354 static void
vs_icap_update_options(vs_scan_ctx_t * ctx)1804911106dfSjm199354 vs_icap_update_options(vs_scan_ctx_t *ctx)
1805911106dfSjm199354 {
1806911106dfSjm199354 int idx = ctx->vsc_idx;
1807911106dfSjm199354
1808911106dfSjm199354 (void) pthread_mutex_lock(&vs_opt_mutex);
1809911106dfSjm199354
1810911106dfSjm199354 if (vs_icap_compare_se(idx, ctx->vsc_host, ctx->vsc_port) == 0) {
1811911106dfSjm199354 vs_icap_free_options(&vs_options[idx]);
1812911106dfSjm199354 vs_icap_copy_options(&vs_options[idx], &ctx->vsc_options);
1813911106dfSjm199354 }
1814911106dfSjm199354
1815911106dfSjm199354 (void) pthread_mutex_unlock(&vs_opt_mutex);
1816911106dfSjm199354 }
1817911106dfSjm199354
1818911106dfSjm199354
1819911106dfSjm199354 /*
1820911106dfSjm199354 * vs_icap_make_strvec
1821911106dfSjm199354 *
1822911106dfSjm199354 * Populate a iovec_t from line, where line is a string of 'sep'
1823911106dfSjm199354 * separated fields. Within the copy of line in the iovec_t each
1824911106dfSjm199354 * field will be null terminated with leading & trailing whitespace
1825911106dfSjm199354 * removed. This allows for fast searching.
1826911106dfSjm199354 *
1827911106dfSjm199354 * The iovec_t itself and the data it points to are allocated
1828911106dfSjm199354 * as a single chunk.
1829911106dfSjm199354 */
1830911106dfSjm199354 static iovec_t *
vs_icap_make_strvec(char * line,const char * sep)1831911106dfSjm199354 vs_icap_make_strvec(char *line, const char *sep)
1832911106dfSjm199354 {
1833911106dfSjm199354 iovec_t *vec;
1834911106dfSjm199354 char *tmp, *ctx;
1835911106dfSjm199354 int datalen, len;
1836911106dfSjm199354
1837911106dfSjm199354 datalen = strlen(line) + 1;
1838911106dfSjm199354 len = sizeof (iovec_t) + datalen;
1839911106dfSjm199354
1840911106dfSjm199354 if ((vec = (iovec_t *)calloc(1, len)) == 0)
1841911106dfSjm199354 return (0);
1842911106dfSjm199354
1843911106dfSjm199354 vec->iov_len = len;
1844911106dfSjm199354 vec->iov_base = (char *)vec + sizeof (iovec_t);
1845911106dfSjm199354 (void) strlcpy(vec->iov_base, line, datalen);
1846911106dfSjm199354
1847911106dfSjm199354 /* tokenize data for easier searching */
1848911106dfSjm199354 for (tmp = strtok_r(vec->iov_base, sep, &ctx); tmp;
1849911106dfSjm199354 tmp = strtok_r(0, sep, &ctx)) {
1850911106dfSjm199354 }
1851911106dfSjm199354
1852911106dfSjm199354 return (vec);
1853911106dfSjm199354 }
1854911106dfSjm199354
1855911106dfSjm199354
1856911106dfSjm199354 /*
1857911106dfSjm199354 * vs_icap_copy_strvec
1858911106dfSjm199354 *
1859911106dfSjm199354 * allocate and copy strvec
1860911106dfSjm199354 */
1861911106dfSjm199354 static iovec_t *
vs_icap_copy_strvec(iovec_t * from_vec)1862911106dfSjm199354 vs_icap_copy_strvec(iovec_t *from_vec)
1863911106dfSjm199354 {
1864911106dfSjm199354 iovec_t *to_vec;
1865911106dfSjm199354
1866911106dfSjm199354 if ((to_vec = (iovec_t *)calloc(1, from_vec->iov_len)) == 0)
1867911106dfSjm199354 return (0);
1868911106dfSjm199354
1869911106dfSjm199354 bcopy(from_vec, to_vec, from_vec->iov_len);
1870911106dfSjm199354 to_vec->iov_base = (char *)to_vec + sizeof (iovec_t);
1871911106dfSjm199354
1872911106dfSjm199354 return (to_vec);
1873911106dfSjm199354 }
1874911106dfSjm199354
1875911106dfSjm199354
1876911106dfSjm199354 /*
1877911106dfSjm199354 * vs_icap_check_ext
1878911106dfSjm199354 *
1879911106dfSjm199354 * Returns: 1 - if ext in strvec
1880911106dfSjm199354 * 0 - otherwise
1881911106dfSjm199354 */
1882911106dfSjm199354 static int
vs_icap_check_ext(char * ext,iovec_t * vec)1883911106dfSjm199354 vs_icap_check_ext(char *ext, iovec_t *vec)
1884911106dfSjm199354 {
1885911106dfSjm199354 char *p, *end = (char *)vec + vec->iov_len;
1886911106dfSjm199354
1887911106dfSjm199354 for (p = vec->iov_base; p < end; p += strlen(p) + 1) {
1888911106dfSjm199354 if (MATCH(ext, p))
1889911106dfSjm199354 return (1);
1890911106dfSjm199354 }
1891911106dfSjm199354
1892911106dfSjm199354 return (0);
1893911106dfSjm199354 }
1894911106dfSjm199354
1895911106dfSjm199354
1896911106dfSjm199354 /*
1897911106dfSjm199354 * vs_icap_resp_str
1898911106dfSjm199354 */
1899911106dfSjm199354 static char *
vs_icap_resp_str(int rc)1900911106dfSjm199354 vs_icap_resp_str(int rc)
1901911106dfSjm199354 {
1902911106dfSjm199354 vs_resp_msg_t *p = icap_resp;
1903911106dfSjm199354
1904911106dfSjm199354 if (rc < 0)
1905911106dfSjm199354 rc = -rc;
1906911106dfSjm199354
1907911106dfSjm199354 while (p->vsm_rc != VS_RESP_UNKNOWN) {
1908911106dfSjm199354 if (p->vsm_rc == rc)
1909911106dfSjm199354 break;
1910911106dfSjm199354 p++;
1911911106dfSjm199354 }
1912911106dfSjm199354
1913911106dfSjm199354 return (p->vsm_msg);
1914911106dfSjm199354 }
1915911106dfSjm199354
1916911106dfSjm199354
1917911106dfSjm199354 /*
1918911106dfSjm199354 * vs_icap_trimspace
1919911106dfSjm199354 *
1920911106dfSjm199354 * Trims whitespace from both the beginning and end of a string. This
1921911106dfSjm199354 * function alters the string buffer in-place.
1922911106dfSjm199354 *
1923911106dfSjm199354 * Whitespaces found at the beginning of the string are eliminated by
1924911106dfSjm199354 * moving forward the start of the string at the first non-whitespace
1925911106dfSjm199354 * character.
1926911106dfSjm199354 * Whitespace found at the end of the string are overwritten with nulls.
1927911106dfSjm199354 *
1928911106dfSjm199354 */
1929911106dfSjm199354 static void
vs_icap_trimspace(char * buf)1930911106dfSjm199354 vs_icap_trimspace(char *buf)
1931911106dfSjm199354 {
1932911106dfSjm199354 char *p = buf;
1933911106dfSjm199354 char *q = buf;
1934911106dfSjm199354
1935911106dfSjm199354 if (buf == 0)
1936911106dfSjm199354 return;
1937911106dfSjm199354
1938911106dfSjm199354 while (*p && isspace(*p))
1939911106dfSjm199354 ++p;
1940911106dfSjm199354
1941911106dfSjm199354 while ((*q = *p++) != 0)
1942911106dfSjm199354 ++q;
1943911106dfSjm199354
1944911106dfSjm199354 if (q != buf) {
1945911106dfSjm199354 while ((--q, isspace(*q)) != 0)
1946911106dfSjm199354 *q = '\0';
1947911106dfSjm199354 }
1948911106dfSjm199354 }
1949911106dfSjm199354
1950911106dfSjm199354
1951911106dfSjm199354 /*
1952911106dfSjm199354 * vs_icap_uri_encode
1953911106dfSjm199354 *
1954911106dfSjm199354 * Encode uri data (eg filename) in accordance with RFC 2396
1955911106dfSjm199354 * 'Illegal' characters should be replaced with %hh, where hh is
1956911106dfSjm199354 * the hex value of the character. For example a space would be
1957911106dfSjm199354 * replaced with %20.
1958911106dfSjm199354 * Filenames are all already UTF-8 encoded. Any UTF-8 octects that
1959911106dfSjm199354 * are 'illegal' characters will be encoded as described above.
1960911106dfSjm199354 *
1961911106dfSjm199354 * Paramaters: data - string to be encoded (NULL terminated)
1962911106dfSjm199354 * buf - output buffer (NULL terminated)
1963911106dfSjm199354 * size - size of output buffer
1964911106dfSjm199354 *
1965911106dfSjm199354 * Returns: strlen of encoded data on success
1966911106dfSjm199354 * -1 size on error (contents of buf undefined)
1967911106dfSjm199354 */
1968911106dfSjm199354 static int
vs_icap_uri_encode(char * buf,int size,char * data)1969911106dfSjm199354 vs_icap_uri_encode(char *buf, int size, char *data)
1970911106dfSjm199354 {
1971911106dfSjm199354 unsigned char *iptr;
1972911106dfSjm199354 char *optr = buf;
1973911106dfSjm199354 int len = strlen(data);
1974911106dfSjm199354
1975911106dfSjm199354 /* modify the data */
1976911106dfSjm199354 for (iptr = (unsigned char *)data; *iptr; iptr++) {
1977911106dfSjm199354 if (vs_icap_uri_illegal_char(*iptr)) {
1978911106dfSjm199354 if ((len += 2) >= size)
1979911106dfSjm199354 return (-1);
1980911106dfSjm199354 (void) sprintf(optr, "%%%0x", *iptr);
1981911106dfSjm199354 optr += 3;
1982911106dfSjm199354 } else {
1983911106dfSjm199354 if (len >= size)
1984911106dfSjm199354 return (-1);
1985911106dfSjm199354 *optr++ = *iptr;
1986911106dfSjm199354 }
1987911106dfSjm199354 }
1988911106dfSjm199354
1989911106dfSjm199354 *optr = '\0';
1990911106dfSjm199354 return (len);
1991911106dfSjm199354 }
1992911106dfSjm199354
1993911106dfSjm199354
1994911106dfSjm199354 /*
1995911106dfSjm199354 * vs_icap_uri_illegal_char
1996911106dfSjm199354 *
1997911106dfSjm199354 * The following us-ascii characters (UTF-8 octets) are 'illegal':
1998911106dfSjm199354 * < > # % " { } | \ ^ [ ] ` space, 0x01 -> 0x1F & 0x7F
1999911106dfSjm199354 * All non us-ascii UTF-8 octets ( >= 0x80) are illegal.
2000911106dfSjm199354 *
2001911106dfSjm199354 * Returns: 1 if character is not allowed in a URI
2002911106dfSjm199354 * 0 otherwise
2003911106dfSjm199354 */
2004911106dfSjm199354 static int
vs_icap_uri_illegal_char(char c)2005911106dfSjm199354 vs_icap_uri_illegal_char(char c)
2006911106dfSjm199354 {
2007911106dfSjm199354 static const char *uri_illegal_chars = "<>#%\" {}|\\^[]`";
2008911106dfSjm199354
2009911106dfSjm199354 /* us-ascii non printable characters or non us-ascii */
2010911106dfSjm199354 if ((c <= 0x1F) || (c >= 0x7F))
2011911106dfSjm199354 return (1);
2012911106dfSjm199354
2013911106dfSjm199354 /* us-ascii dis-allowed characters */
2014911106dfSjm199354 if (strchr(uri_illegal_chars, c))
2015911106dfSjm199354 return (1);
2016911106dfSjm199354
2017911106dfSjm199354 return (0);
2018911106dfSjm199354
2019911106dfSjm199354 }
2020