xref: /onnv-gate/usr/src/common/net/wanboot/boot_http.c (revision 13093:48f2dbca79a2)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*13093SRoger.Faulkner@Oracle.COM  * Common Development and Distribution License (the "License").
6*13093SRoger.Faulkner@Oracle.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
21*13093SRoger.Faulkner@Oracle.COM 
220Sstevel@tonic-gate /*
23*13093SRoger.Faulkner@Oracle.COM  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <errno.h>
270Sstevel@tonic-gate #include <sys/types.h>
280Sstevel@tonic-gate #include <sys/socket.h>
290Sstevel@tonic-gate #include <netinet/in.h>
300Sstevel@tonic-gate #include <arpa/inet.h>
310Sstevel@tonic-gate #include <ctype.h>
320Sstevel@tonic-gate #include <stdio.h>
330Sstevel@tonic-gate #include <strings.h>
340Sstevel@tonic-gate #include <stdlib.h>
350Sstevel@tonic-gate #include <netdb.h>
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #include <openssl/ssl.h>
380Sstevel@tonic-gate #include <openssl/err.h>
390Sstevel@tonic-gate #include <openssl/rand.h>
400Sstevel@tonic-gate #include <openssl/pkcs12.h>
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /* this must be included after ssl.h to avoid re-defining 'offsetof' */
430Sstevel@tonic-gate #include <sys/sysmacros.h>
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #include <boot_http.h>
460Sstevel@tonic-gate #include <socket_inet.h>
470Sstevel@tonic-gate #include <p12access.h>
480Sstevel@tonic-gate 
490Sstevel@tonic-gate #include "bootlog.h"
500Sstevel@tonic-gate 
510Sstevel@tonic-gate #define	BOOT_HTTP_MAJOR_VERSION	1
520Sstevel@tonic-gate #define	BOOT_HTTP_MINOR_VERSION	0
530Sstevel@tonic-gate #define	BOOT_HTTP_MICRO_VERSION	0
540Sstevel@tonic-gate 
550Sstevel@tonic-gate static boot_http_ver_t boot_http_ver = {
560Sstevel@tonic-gate 	BOOT_HTTP_MAJOR_VERSION,
570Sstevel@tonic-gate 	BOOT_HTTP_MINOR_VERSION,
580Sstevel@tonic-gate 	BOOT_HTTP_MICRO_VERSION
590Sstevel@tonic-gate };
600Sstevel@tonic-gate 
610Sstevel@tonic-gate static int	early_err;	/* Error from before error occurred */
620Sstevel@tonic-gate 
630Sstevel@tonic-gate static boolean_t verbosemode = B_FALSE;
640Sstevel@tonic-gate static char	*cipher_list = NULL; /* Ciphers supported (if not default) */
650Sstevel@tonic-gate 
660Sstevel@tonic-gate typedef struct {
670Sstevel@tonic-gate 	int	i;		/* current position in buffer */
680Sstevel@tonic-gate 	int	n;		/* number of bytes in buffer */
690Sstevel@tonic-gate 	char	buf[512];	/* buffer */
700Sstevel@tonic-gate } buf_struct_t;
710Sstevel@tonic-gate 
720Sstevel@tonic-gate typedef struct {
730Sstevel@tonic-gate 	uint_t	errsrc;		/* Source of this error */
740Sstevel@tonic-gate 	ulong_t	error;		/* Which error? */
750Sstevel@tonic-gate } errent_t;
760Sstevel@tonic-gate 
770Sstevel@tonic-gate 
780Sstevel@tonic-gate typedef enum {
790Sstevel@tonic-gate 	HTTP_REQ_TYPE_HEAD = 1,
800Sstevel@tonic-gate 	HTTP_REQ_TYPE_GET
810Sstevel@tonic-gate } http_req_t;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate #define	FAILSAFE 20		/* Max # empty lines to accept */
840Sstevel@tonic-gate #define	DEFAULT_TIMEOUT	10	/* Default socket read timeout value */
850Sstevel@tonic-gate #define	HTTP_CONN_INFO 0x90919293 /* Identifies a http_conn_t struct */
860Sstevel@tonic-gate #define	ESTACK_SIZE	20	/* Size of the stack */
870Sstevel@tonic-gate 
880Sstevel@tonic-gate typedef struct http_conn_t {
890Sstevel@tonic-gate 	uint_t	signature;	/* Cookie indicating this is a handle */
900Sstevel@tonic-gate 	int	fd;		/* Connection's fd... */
910Sstevel@tonic-gate 	SSL_CTX *ctx;
920Sstevel@tonic-gate 	void	*ssl;		/* Handle to ssl data structure */
930Sstevel@tonic-gate 	int	read_timeout;	/* Timeout to use on read requests in sec */
940Sstevel@tonic-gate 	char    *basic_auth_userid;   /* Basic authentication user ID */
950Sstevel@tonic-gate 	char   	*basic_auth_password; /* and password */
960Sstevel@tonic-gate 	char	is_multipart;	/* B_TRUE if doing multipart/mixed download */
970Sstevel@tonic-gate 	char	is_firstpart;	/* B_TRUE if first part in a multipart xfer */
980Sstevel@tonic-gate 	char	is_firstchunk;	/* B_TRUE if first chunk in chunked xfer */
990Sstevel@tonic-gate 	char	is_chunked;	/* B_TRUE if message body is chunked */
1000Sstevel@tonic-gate 	boolean_t keepalive;
1010Sstevel@tonic-gate 	struct	sockaddr_in  host_addr; /* Address of host */
1020Sstevel@tonic-gate 	url_t		uri;   		/* The current URI */
1030Sstevel@tonic-gate 	url_hport_t	proxy;		/* The proxy info */
1040Sstevel@tonic-gate 	boolean_t 	proxied;	/* Connection is proxied */
1050Sstevel@tonic-gate 	char	*random_file;	/* File with seed info for pseudo random  */
1060Sstevel@tonic-gate 				/* number generator */
1070Sstevel@tonic-gate 	char	*client_cert_file;	/* File holding client's certificate */
1080Sstevel@tonic-gate 	char	*private_key_file;	/* File with the private key */
1090Sstevel@tonic-gate 	char	*file_password;	/* file with password to key or pkcs12 file. */
1100Sstevel@tonic-gate 	http_respinfo_t resp;	/* Response summary info */
1110Sstevel@tonic-gate 	char	**resphdr;	/* Array of header response lines */
1120Sstevel@tonic-gate 	buf_struct_t inbuf;
1130Sstevel@tonic-gate 	char	*boundary;	/* Boundary text (multipart downloads only) */
1140Sstevel@tonic-gate 	uint_t	boundary_len;	/* Length of boundary string */
1150Sstevel@tonic-gate 	uint_t	numerrs;
1160Sstevel@tonic-gate 	uint_t	nexterr;	/* Next error to return */
1170Sstevel@tonic-gate 	ssize_t	body_size;	/* Size of message body or chunk */
1180Sstevel@tonic-gate 	ssize_t	body_read;	/* # of bytes of body_size processed */
1190Sstevel@tonic-gate 	ssize_t	body_size_tot;	/* Total message body size */
1200Sstevel@tonic-gate 	ssize_t	body_read_tot;	/* # of bytes of body_size_tot processed */
1210Sstevel@tonic-gate 	errent_t errs[ESTACK_SIZE]; /* stack of errors on the last request */
1220Sstevel@tonic-gate 				/* (libssl can return multiple errors on one */
1230Sstevel@tonic-gate 				/* operation) */
1240Sstevel@tonic-gate } http_conn_t;
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate /*
1270Sstevel@tonic-gate  * Convenient macros for accessing fields in connection structure.
1280Sstevel@tonic-gate  */
1290Sstevel@tonic-gate #define	CONN_HOSTNAME		c_id->uri.hport.hostname
1300Sstevel@tonic-gate #define	CONN_PORT		c_id->uri.hport.port
1310Sstevel@tonic-gate #define	CONN_ABSPATH		c_id->uri.abspath
1320Sstevel@tonic-gate #define	CONN_HTTPS		c_id->uri.https
1330Sstevel@tonic-gate #define	CONN_PROXY_HOSTNAME	c_id->proxy.hostname
1340Sstevel@tonic-gate #define	CONN_PROXY_PORT		c_id->proxy.port
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate #define	RESET_ERR(c_id)	(c_id)->numerrs = 0, (c_id)->nexterr = 0
1370Sstevel@tonic-gate #define	SET_ERR(c_id, src, err)	if ((c_id)->numerrs < ESTACK_SIZE) \
1380Sstevel@tonic-gate 		(c_id)->errs[(c_id)->numerrs].errsrc = (src), \
1390Sstevel@tonic-gate 		(c_id)->errs[(c_id)->numerrs ++].error = (err)
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate #define	GET_ERR(c_id, e_src, e_code) \
1420Sstevel@tonic-gate 		if ((c_id)->nexterr < (c_id)->numerrs) \
1430Sstevel@tonic-gate 			(e_src) = (c_id)->errs[((c_id)->nexterr)].errsrc, \
1440Sstevel@tonic-gate 			(e_code) = (c_id)->errs[((c_id)->nexterr)++].error; \
1450Sstevel@tonic-gate 		else \
1460Sstevel@tonic-gate 			(e_src) = 0, (e_code) = 0
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate /*
1490Sstevel@tonic-gate  * Macro used to increment message body read counters
1500Sstevel@tonic-gate  */
1510Sstevel@tonic-gate #define	INC_BREAD_CNT(bool, bcnt) \
1520Sstevel@tonic-gate 	if (bool) { \
1530Sstevel@tonic-gate 		bcnt--; \
1540Sstevel@tonic-gate 		c_id->body_read++;\
1550Sstevel@tonic-gate 		c_id->body_read_tot++; \
1560Sstevel@tonic-gate 	}
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate static int	ssl_init = 0;		/* 1 when ssl has been initialized */
1590Sstevel@tonic-gate static char	*ca_verify_file;	/* List of trusted CA's  */
1600Sstevel@tonic-gate static int	verify_depth = 16;	/* Certificate chain depth to verify */
1610Sstevel@tonic-gate static int	p12_format = 0;		/* Default to PEM format */
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate /* prototypes for local functions */
1651279Svh115876 static int	http_req(http_handle_t, const char *, http_req_t, offset_t,
1661279Svh115876     offset_t);
1670Sstevel@tonic-gate static boolean_t http_check_conn(http_conn_t *);
1680Sstevel@tonic-gate static SSL_CTX *initialize_ctx(http_conn_t *);
1690Sstevel@tonic-gate static int	tcp_connect(http_conn_t *, const char *, uint16_t);
1700Sstevel@tonic-gate static int	readline(http_conn_t *, int, char *, int);
1710Sstevel@tonic-gate static int	proxy_connect(http_conn_t *);
1720Sstevel@tonic-gate static int	check_cert_chain(http_conn_t *, char *);
1730Sstevel@tonic-gate static void	print_ciphers(SSL *);
1740Sstevel@tonic-gate static int	read_headerlines(http_conn_t *, boolean_t);
1750Sstevel@tonic-gate static void	free_response(http_conn_t *, int);
1760Sstevel@tonic-gate static int	free_ctx_ssl(http_conn_t *);
1770Sstevel@tonic-gate static int	get_chunk_header(http_conn_t *);
1780Sstevel@tonic-gate static int	init_bread(http_conn_t *);
1790Sstevel@tonic-gate static int	get_msgcnt(http_conn_t *, ssize_t *);
180*13093SRoger.Faulkner@Oracle.COM static int	getaline(http_conn_t *, char *, int, boolean_t);
1810Sstevel@tonic-gate static int	getbytes(http_conn_t *, char *, int);
1820Sstevel@tonic-gate static int	http_srv_send(http_conn_t *, const void *, size_t);
1830Sstevel@tonic-gate static int	http_srv_recv(http_conn_t *, void *, size_t);
1840Sstevel@tonic-gate static void	handle_ssl_error(http_conn_t *, int);
1850Sstevel@tonic-gate static int	count_digits(int);
1860Sstevel@tonic-gate static int	hexdigit(char);
1870Sstevel@tonic-gate static char	*eat_ws(const char *);
1880Sstevel@tonic-gate static boolean_t startswith(const char **strp, const char *starts);
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate /* ---------------------- public functions ----------------------- */
1910Sstevel@tonic-gate 
1920Sstevel@tonic-gate /*
1930Sstevel@tonic-gate  * http_set_p12_format - Set flag indicating that certs & keys will be in
1940Sstevel@tonic-gate  *                    pkcs12 format.
1950Sstevel@tonic-gate  *
1960Sstevel@tonic-gate  * Default is PEM certs.  When this is called, the default can be changed to
1970Sstevel@tonic-gate  * pcs12 format.
1980Sstevel@tonic-gate  */
1990Sstevel@tonic-gate void
http_set_p12_format(int on_off)2000Sstevel@tonic-gate http_set_p12_format(int on_off)
2010Sstevel@tonic-gate {
2020Sstevel@tonic-gate 	p12_format = on_off;
2030Sstevel@tonic-gate }
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate /*
2060Sstevel@tonic-gate  * http_get_version - Get current boot http support version
2070Sstevel@tonic-gate  *
2080Sstevel@tonic-gate  *     pVer = http_get_version();
2090Sstevel@tonic-gate  *
2100Sstevel@tonic-gate  * Arguments:
2110Sstevel@tonic-gate  *	None.
2120Sstevel@tonic-gate  *
2130Sstevel@tonic-gate  * Returns:
2140Sstevel@tonic-gate  *	Pointer to struct with version information.
2150Sstevel@tonic-gate  *
2160Sstevel@tonic-gate  * Returns the version of the http support in the current library.  This
2170Sstevel@tonic-gate  * is a struct with unsigned integsrs for <major>, <minor> and
2180Sstevel@tonic-gate  * <micro> version numbers.  <major> changes when an incompatible change
2190Sstevel@tonic-gate  * is made.  <minor> changes when an upwardly-compatible API change is
2200Sstevel@tonic-gate  * made.  <micro> consists of bug fixes, etc.
2210Sstevel@tonic-gate  */
2220Sstevel@tonic-gate boot_http_ver_t const *
http_get_version(void)2230Sstevel@tonic-gate http_get_version(void)
2240Sstevel@tonic-gate {
2250Sstevel@tonic-gate 	return (&boot_http_ver);
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate /*
2290Sstevel@tonic-gate  * http_set_verbose - Turn verbose on/off
2300Sstevel@tonic-gate  *
2310Sstevel@tonic-gate  *     http_set_verbose(on_off);
2320Sstevel@tonic-gate  *
2330Sstevel@tonic-gate  * Arguments:
2340Sstevel@tonic-gate  *	on_off	- When TRUE, turn verbose mode one.  When FALSE, turn
2350Sstevel@tonic-gate  *		  verbose off.
2360Sstevel@tonic-gate  *
2370Sstevel@tonic-gate  * Returns:
2380Sstevel@tonic-gate  *	None.
2390Sstevel@tonic-gate  *
2400Sstevel@tonic-gate  * When enabled, information is logged to bootlog (or the Solaris equivalent).
2410Sstevel@tonic-gate  */
2420Sstevel@tonic-gate void
http_set_verbose(boolean_t on_off)2430Sstevel@tonic-gate http_set_verbose(boolean_t on_off)
2440Sstevel@tonic-gate {
2450Sstevel@tonic-gate 	verbosemode = on_off;
2460Sstevel@tonic-gate }
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate /*
2490Sstevel@tonic-gate  * http_set_cipher_list - Change the list of ciphers that can be used.
2500Sstevel@tonic-gate  *
2510Sstevel@tonic-gate  *     ret = http_set_cipher_list(handle, list);
2520Sstevel@tonic-gate  *
2530Sstevel@tonic-gate  * Arguments:
2540Sstevel@tonic-gate  *	list	- List of ciphers that can be used.
2550Sstevel@tonic-gate  *
2560Sstevel@tonic-gate  * Returns:
2570Sstevel@tonic-gate  *	0	- Success
2580Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
2590Sstevel@tonic-gate  */
2600Sstevel@tonic-gate int
http_set_cipher_list(const char * list)2610Sstevel@tonic-gate http_set_cipher_list(const char *list)
2620Sstevel@tonic-gate {
2630Sstevel@tonic-gate 	early_err = 0;
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	if (list != NULL) {
2660Sstevel@tonic-gate 		list = strdup(list);
2670Sstevel@tonic-gate 		if (list == NULL) {
2680Sstevel@tonic-gate 			early_err = EHTTP_NOMEM;
2690Sstevel@tonic-gate 			return (-1);
2700Sstevel@tonic-gate 		}
2710Sstevel@tonic-gate 	}
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 	free(cipher_list);
2740Sstevel@tonic-gate 	cipher_list = (char *)list;
2750Sstevel@tonic-gate 	return (0);
2760Sstevel@tonic-gate }
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate /*
2790Sstevel@tonic-gate  * http_srv_init - Set up a structure for a connection.
2800Sstevel@tonic-gate  *
2810Sstevel@tonic-gate  *     handle = http_srv_init(url);
2820Sstevel@tonic-gate  *
2830Sstevel@tonic-gate  * Arguments:
2840Sstevel@tonic-gate  *	url - the structure that contains the URI.
2850Sstevel@tonic-gate  *
2860Sstevel@tonic-gate  * Returns:
2870Sstevel@tonic-gate  *	!= NULL	- A handle for referring to this connection.
2880Sstevel@tonic-gate  *	== NULL - An error occurred.  Get the exact error from
2890Sstevel@tonic-gate  *                http_get_lasterr().
2900Sstevel@tonic-gate  */
2910Sstevel@tonic-gate http_handle_t
http_srv_init(const url_t * url)2920Sstevel@tonic-gate http_srv_init(const url_t *url)
2930Sstevel@tonic-gate {
2940Sstevel@tonic-gate 	http_conn_t	*c_id;
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 	early_err = 0;
2970Sstevel@tonic-gate 	if (url == NULL) {
2980Sstevel@tonic-gate 		early_err = EHTTP_BADARG;
2990Sstevel@tonic-gate 		return (NULL);
3000Sstevel@tonic-gate 	}
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	if ((c_id = malloc(sizeof (*c_id))) == NULL) {
3030Sstevel@tonic-gate 		early_err = EHTTP_NOMEM;
3040Sstevel@tonic-gate 		return (NULL);
3050Sstevel@tonic-gate 	}
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 	bzero(c_id, sizeof (*c_id));
3080Sstevel@tonic-gate 	c_id->uri = *url;
3090Sstevel@tonic-gate 	c_id->proxied = B_FALSE;
3100Sstevel@tonic-gate 	c_id->read_timeout = DEFAULT_TIMEOUT;
3110Sstevel@tonic-gate 	c_id->keepalive = B_TRUE;
3120Sstevel@tonic-gate 	c_id->fd = -1;
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	/* Do this at the end, just in case.... */
3150Sstevel@tonic-gate 	c_id->signature = HTTP_CONN_INFO;
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	return (c_id);
3180Sstevel@tonic-gate }
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate /*
3210Sstevel@tonic-gate  * http_conn_is_https - Determine whether the scheme is http or https.
3220Sstevel@tonic-gate  *
3230Sstevel@tonic-gate  *	B_TRUE	- Connection is an SSL connection.
3240Sstevel@tonic-gate  *	B_FALSE - Connection isn't SSL.
3250Sstevel@tonic-gate  *
3260Sstevel@tonic-gate  *     ret = http_conn_is_https(handle, boolean_t *bool);
3270Sstevel@tonic-gate  *
3280Sstevel@tonic-gate  * Arguments:
3290Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
3300Sstevel@tonic-gate  *	bool	- Ptr to boolean in which to place result
3310Sstevel@tonic-gate  *
3320Sstevel@tonic-gate  * Returns:
3330Sstevel@tonic-gate  *	0	- Success
3340Sstevel@tonic-gate  *	-1	- Some error occurred.
3350Sstevel@tonic-gate  */
3360Sstevel@tonic-gate int
http_conn_is_https(http_handle_t handle,boolean_t * bool)3370Sstevel@tonic-gate http_conn_is_https(http_handle_t handle, boolean_t *bool)
3380Sstevel@tonic-gate {
3390Sstevel@tonic-gate 	http_conn_t	*c_id = handle;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	if (!http_check_conn(c_id))
3420Sstevel@tonic-gate 		return (-1);
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	*bool = CONN_HTTPS;
3450Sstevel@tonic-gate 	return (0);
3460Sstevel@tonic-gate }
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate /*
3490Sstevel@tonic-gate  * http_set_proxy - Establish the proxy name/port.
3500Sstevel@tonic-gate  *
3510Sstevel@tonic-gate  *     ret = http_set_proxy(handle, proxy);
3520Sstevel@tonic-gate  *
3530Sstevel@tonic-gate  * Arguments:
3540Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
3550Sstevel@tonic-gate  *	proxy	- The hostport definition for the proxy. If NULL,
3560Sstevel@tonic-gate  *		  The next connect will not use a proxy.
3570Sstevel@tonic-gate  *
3580Sstevel@tonic-gate  * Returns:
3590Sstevel@tonic-gate  *	0	- Success
3600Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
3610Sstevel@tonic-gate  */
3620Sstevel@tonic-gate int
http_set_proxy(http_handle_t handle,const url_hport_t * proxy)3630Sstevel@tonic-gate http_set_proxy(http_handle_t handle, const url_hport_t *proxy)
3640Sstevel@tonic-gate {
3650Sstevel@tonic-gate 	http_conn_t *c_id = handle;
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 	if (!http_check_conn(c_id))
3680Sstevel@tonic-gate 		return (-1);
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	if (proxy != NULL) {
3710Sstevel@tonic-gate 		c_id->proxy = *proxy;
3720Sstevel@tonic-gate 		c_id->proxied = B_TRUE;
3730Sstevel@tonic-gate 	} else {
3740Sstevel@tonic-gate 		CONN_PROXY_HOSTNAME[0] = '\0';
3750Sstevel@tonic-gate 		c_id->proxied = B_FALSE;
3760Sstevel@tonic-gate 	}
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate 	return (0);
3790Sstevel@tonic-gate }
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate /*
3820Sstevel@tonic-gate  * http_set_keepalive - Set keepalive for this connection.
3830Sstevel@tonic-gate  *
3840Sstevel@tonic-gate  *     http_set_keepalive(handle, on_off);
3850Sstevel@tonic-gate  *
3860Sstevel@tonic-gate  * Arguments:
3870Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
3880Sstevel@tonic-gate  *	on_off	- Boolean turning keepalive on (TRUE) or off (FALSE)
3890Sstevel@tonic-gate  *
3900Sstevel@tonic-gate  * Returns:
3910Sstevel@tonic-gate  *	0	- Success.
3920Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
3930Sstevel@tonic-gate  *
3940Sstevel@tonic-gate  * This setting takes effect next time a connection is opened using this
3950Sstevel@tonic-gate  * handle.
3960Sstevel@tonic-gate  */
3970Sstevel@tonic-gate int
http_set_keepalive(http_handle_t handle,boolean_t on_off)3980Sstevel@tonic-gate http_set_keepalive(http_handle_t handle, boolean_t on_off)
3990Sstevel@tonic-gate {
4000Sstevel@tonic-gate 	http_conn_t *c_id = handle;
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 	if (!http_check_conn(c_id))
4030Sstevel@tonic-gate 		return (-1);
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 	c_id->keepalive = on_off;
4060Sstevel@tonic-gate 	return (0);
4070Sstevel@tonic-gate }
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate /*
4100Sstevel@tonic-gate  * http_set_socket_read_timeout - Set the timeout reads
4110Sstevel@tonic-gate  *
4120Sstevel@tonic-gate  *     http_set_socket_read_timeout(handle, timeout);
4130Sstevel@tonic-gate  *
4140Sstevel@tonic-gate  * Arguments:
4150Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
4160Sstevel@tonic-gate  *	timeout	- Timeout, in seconds.  Zero will default to 10 second
4170Sstevel@tonic-gate  *		  timeouts.
4180Sstevel@tonic-gate  *
4190Sstevel@tonic-gate  * Returns:
4200Sstevel@tonic-gate  *	0	- Success.
4210Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
4220Sstevel@tonic-gate  *
4230Sstevel@tonic-gate  * This setting takes effect beginning with the next read operation on this
4240Sstevel@tonic-gate  * connection.
4250Sstevel@tonic-gate  */
4260Sstevel@tonic-gate int
http_set_socket_read_timeout(http_handle_t handle,uint_t timout)4270Sstevel@tonic-gate http_set_socket_read_timeout(http_handle_t handle, uint_t timout)
4280Sstevel@tonic-gate {
4290Sstevel@tonic-gate 	http_conn_t *c_id = handle;
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	if (!http_check_conn(c_id))
4320Sstevel@tonic-gate 		return (-1);
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	c_id->read_timeout = (timout) ? timout : DEFAULT_TIMEOUT;
4350Sstevel@tonic-gate 	return (0);
4360Sstevel@tonic-gate }
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate /*
4390Sstevel@tonic-gate  * http_set_basic_auth - Set the basic authorization user ID and password
4400Sstevel@tonic-gate  *
4410Sstevel@tonic-gate  *     ret = http_set_basic_auth(handle, userid, password);
4420Sstevel@tonic-gate  *
4430Sstevel@tonic-gate  * Arguments:
4440Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
4450Sstevel@tonic-gate  *	userid	- ID to pass as part of http/https request
4460Sstevel@tonic-gate  *	password- Password which goes with the user ID
4470Sstevel@tonic-gate  *
4480Sstevel@tonic-gate  * Returns:
4490Sstevel@tonic-gate  *	0	- Success
4500Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
4510Sstevel@tonic-gate  *
4520Sstevel@tonic-gate  * This must be set before a https connection is made.
4530Sstevel@tonic-gate  */
4540Sstevel@tonic-gate int
http_set_basic_auth(http_handle_t handle,const char * userid,const char * password)4550Sstevel@tonic-gate http_set_basic_auth(http_handle_t handle, const char *userid,
4560Sstevel@tonic-gate     const char *password)
4570Sstevel@tonic-gate {
4580Sstevel@tonic-gate 	http_conn_t *c_id = handle;
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 	if (!http_check_conn(c_id))
4610Sstevel@tonic-gate 		return (-1);
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 	if (password == NULL || userid == NULL || userid[0] == '\0') {
4640Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
4650Sstevel@tonic-gate 		return (-1);
4660Sstevel@tonic-gate 	}
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	userid = strdup(userid);
4690Sstevel@tonic-gate 	password = strdup(password);
4700Sstevel@tonic-gate 	if (userid == NULL || password == NULL) {
4710Sstevel@tonic-gate 		free((void *)userid);
4720Sstevel@tonic-gate 		free((void *)password);
4730Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
4740Sstevel@tonic-gate 		return (-1);
4750Sstevel@tonic-gate 	}
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 	free(c_id->basic_auth_userid);
4780Sstevel@tonic-gate 	c_id->basic_auth_userid = (char *)userid;
4790Sstevel@tonic-gate 	free(c_id->basic_auth_password);
4800Sstevel@tonic-gate 	c_id->basic_auth_password = (char *)password;
4810Sstevel@tonic-gate 	return (0);
4820Sstevel@tonic-gate }
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate /*
4850Sstevel@tonic-gate  * http_set_random_file - See the pseudo random number generator with file data
4860Sstevel@tonic-gate  *
4870Sstevel@tonic-gate  *     ret = http_set_random_file(handle, filename);
4880Sstevel@tonic-gate  *
4890Sstevel@tonic-gate  * Arguments:
4900Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
4910Sstevel@tonic-gate  *	filename
4920Sstevel@tonic-gate  *		- filename (including path) with random number seed.
4930Sstevel@tonic-gate  *
4940Sstevel@tonic-gate  * Returns:
4950Sstevel@tonic-gate  *	0	- Success
4960Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
4970Sstevel@tonic-gate  *
4980Sstevel@tonic-gate  * This must be set before a https connection is made.
4990Sstevel@tonic-gate  */
5000Sstevel@tonic-gate int
http_set_random_file(http_handle_t handle,const char * fname)5010Sstevel@tonic-gate http_set_random_file(http_handle_t handle, const char *fname)
5020Sstevel@tonic-gate {
5030Sstevel@tonic-gate 	http_conn_t *c_id = handle;
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	if (!http_check_conn(c_id))
5060Sstevel@tonic-gate 		return (-1);
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	if (fname != NULL) {
5090Sstevel@tonic-gate 		fname = strdup(fname);
5100Sstevel@tonic-gate 		if (fname == NULL) {
5110Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
5120Sstevel@tonic-gate 			return (-1);
5130Sstevel@tonic-gate 		}
5140Sstevel@tonic-gate 	}
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	free(c_id->random_file);
5170Sstevel@tonic-gate 	c_id->random_file = (char *)fname;
5180Sstevel@tonic-gate 	return (0);
5190Sstevel@tonic-gate }
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate /*
5220Sstevel@tonic-gate  * http_set_certificate_authority_file - Set the CA file.
5230Sstevel@tonic-gate  *
5240Sstevel@tonic-gate  *     ret = http_set_certificate_authority_file(filename);
5250Sstevel@tonic-gate  *
5260Sstevel@tonic-gate  * Arguments:
5270Sstevel@tonic-gate  *	filename- File with the certificate authority certs
5280Sstevel@tonic-gate  *
5290Sstevel@tonic-gate  * Returns:
5300Sstevel@tonic-gate  *	0	- Success
5310Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
5320Sstevel@tonic-gate  *
5330Sstevel@tonic-gate  * This must be set before https connections to the servers is done.
5340Sstevel@tonic-gate  */
5350Sstevel@tonic-gate int
http_set_certificate_authority_file(const char * fname)5360Sstevel@tonic-gate http_set_certificate_authority_file(const char *fname)
5370Sstevel@tonic-gate {
5380Sstevel@tonic-gate 	early_err = 0;
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	if (fname != NULL) {
5410Sstevel@tonic-gate 		fname = strdup(fname);
5420Sstevel@tonic-gate 		if (fname == NULL) {
5430Sstevel@tonic-gate 			early_err = EHTTP_NOMEM;
5440Sstevel@tonic-gate 			return (-1);
5450Sstevel@tonic-gate 		}
5460Sstevel@tonic-gate 	}
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 	free(ca_verify_file);
5490Sstevel@tonic-gate 	ca_verify_file = (char *)fname;
5500Sstevel@tonic-gate 	return (0);
5510Sstevel@tonic-gate }
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate /*
5540Sstevel@tonic-gate  * http_set_client_certificate_file - Set the file containing the PKCS#12
5550Sstevel@tonic-gate  *		client certificate and optionally its certificate chain.
5560Sstevel@tonic-gate  *
5570Sstevel@tonic-gate  *     ret = http_set_client_certificate_file(handle, filename);
5580Sstevel@tonic-gate  *
5590Sstevel@tonic-gate  * Arguments:
5600Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
5610Sstevel@tonic-gate  *	filename- File (including path) containing certificate, etc.
5620Sstevel@tonic-gate  *
5630Sstevel@tonic-gate  * Returns:
5640Sstevel@tonic-gate  *	0	- Success
5650Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
5660Sstevel@tonic-gate  *
5670Sstevel@tonic-gate  * This must be set before the handle is used to make a https connection
5680Sstevel@tonic-gate  * which will require a client certificate.
5690Sstevel@tonic-gate  */
5700Sstevel@tonic-gate int
http_set_client_certificate_file(http_handle_t handle,const char * fname)5710Sstevel@tonic-gate http_set_client_certificate_file(http_handle_t handle, const char *fname)
5720Sstevel@tonic-gate {
5730Sstevel@tonic-gate 	http_conn_t *c_id = handle;
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	if (!http_check_conn(c_id))
5760Sstevel@tonic-gate 		return (-1);
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	if (fname != NULL) {
5790Sstevel@tonic-gate 		fname = strdup(fname);
5800Sstevel@tonic-gate 		if (fname == NULL) {
5810Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
5820Sstevel@tonic-gate 			return (-1);
5830Sstevel@tonic-gate 		}
5840Sstevel@tonic-gate 	}
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 	free(c_id->client_cert_file);
5870Sstevel@tonic-gate 	c_id->client_cert_file = (char *)fname;
5880Sstevel@tonic-gate 	return (0);
5890Sstevel@tonic-gate }
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate /*
5920Sstevel@tonic-gate  * http_set_password - Set the password for the private key or pkcs12 file.
5930Sstevel@tonic-gate  *
5940Sstevel@tonic-gate  *     ret = http_set_password(handle, password);
5950Sstevel@tonic-gate  *
5960Sstevel@tonic-gate  * Arguments:
5970Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
5980Sstevel@tonic-gate  *	password- Password for the client's private key file or pkcs12 file.
5990Sstevel@tonic-gate  *
6000Sstevel@tonic-gate  * Returns:
6010Sstevel@tonic-gate  *	0	- Success
6020Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
6030Sstevel@tonic-gate  *
6040Sstevel@tonic-gate  * This must be set before the handle is used to make a https connection.
6050Sstevel@tonic-gate  */
6060Sstevel@tonic-gate int
http_set_password(http_handle_t handle,const char * password)6070Sstevel@tonic-gate http_set_password(http_handle_t handle, const char *password)
6080Sstevel@tonic-gate {
6090Sstevel@tonic-gate 	http_conn_t *c_id = handle;
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 	if (!http_check_conn(c_id))
6120Sstevel@tonic-gate 		return (-1);
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	if (password != NULL) {
6150Sstevel@tonic-gate 		password = strdup(password);
6160Sstevel@tonic-gate 		if (password == NULL) {
6170Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
6180Sstevel@tonic-gate 			return (-1);
6190Sstevel@tonic-gate 		}
6200Sstevel@tonic-gate 	}
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	free(c_id->file_password);
6230Sstevel@tonic-gate 	c_id->file_password = (char *)password;
6240Sstevel@tonic-gate 	return (0);
6250Sstevel@tonic-gate }
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate /*
6280Sstevel@tonic-gate  * http_set_key_file_password - Set the password for the private key
6290Sstevel@tonic-gate  *		file.
6300Sstevel@tonic-gate  *
6310Sstevel@tonic-gate  *     ret = http_set_key_file_password(handle, password);
6320Sstevel@tonic-gate  *
6330Sstevel@tonic-gate  * Arguments:
6340Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
6350Sstevel@tonic-gate  *	password- Password for the client's private key file.
6360Sstevel@tonic-gate  *
6370Sstevel@tonic-gate  * Returns:
6380Sstevel@tonic-gate  *	0	- Success
6390Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
6400Sstevel@tonic-gate  *
6410Sstevel@tonic-gate  * This must be set before the handle is used to make a https connection.
6420Sstevel@tonic-gate  */
6430Sstevel@tonic-gate int
http_set_key_file_password(http_handle_t handle,const char * password)6440Sstevel@tonic-gate http_set_key_file_password(http_handle_t handle, const char *password)
6450Sstevel@tonic-gate {
6460Sstevel@tonic-gate 	return (http_set_password(handle, password));
6470Sstevel@tonic-gate }
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate /*
6500Sstevel@tonic-gate  * http_set_private_key_file - Set the file containing the PKCS#12
6510Sstevel@tonic-gate  *		private key for this client.
6520Sstevel@tonic-gate  *
6530Sstevel@tonic-gate  *     ret = http_set_private_key_file(handle, filename);
6540Sstevel@tonic-gate  *
6550Sstevel@tonic-gate  * Arguments:
6560Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
6570Sstevel@tonic-gate  *	filename- File (including path) containing the private key.
6580Sstevel@tonic-gate  *
6590Sstevel@tonic-gate  * Returns:
6600Sstevel@tonic-gate  *	0	- Success
6610Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
6620Sstevel@tonic-gate  *
6630Sstevel@tonic-gate  * This must be set before the handle is used to make a https connection.
6640Sstevel@tonic-gate  */
6650Sstevel@tonic-gate int
http_set_private_key_file(http_handle_t handle,const char * fname)6660Sstevel@tonic-gate http_set_private_key_file(http_handle_t handle, const char *fname)
6670Sstevel@tonic-gate {
6680Sstevel@tonic-gate 	http_conn_t *c_id = handle;
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	if (!http_check_conn(c_id))
6710Sstevel@tonic-gate 		return (-1);
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 	if (fname != NULL) {
6740Sstevel@tonic-gate 		fname = strdup(fname);
6750Sstevel@tonic-gate 		if (fname == NULL) {
6760Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
6770Sstevel@tonic-gate 			return (-1);
6780Sstevel@tonic-gate 		}
6790Sstevel@tonic-gate 	}
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	free(c_id->private_key_file);
6820Sstevel@tonic-gate 	c_id->private_key_file = (char *)fname;
6830Sstevel@tonic-gate 	return (0);
6840Sstevel@tonic-gate }
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate /*
6870Sstevel@tonic-gate  * http_srv_connect - Establish a connection to the server
6880Sstevel@tonic-gate  *
6890Sstevel@tonic-gate  *     ret = http_srv_connect(handle);
6900Sstevel@tonic-gate  *
6910Sstevel@tonic-gate  * Arguments:
6920Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
6930Sstevel@tonic-gate  *
6940Sstevel@tonic-gate  * Returns:
6950Sstevel@tonic-gate  *	0	- Success
6960Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr() for specifics.
6970Sstevel@tonic-gate  */
6980Sstevel@tonic-gate int
http_srv_connect(http_handle_t handle)6990Sstevel@tonic-gate http_srv_connect(http_handle_t handle)
7000Sstevel@tonic-gate {
7010Sstevel@tonic-gate 	http_conn_t	*c_id = handle;
7020Sstevel@tonic-gate 	SSL_CTX		*ctx = NULL;
7030Sstevel@tonic-gate 	int		retval;
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 	ERR_clear_error();
7060Sstevel@tonic-gate 	if (!http_check_conn(c_id))
7070Sstevel@tonic-gate 		return (-1);
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	if (CONN_HTTPS) {
7100Sstevel@tonic-gate 		/* Build our SSL context (this function sets any errors) */
7110Sstevel@tonic-gate 		ctx = initialize_ctx(c_id);
7120Sstevel@tonic-gate 		if (ctx == NULL) {
7130Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
7140Sstevel@tonic-gate 			    "http_srv_connect: initialize_ctx returned NULL");
7150Sstevel@tonic-gate 			return (-1);
7160Sstevel@tonic-gate 		}
7170Sstevel@tonic-gate 	}
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 	/* Connect the TCP socket */
7200Sstevel@tonic-gate 	if (c_id->proxied) {
7210Sstevel@tonic-gate 		c_id->fd = proxy_connect(c_id);
7220Sstevel@tonic-gate 	} else {
7230Sstevel@tonic-gate 		c_id->fd = tcp_connect(c_id, CONN_HOSTNAME, CONN_PORT);
7240Sstevel@tonic-gate 	}
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 	if (c_id->fd < 0) {
7270Sstevel@tonic-gate 		if (ctx != NULL)
7280Sstevel@tonic-gate 			SSL_CTX_free(ctx);
7290Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
7300Sstevel@tonic-gate 			    "http_srv_connect: %s returned %d",
7310Sstevel@tonic-gate 			    (c_id->proxied) ? "proxy_connect" : "tcp_connect",
7320Sstevel@tonic-gate 			    c_id->fd);
7330Sstevel@tonic-gate 		return (-1);
7340Sstevel@tonic-gate 	}
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate 	if (CONN_HTTPS) {
7370Sstevel@tonic-gate 		/* Connect the SSL socket */
7380Sstevel@tonic-gate 		if ((c_id->ssl = SSL_new(ctx)) == NULL) {
7390Sstevel@tonic-gate 			ulong_t err;
7400Sstevel@tonic-gate 			while ((err = ERR_get_error()) != 0)
7410Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBSSL, err);
7420Sstevel@tonic-gate 				libbootlog(BOOTLOG_CRIT,
7430Sstevel@tonic-gate 				    "http_srv_connect: SSL_new returned "
7440Sstevel@tonic-gate 				    "NULL");
7450Sstevel@tonic-gate 			(void) free_ctx_ssl(c_id);
7460Sstevel@tonic-gate 			return (-1);
7470Sstevel@tonic-gate 		}
7480Sstevel@tonic-gate 		if (verbosemode)
7490Sstevel@tonic-gate 			print_ciphers(c_id->ssl);
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 		/* Ensure automatic negotiations will do things right */
7520Sstevel@tonic-gate 		SSL_set_connect_state(c_id->ssl);
7530Sstevel@tonic-gate 
7540Sstevel@tonic-gate 		if (SSL_set_fd(c_id->ssl, c_id->fd) == 0) {
7550Sstevel@tonic-gate 			ulong_t err;
7560Sstevel@tonic-gate 			while ((err = ERR_get_error()) != 0)
7570Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBSSL, err);
7580Sstevel@tonic-gate 				libbootlog(BOOTLOG_CRIT,
7590Sstevel@tonic-gate 				    "http_srv_connect: SSL_set_fd returned 0");
7600Sstevel@tonic-gate 			(void) free_ctx_ssl(c_id);
7610Sstevel@tonic-gate 			return (-1);
7620Sstevel@tonic-gate 		}
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 		if ((retval = SSL_connect(c_id->ssl)) <= 0) {
7650Sstevel@tonic-gate 			handle_ssl_error(c_id, retval);
7660Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
7670Sstevel@tonic-gate 			    "http_srv_connect: SSL_connect");
7680Sstevel@tonic-gate 			(void) free_ctx_ssl(c_id);
7690Sstevel@tonic-gate 			return (-1);
7700Sstevel@tonic-gate 		}
7710Sstevel@tonic-gate 
7720Sstevel@tonic-gate 		if (check_cert_chain(c_id, CONN_HOSTNAME) != 0) {
7730Sstevel@tonic-gate 			(void) free_ctx_ssl(c_id);
7740Sstevel@tonic-gate 			return (-1);
7750Sstevel@tonic-gate 		}
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate 		if (verbosemode)
7780Sstevel@tonic-gate 			print_ciphers(c_id->ssl);
7790Sstevel@tonic-gate 	}
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	return (0);
7820Sstevel@tonic-gate }
7830Sstevel@tonic-gate 
7840Sstevel@tonic-gate /*
7850Sstevel@tonic-gate  * http_head_request - Issue http HEAD request
7860Sstevel@tonic-gate  *
7870Sstevel@tonic-gate  *     ret = http_head_request(handle, abs_path);
7880Sstevel@tonic-gate  *
7890Sstevel@tonic-gate  * Arguments:
7900Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
7910Sstevel@tonic-gate  *	abs_path- File name portion of the URI, beginning with a /.  Query,
7920Sstevel@tonic-gate  *		  segment, etc are allowed.
7930Sstevel@tonic-gate  *
7940Sstevel@tonic-gate  * Returns:
7950Sstevel@tonic-gate  *	0	- Success
7960Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
7970Sstevel@tonic-gate  */
7980Sstevel@tonic-gate int
http_head_request(http_handle_t handle,const char * abs_path)7990Sstevel@tonic-gate http_head_request(http_handle_t handle, const char *abs_path)
8000Sstevel@tonic-gate {
8010Sstevel@tonic-gate 	return (http_req(handle, abs_path, HTTP_REQ_TYPE_HEAD, 0, 0));
8020Sstevel@tonic-gate }
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate /*
8050Sstevel@tonic-gate  * http_get_request - Issue http GET request without a range.
8060Sstevel@tonic-gate  *
8070Sstevel@tonic-gate  *     ret = http_get_request(handle, abs_path);
8080Sstevel@tonic-gate  *
8090Sstevel@tonic-gate  * Arguments:
8100Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
8110Sstevel@tonic-gate  *	abs_path- File name portion of the URI, beginning with a /.  Query,
8120Sstevel@tonic-gate  *		  segment, etc are allowed.
8130Sstevel@tonic-gate  *
8140Sstevel@tonic-gate  * Returns:
8150Sstevel@tonic-gate  *	0	- Success
8160Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
8170Sstevel@tonic-gate  */
8180Sstevel@tonic-gate int
http_get_request(http_handle_t handle,const char * abs_path)8190Sstevel@tonic-gate http_get_request(http_handle_t handle, const char *abs_path)
8200Sstevel@tonic-gate {
8210Sstevel@tonic-gate 	return (http_req(handle, abs_path, HTTP_REQ_TYPE_GET, -1, 0));
8220Sstevel@tonic-gate }
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate /*
8250Sstevel@tonic-gate  * http_get_range_request - Issue http GET request using a range.
8260Sstevel@tonic-gate  *
8270Sstevel@tonic-gate  *     ret = http_get_range_request(handle, abs_path, curpos, len);
8280Sstevel@tonic-gate  *
8290Sstevel@tonic-gate  * Arguments:
8300Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
8310Sstevel@tonic-gate  *	abs_path- File name portion of the URI, beginning with a /.  Query,
8320Sstevel@tonic-gate  *		  segment, etc are allowed.
8330Sstevel@tonic-gate  *	curpos  - >=0 - Beginning of range
8340Sstevel@tonic-gate  *	len	- = 0 - Range ends at the end of the file
8350Sstevel@tonic-gate  *		  > 0 - Length of range.
8360Sstevel@tonic-gate  *
8370Sstevel@tonic-gate  * Returns:
8380Sstevel@tonic-gate  *	0	- Success
8390Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
8400Sstevel@tonic-gate  */
8410Sstevel@tonic-gate int
http_get_range_request(http_handle_t handle,const char * abs_path,offset_t curpos,offset_t len)8420Sstevel@tonic-gate http_get_range_request(http_handle_t handle, const char *abs_path,
8431279Svh115876     offset_t curpos, offset_t len)
8440Sstevel@tonic-gate {
8450Sstevel@tonic-gate 	http_conn_t *c_id = handle;
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	if (!http_check_conn(c_id))
8480Sstevel@tonic-gate 		return (-1);
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate 	if (curpos < 0) {
8510Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
8520Sstevel@tonic-gate 		return (-1);
8530Sstevel@tonic-gate 	}
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 	return (http_req(handle, abs_path, HTTP_REQ_TYPE_GET, curpos, len));
8560Sstevel@tonic-gate }
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate /*
8590Sstevel@tonic-gate  * http_free_respinfo - Free a respinfo structure
8600Sstevel@tonic-gate  *
8610Sstevel@tonic-gate  *     ret = http_free_respinfo(resp);
8620Sstevel@tonic-gate  *
8630Sstevel@tonic-gate  * Arguments:
8640Sstevel@tonic-gate  *	resp	- respinfo structure presumably allocated by
8650Sstevel@tonic-gate  *		  http_process_headers() or http_process_part_headers()
8660Sstevel@tonic-gate  *
8670Sstevel@tonic-gate  * Note that if resp is NULL, then this results in a NOOP.
8680Sstevel@tonic-gate  *
8690Sstevel@tonic-gate  */
8700Sstevel@tonic-gate void
http_free_respinfo(http_respinfo_t * resp)8710Sstevel@tonic-gate http_free_respinfo(http_respinfo_t *resp)
8720Sstevel@tonic-gate {
8730Sstevel@tonic-gate 	if (resp == NULL) {
8740Sstevel@tonic-gate 		return;
8750Sstevel@tonic-gate 	}
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 	if (resp->statusmsg != NULL) {
8780Sstevel@tonic-gate 		free(resp->statusmsg);
8790Sstevel@tonic-gate 	}
8800Sstevel@tonic-gate 	free(resp);
8810Sstevel@tonic-gate }
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate /*
8840Sstevel@tonic-gate  * http_process_headers - Read in the header lines from the response
8850Sstevel@tonic-gate  *
8860Sstevel@tonic-gate  *     ret = http_process_headers(handle, resp);
8870Sstevel@tonic-gate  *
8880Sstevel@tonic-gate  * Arguments:
8890Sstevel@tonic-gate  *	handle	- Handle associated with the connection where the request
8900Sstevel@tonic-gate  *		  was made.
8910Sstevel@tonic-gate  *	resp	- Summary information about the response.
8920Sstevel@tonic-gate  *
8930Sstevel@tonic-gate  * Returns:
8940Sstevel@tonic-gate  *	0	- Success
8950Sstevel@tonic-gate  *	< 0	- An error occurred.  Specifics of the error can
8960Sstevel@tonic-gate  *		  be gotten using http_get_lasterr().
8970Sstevel@tonic-gate  *
8980Sstevel@tonic-gate  * Process the HTTP headers in the response. Check for a valid response
8990Sstevel@tonic-gate  * status line.  Allocate and return response information via the 'resp'
9000Sstevel@tonic-gate  * argument. Header lines are stored locally, are are returned using calls
9010Sstevel@tonic-gate  * to http_get_response_header() and http_get_header_value().
9020Sstevel@tonic-gate  *
9030Sstevel@tonic-gate  * Note that the errors will be set in the http_conn_t struct before the
9040Sstevel@tonic-gate  * function which detected the error returns.
9050Sstevel@tonic-gate  *
9060Sstevel@tonic-gate  * Note that if resp is non-NULL, then upon a successful return, information
9070Sstevel@tonic-gate  * about the status line, the code in the status line and the number of
9080Sstevel@tonic-gate  * header lines are returned in the http_respinfo_t structure. The caller is
9090Sstevel@tonic-gate  * responsible for freeing the resources allocated to this structure via
9100Sstevel@tonic-gate  * http_free_respinfo().
9110Sstevel@tonic-gate  *
9120Sstevel@tonic-gate  * Note that the counters used to read message bodies are initialized here.
9130Sstevel@tonic-gate  *
9140Sstevel@tonic-gate  * Calling this function replaces the header information which is
9150Sstevel@tonic-gate  * queried using http_get_response_header() and http_get_header_value().
9160Sstevel@tonic-gate  * Once this function is called, headers read by the previous call
9170Sstevel@tonic-gate  * to http_process_headers() or http_process_part_headers() is lost.
9180Sstevel@tonic-gate  */
9190Sstevel@tonic-gate int
http_process_headers(http_handle_t handle,http_respinfo_t ** resp)9200Sstevel@tonic-gate http_process_headers(http_handle_t handle, http_respinfo_t **resp)
9210Sstevel@tonic-gate {
9220Sstevel@tonic-gate 	http_conn_t *c_id = handle;
9230Sstevel@tonic-gate 	http_respinfo_t *lresp;
9240Sstevel@tonic-gate 	char	line[MAXHOSTNAMELEN];
9250Sstevel@tonic-gate 	char	*ptr;
9260Sstevel@tonic-gate 	int	i;
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 	ERR_clear_error();
9290Sstevel@tonic-gate 	if (!http_check_conn(c_id))
9300Sstevel@tonic-gate 		return (-1);
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate 	if (resp != NULL) {
9330Sstevel@tonic-gate 		if ((lresp = malloc(sizeof (http_respinfo_t))) == NULL) {
9340Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
9350Sstevel@tonic-gate 			return (-1);
9360Sstevel@tonic-gate 		}
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate 		bzero(lresp, sizeof (http_respinfo_t));
9390Sstevel@tonic-gate 	}
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 	/*
9420Sstevel@tonic-gate 	 * check the response status line, expecting
9430Sstevel@tonic-gate 	 * HTTP/1.1 200 OK
9440Sstevel@tonic-gate 	 */
945*13093SRoger.Faulkner@Oracle.COM 	i = getaline(c_id, line, sizeof (line), B_FALSE);
9460Sstevel@tonic-gate 	if (i == 0) {
9470Sstevel@tonic-gate 		if (resp != NULL) {
9480Sstevel@tonic-gate 			*resp = lresp;
9490Sstevel@tonic-gate 		}
9500Sstevel@tonic-gate 		return (0);
9510Sstevel@tonic-gate 	}
9520Sstevel@tonic-gate 
9530Sstevel@tonic-gate 	if (i < 0) {
9540Sstevel@tonic-gate 		/*
9550Sstevel@tonic-gate 		 * Cause of I/O error was already put into
9560Sstevel@tonic-gate 		 * error stack.  This is an additional error.
9570Sstevel@tonic-gate 		 */
9580Sstevel@tonic-gate 		http_free_respinfo(lresp);
9590Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NODATA);
9600Sstevel@tonic-gate 		return (-1);
9610Sstevel@tonic-gate 	}
9620Sstevel@tonic-gate 
9630Sstevel@tonic-gate 	free_response(c_id, B_TRUE);
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	if (verbosemode)
9660Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE, "http_process_headers: %s", line);
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 	ptr = line;
9690Sstevel@tonic-gate 	if (strncmp(ptr, "HTTP/1.1", 8) != 0) {
9700Sstevel@tonic-gate 		http_free_respinfo(lresp);
9710Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOT_1_1);
9720Sstevel@tonic-gate 		return (-1);
9730Sstevel@tonic-gate 	}
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 	/* skip to the code */
9760Sstevel@tonic-gate 	ptr += 8;
9770Sstevel@tonic-gate 	while (isspace(*ptr))
9780Sstevel@tonic-gate 		ptr++;
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 	/* make sure it's three digits */
9810Sstevel@tonic-gate 	i = 0;
9820Sstevel@tonic-gate 	while (isdigit(ptr[i]))
9830Sstevel@tonic-gate 		i++;
9840Sstevel@tonic-gate 	if (i != 3) {
9850Sstevel@tonic-gate 		http_free_respinfo(lresp);
9860Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADHDR);
9870Sstevel@tonic-gate 		return (-1);
9880Sstevel@tonic-gate 	}
9890Sstevel@tonic-gate 	c_id->resp.code = strtol(ptr, NULL, 10);
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	/* skip to the message */
9920Sstevel@tonic-gate 	ptr += 3;
9930Sstevel@tonic-gate 	while (isspace(*ptr))
9940Sstevel@tonic-gate 		ptr++;
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 	/* save the message */
9970Sstevel@tonic-gate 	c_id->resp.statusmsg = malloc(strlen(ptr) + 1);
9980Sstevel@tonic-gate 	if (c_id->resp.statusmsg == NULL) {
9990Sstevel@tonic-gate 		http_free_respinfo(lresp);
10000Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
10010Sstevel@tonic-gate 		return (-1);
10020Sstevel@tonic-gate 	}
10030Sstevel@tonic-gate 	(void) strcpy(c_id->resp.statusmsg, ptr);
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 	if ((i = read_headerlines(c_id, B_FALSE)) < 0) {
10060Sstevel@tonic-gate 		/*
10070Sstevel@tonic-gate 		 * Error stack was already set at a lower level.
10080Sstevel@tonic-gate 		 * 'statusmsg' will be cleaned up next time
10090Sstevel@tonic-gate 		 * headers are read.
10100Sstevel@tonic-gate 		 */
10110Sstevel@tonic-gate 		http_free_respinfo(lresp);
10120Sstevel@tonic-gate 		return (-1);
10130Sstevel@tonic-gate 	}
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate 	/*
10160Sstevel@tonic-gate 	 * See if there is a 'content-type: multipart/mixed' line in the
10170Sstevel@tonic-gate 	 * headers.  If so, get the boundary string.
10180Sstevel@tonic-gate 	 */
10190Sstevel@tonic-gate 	ptr = http_get_header_value(handle, "Content-Type");
10200Sstevel@tonic-gate 	if (ptr != NULL) {
10210Sstevel@tonic-gate 		char *ptr2;
10220Sstevel@tonic-gate 
10230Sstevel@tonic-gate 		ptr2 = ptr;
10240Sstevel@tonic-gate 		while (isspace(*ptr2))
10250Sstevel@tonic-gate 			ptr2 ++;
10260Sstevel@tonic-gate 		if (startswith((const char **)&ptr2, "Multipart/Mixed;")) {
10270Sstevel@tonic-gate 			while (isspace(*ptr2))
10280Sstevel@tonic-gate 				ptr2 ++;
10290Sstevel@tonic-gate 			if (startswith((const char **)&ptr2, "Boundary=")) {
10300Sstevel@tonic-gate 				if (ptr2[0] == '"') {
10310Sstevel@tonic-gate 					ptr2 ++;
10320Sstevel@tonic-gate 					if (ptr2[strlen(ptr2) - 1] == '"')
10330Sstevel@tonic-gate 						ptr2[strlen(ptr2) - 1] = '\0';
10340Sstevel@tonic-gate 				}
10350Sstevel@tonic-gate 				c_id->boundary = strdup(ptr2);
10360Sstevel@tonic-gate 				if (c_id->boundary == NULL) {
10370Sstevel@tonic-gate 					free(ptr);
10380Sstevel@tonic-gate 					http_free_respinfo(lresp);
10390Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBHTTP,
10400Sstevel@tonic-gate 					    EHTTP_NOMEM);
10410Sstevel@tonic-gate 					return (-1);
10420Sstevel@tonic-gate 				}
10430Sstevel@tonic-gate 				c_id->boundary_len = strlen(c_id->boundary);
10440Sstevel@tonic-gate 				c_id->is_multipart = B_TRUE;
10450Sstevel@tonic-gate 				c_id->is_firstpart = B_TRUE;
10460Sstevel@tonic-gate 			}
10470Sstevel@tonic-gate 		}
10480Sstevel@tonic-gate 		free(ptr);
10490Sstevel@tonic-gate 	}
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate 	/*
10520Sstevel@tonic-gate 	 * Initialize the counters used to process message bodies.
10530Sstevel@tonic-gate 	 */
10540Sstevel@tonic-gate 	if (init_bread(c_id) != 0) {
10550Sstevel@tonic-gate 		/*
10560Sstevel@tonic-gate 		 * Error stack was already set at a lower level.
10570Sstevel@tonic-gate 		 */
10580Sstevel@tonic-gate 		http_free_respinfo(lresp);
10590Sstevel@tonic-gate 		return (-1);
10600Sstevel@tonic-gate 	}
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	/* Copy fields to the caller's structure */
10630Sstevel@tonic-gate 	if (resp != NULL) {
10640Sstevel@tonic-gate 		lresp->code = c_id->resp.code;
10650Sstevel@tonic-gate 		lresp->nresphdrs = c_id->resp.nresphdrs;
10660Sstevel@tonic-gate 		lresp->statusmsg = strdup(c_id->resp.statusmsg);
10670Sstevel@tonic-gate 		if (lresp->statusmsg == NULL) {
10680Sstevel@tonic-gate 			http_free_respinfo(lresp);
10690Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
10700Sstevel@tonic-gate 			return (-1);
10710Sstevel@tonic-gate 		}
10720Sstevel@tonic-gate 		*resp = lresp;
10730Sstevel@tonic-gate 	}
10740Sstevel@tonic-gate 
10750Sstevel@tonic-gate 	return (0);
10760Sstevel@tonic-gate }
10770Sstevel@tonic-gate 
10780Sstevel@tonic-gate /*
10790Sstevel@tonic-gate  * http_process_part_headers - Read in part boundary and header lines for the
10800Sstevel@tonic-gate  *                             next part of a multipart message.
10810Sstevel@tonic-gate  *
10820Sstevel@tonic-gate  *        ret = http_process_part_headers(handle, resp);
10830Sstevel@tonic-gate  *
10840Sstevel@tonic-gate  * Arguments:
10850Sstevel@tonic-gate  *   handle	- Handle associated with the connection where the request
10860Sstevel@tonic-gate  *		  was made.
10870Sstevel@tonic-gate  *   resp	- Return address for summary information about the
10880Sstevel@tonic-gate  *		  header block.
10890Sstevel@tonic-gate  *
10900Sstevel@tonic-gate  * Returns:
10910Sstevel@tonic-gate  *   = 1	- The end part was found.
10920Sstevel@tonic-gate  *   = 0	- Success, with header info returned in 'resp'
10930Sstevel@tonic-gate  *   = -1	- An error occurred.  Specifics of the error can
10940Sstevel@tonic-gate  *		  be gotten using http_get_lasterr().
10950Sstevel@tonic-gate  *
10960Sstevel@tonic-gate  * This function reads any \r\n sequences (empty lines) and expects to get
10970Sstevel@tonic-gate  * a boundary line as the next non-empty line.  It then reads header lines
10980Sstevel@tonic-gate  * (content-length, etc) until it gets another empty lines, which ends the
10990Sstevel@tonic-gate  * header section.
11000Sstevel@tonic-gate  *
11010Sstevel@tonic-gate  * Note that if resp is non-NULL, then upon a successful return, information
11020Sstevel@tonic-gate  * about the the number of header lines is returned in the http_respinfo_t
11030Sstevel@tonic-gate  * structure. The caller is responsible for freeing the resources allocated
11040Sstevel@tonic-gate  * to this structure via http_free_respinfo().
11050Sstevel@tonic-gate  *
11060Sstevel@tonic-gate  * Headers values can be returned using http_get_response_header() and
11070Sstevel@tonic-gate  * http_get_header_value().
11080Sstevel@tonic-gate  *
11090Sstevel@tonic-gate  * Calling this function replaces the header information which is
11100Sstevel@tonic-gate  * queried using http_get_response_header() and http_get_header_value().
11110Sstevel@tonic-gate  * Once this function is called, information returned by the previous call
11120Sstevel@tonic-gate  * to http_process_headers() or http_process_part_headers() is gone.
11130Sstevel@tonic-gate  */
11140Sstevel@tonic-gate int
http_process_part_headers(http_handle_t handle,http_respinfo_t ** resp)11150Sstevel@tonic-gate http_process_part_headers(http_handle_t handle, http_respinfo_t **resp)
11160Sstevel@tonic-gate {
11170Sstevel@tonic-gate 	http_conn_t *c_id = handle;
11180Sstevel@tonic-gate 	char	line[MAXHOSTNAMELEN];
11190Sstevel@tonic-gate 	int	count;
11200Sstevel@tonic-gate 	int 	limit;
11210Sstevel@tonic-gate 	int	i;
11220Sstevel@tonic-gate 
11230Sstevel@tonic-gate 	ERR_clear_error();
11240Sstevel@tonic-gate 	if (!http_check_conn(c_id))
11250Sstevel@tonic-gate 		return (-1);
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate 	if (c_id->is_multipart == 0) {
11280Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOTMULTI);
11290Sstevel@tonic-gate 		return (-1);
11300Sstevel@tonic-gate 	}
11310Sstevel@tonic-gate 
11320Sstevel@tonic-gate 	/*
11330Sstevel@tonic-gate 	 * Figure out how many empty lines to allow.  Before the first
11340Sstevel@tonic-gate 	 * boundary of the transmission, there can be any number of
11350Sstevel@tonic-gate 	 * empty lines (from 0 up).  Limit these to some reasonable
11360Sstevel@tonic-gate 	 * failsafe.
11370Sstevel@tonic-gate 	 *
11380Sstevel@tonic-gate 	 * For the 2nd and later boundaries, there is supposed to be
11390Sstevel@tonic-gate 	 * one crlf pair.  However, many implementations don't require
11400Sstevel@tonic-gate 	 * it.  So don't require it.
11410Sstevel@tonic-gate 	 */
11420Sstevel@tonic-gate 	if (c_id->is_firstpart) {
11430Sstevel@tonic-gate 		limit = FAILSAFE;
11440Sstevel@tonic-gate 		c_id->is_firstpart = B_FALSE;
11450Sstevel@tonic-gate 	} else
11460Sstevel@tonic-gate 		limit = 1;
11470Sstevel@tonic-gate 
11480Sstevel@tonic-gate 	/* Look for the boundary line. */
11490Sstevel@tonic-gate 	count = 0;
1150*13093SRoger.Faulkner@Oracle.COM 	while ((i = getaline(c_id, line, sizeof (line), B_TRUE)) == 0 &&
11510Sstevel@tonic-gate 	    count < FAILSAFE)
11520Sstevel@tonic-gate 		count ++;
11530Sstevel@tonic-gate 	if (i < 0 || count > limit) {
11540Sstevel@tonic-gate 		/*
11550Sstevel@tonic-gate 		 * If I/O error, cause was already put into
11560Sstevel@tonic-gate 		 * error stack.  This is an additional error.
11570Sstevel@tonic-gate 		 */
11580Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOBOUNDARY);
11590Sstevel@tonic-gate 		return (-1);
11600Sstevel@tonic-gate 	}
11610Sstevel@tonic-gate 
11620Sstevel@tonic-gate 	free_response(c_id, B_FALSE);
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 	if (verbosemode)
11650Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE,
11660Sstevel@tonic-gate 		    "http_process_part_headers: %s", line);
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate 	/* Look for boundary line - '--<boundary text> */
11690Sstevel@tonic-gate 	if (line[0] != '-' || line[1] != '-' ||
11700Sstevel@tonic-gate 	    strncmp(&line[2], c_id->boundary, c_id->boundary_len) != 0) {
11710Sstevel@tonic-gate 		/* No boundary line.... */
11720Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOBOUNDARY);
11730Sstevel@tonic-gate 		return (-1);
11740Sstevel@tonic-gate 	}
11750Sstevel@tonic-gate 
11760Sstevel@tonic-gate 	/* Is this the end-of-parts boundary (ends with a trailing '--') */
11770Sstevel@tonic-gate 	if (strcmp(&line[c_id->boundary_len + 2], "--") == 0) {
11780Sstevel@tonic-gate 		return (1);
11790Sstevel@tonic-gate 	}
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate 	free_response(c_id, B_FALSE);
11820Sstevel@tonic-gate 	if (read_headerlines(c_id, B_TRUE) < 0) {
11830Sstevel@tonic-gate 		/* Error stack was already set at a lower level. */
11840Sstevel@tonic-gate 		return (-1);
11850Sstevel@tonic-gate 	}
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate 	/* Copy fields to the caller's structure */
11880Sstevel@tonic-gate 	if (resp != NULL) {
11890Sstevel@tonic-gate 		if ((*resp = malloc(sizeof (http_respinfo_t))) == NULL) {
11900Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
11910Sstevel@tonic-gate 			return (-1);
11920Sstevel@tonic-gate 		}
11930Sstevel@tonic-gate 		bzero(*resp, sizeof (http_respinfo_t));
11940Sstevel@tonic-gate 		(*resp)->code = ' ';
11950Sstevel@tonic-gate 		(*resp)->nresphdrs = c_id->resp.nresphdrs;
11960Sstevel@tonic-gate 	}
11970Sstevel@tonic-gate 
11980Sstevel@tonic-gate 	return (0);
11990Sstevel@tonic-gate }
12000Sstevel@tonic-gate 
12010Sstevel@tonic-gate /*
12020Sstevel@tonic-gate  * http_get_response_header - Get a line from the response header
12030Sstevel@tonic-gate  *
12040Sstevel@tonic-gate  *     ret = http_get_response_header(handle, whichline);
12050Sstevel@tonic-gate  *
12060Sstevel@tonic-gate  * Arguments:
12070Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
12080Sstevel@tonic-gate  *	whichline - Which line of the header to return.  This must be between
12090Sstevel@tonic-gate  *		  zero and resp.nresphdrs which was returned by the call to
12100Sstevel@tonic-gate  *		  http_process_headers().
12110Sstevel@tonic-gate  *
12120Sstevel@tonic-gate  * Returns:
12130Sstevel@tonic-gate  *	ptr	- Points to a copy of the header line.
12140Sstevel@tonic-gate  *	NULL	- An error occurred.  Check http_get_lasterr().
12150Sstevel@tonic-gate  */
12160Sstevel@tonic-gate char *
http_get_response_header(http_handle_t handle,uint_t which)12170Sstevel@tonic-gate http_get_response_header(http_handle_t handle, uint_t which)
12180Sstevel@tonic-gate {
12190Sstevel@tonic-gate 	http_conn_t *c_id = handle;
12200Sstevel@tonic-gate 	char *res;
12210Sstevel@tonic-gate 
12220Sstevel@tonic-gate 	if (!http_check_conn(c_id))
12230Sstevel@tonic-gate 		return (NULL);
12240Sstevel@tonic-gate 
12250Sstevel@tonic-gate 	if (which >= c_id->resp.nresphdrs) {
12260Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_OORANGE);
12270Sstevel@tonic-gate 		return (NULL);
12280Sstevel@tonic-gate 	}
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate 	res = strdup(c_id->resphdr[which]);
12310Sstevel@tonic-gate 	if (res == NULL) {
12320Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
12330Sstevel@tonic-gate 		return (NULL);
12340Sstevel@tonic-gate 	}
12350Sstevel@tonic-gate 	return (res);
12360Sstevel@tonic-gate }
12370Sstevel@tonic-gate 
12380Sstevel@tonic-gate /*
12390Sstevel@tonic-gate  * http_get_header_value - Get the value of a header line.
12400Sstevel@tonic-gate  *
12410Sstevel@tonic-gate  *     ret = http_get_header_value(handle, what);
12420Sstevel@tonic-gate  *
12430Sstevel@tonic-gate  * Arguments:
12440Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
12450Sstevel@tonic-gate  *	what	- The field name to look up.
12460Sstevel@tonic-gate  *
12470Sstevel@tonic-gate  * Returns:
12480Sstevel@tonic-gate  *	ptr	- Points to a copy of the header value.
12490Sstevel@tonic-gate  *	NULL	- An error occurred.  Check http_get_lasterr().
12500Sstevel@tonic-gate  */
12510Sstevel@tonic-gate char *
http_get_header_value(http_handle_t handle,const char * field_name)12520Sstevel@tonic-gate http_get_header_value(http_handle_t handle, const char *field_name)
12530Sstevel@tonic-gate {
12540Sstevel@tonic-gate 	http_conn_t *c_id = handle;
12550Sstevel@tonic-gate 	char	*ptr;
12560Sstevel@tonic-gate 	char	*res;
12570Sstevel@tonic-gate 	int	i;
12580Sstevel@tonic-gate 	int	n;
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 	if (!http_check_conn(c_id))
12610Sstevel@tonic-gate 		return (NULL);
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 	if (field_name == NULL || field_name[0] == '\0') {
12640Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
12650Sstevel@tonic-gate 		return (NULL);
12660Sstevel@tonic-gate 	}
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 	for (i = 0; i < c_id->resp.nresphdrs; i++) {
12690Sstevel@tonic-gate 		ptr = c_id->resphdr[i];
12700Sstevel@tonic-gate 		n = strlen(field_name);
12710Sstevel@tonic-gate 		if (strncasecmp(field_name, ptr, n) == 0 && ptr[n] == ':') {
12720Sstevel@tonic-gate 			ptr += n + 1;
12730Sstevel@tonic-gate 
12740Sstevel@tonic-gate 			while (isspace(*ptr))
12750Sstevel@tonic-gate 				ptr++;
12760Sstevel@tonic-gate 
12770Sstevel@tonic-gate 			res = strdup(ptr);
12780Sstevel@tonic-gate 			if (res == NULL) {
12790Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
12800Sstevel@tonic-gate 				return (NULL);
12810Sstevel@tonic-gate 			}
12820Sstevel@tonic-gate 			return (res);
12830Sstevel@tonic-gate 		}
12840Sstevel@tonic-gate 	}
12850Sstevel@tonic-gate 	SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMATCH);
12860Sstevel@tonic-gate 	return (NULL);
12870Sstevel@tonic-gate }
12880Sstevel@tonic-gate 
12890Sstevel@tonic-gate /*
12900Sstevel@tonic-gate  * http_read_body - Read the HTTP response body.
12910Sstevel@tonic-gate  *
12920Sstevel@tonic-gate  *     ret = http_read_body(handle, recv_buf_ptr, recv_buf_size);
12930Sstevel@tonic-gate  *
12940Sstevel@tonic-gate  * Arguments:
12950Sstevel@tonic-gate  *	handle	- Handle associated with the relevant connection
12960Sstevel@tonic-gate  *	recv_buf_ptr - Points to buffer to receive buffer
12970Sstevel@tonic-gate  *	recv_buf_size - Length in bytes of buffer.
12980Sstevel@tonic-gate  *
12990Sstevel@tonic-gate  * Returns:
13000Sstevel@tonic-gate  *	n	- Number of bytes read..
13010Sstevel@tonic-gate  *	< 0	- An error occurred.  This is (the number of bytes gotten + 1),
13020Sstevel@tonic-gate  *		  negated.  In other words, if 'n' bytes were read and then an
13030Sstevel@tonic-gate  *		  error occurred, this will return (-(n+1)).  So zero bytes
13040Sstevel@tonic-gate  *		  were read and then an error occurs, this will return -1.  If
13050Sstevel@tonic-gate  *		  1 byte was read, it will return -2, etc.  Specifics of the
13060Sstevel@tonic-gate  *		  error can be gotten using http_get_lasterr().
13070Sstevel@tonic-gate  *
13080Sstevel@tonic-gate  * Note that the errors will be set in the http_conn_t struct before the
13090Sstevel@tonic-gate  * function which detected the error returns.
13100Sstevel@tonic-gate  */
13110Sstevel@tonic-gate int
http_read_body(http_handle_t handle,char * recv_buf_ptr,size_t recv_buf_size)13120Sstevel@tonic-gate http_read_body(http_handle_t handle, char *recv_buf_ptr, size_t recv_buf_size)
13130Sstevel@tonic-gate {
13140Sstevel@tonic-gate 	http_conn_t *c_id = handle;
13150Sstevel@tonic-gate 
13160Sstevel@tonic-gate 	ERR_clear_error();
13170Sstevel@tonic-gate 	if (!http_check_conn(c_id))
13180Sstevel@tonic-gate 		return (-1);
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 	if (recv_buf_ptr == NULL || recv_buf_size == 0) {
13210Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
13220Sstevel@tonic-gate 		return (-1);
13230Sstevel@tonic-gate 	}
13240Sstevel@tonic-gate 
13250Sstevel@tonic-gate 	return (getbytes(c_id, recv_buf_ptr, recv_buf_size));
13260Sstevel@tonic-gate }
13270Sstevel@tonic-gate 
13280Sstevel@tonic-gate /*
13290Sstevel@tonic-gate  * http_srv_disconnect - Get rid of the connection to the server without
13300Sstevel@tonic-gate  *			freeing the http_conn_t structure.
13310Sstevel@tonic-gate  *
13320Sstevel@tonic-gate  *     ret = http_srv_disconnect(handle);
13330Sstevel@tonic-gate  *
13340Sstevel@tonic-gate  * Arguments:
13350Sstevel@tonic-gate  *	handle	- Handle associated with the connection
13360Sstevel@tonic-gate  *
13370Sstevel@tonic-gate  * Returns:
13380Sstevel@tonic-gate  *	0	- Success
13390Sstevel@tonic-gate  *	-1	- An error occurred.  Specifics of the error can
13400Sstevel@tonic-gate  *		  be gotten using http_get_lasterr().
13410Sstevel@tonic-gate  */
13420Sstevel@tonic-gate int
http_srv_disconnect(http_handle_t handle)13430Sstevel@tonic-gate http_srv_disconnect(http_handle_t handle)
13440Sstevel@tonic-gate {
13450Sstevel@tonic-gate 	http_conn_t *c_id = handle;
13460Sstevel@tonic-gate 	int err_ret;
13470Sstevel@tonic-gate 
13480Sstevel@tonic-gate 	ERR_clear_error();
13490Sstevel@tonic-gate 	if (!http_check_conn(c_id))
13500Sstevel@tonic-gate 		return (-1);
13510Sstevel@tonic-gate 
13520Sstevel@tonic-gate 	err_ret = free_ctx_ssl(c_id);
13530Sstevel@tonic-gate 	bzero(&c_id->inbuf, sizeof (c_id->inbuf));
13540Sstevel@tonic-gate 	free_response(c_id, B_TRUE);
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate 	return (err_ret);
13570Sstevel@tonic-gate }
13580Sstevel@tonic-gate 
13590Sstevel@tonic-gate /*
13600Sstevel@tonic-gate  * http_srv_close - Close the connection and clean up the http_conn_t
13610Sstevel@tonic-gate  *		structure.
13620Sstevel@tonic-gate  *
13630Sstevel@tonic-gate  *     http_srv_close(handle);
13640Sstevel@tonic-gate  *
13650Sstevel@tonic-gate  * Arguments:
13660Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
13670Sstevel@tonic-gate  *
13680Sstevel@tonic-gate  * Returns:
13690Sstevel@tonic-gate  *	0	- Success
13700Sstevel@tonic-gate  *	-1	- An error occurred.  Specifics of the error can
13710Sstevel@tonic-gate  *		  be gotten using http_get_lasterr().
13720Sstevel@tonic-gate  */
13730Sstevel@tonic-gate int
http_srv_close(http_handle_t handle)13740Sstevel@tonic-gate http_srv_close(http_handle_t handle)
13750Sstevel@tonic-gate {
13760Sstevel@tonic-gate 	http_conn_t *c_id = handle;
13770Sstevel@tonic-gate 	int err_ret = 0;
13780Sstevel@tonic-gate 
13790Sstevel@tonic-gate 	if (!http_check_conn(c_id))
13800Sstevel@tonic-gate 		return (-1);
13810Sstevel@tonic-gate 
13820Sstevel@tonic-gate 	if (c_id->ctx != NULL || c_id->ssl != NULL || c_id->fd != -1)
13830Sstevel@tonic-gate 		err_ret = http_srv_disconnect(handle);
13840Sstevel@tonic-gate 
13850Sstevel@tonic-gate 	free(c_id->basic_auth_userid);
13860Sstevel@tonic-gate 	free(c_id->basic_auth_password);
13870Sstevel@tonic-gate 	free(c_id->resp.statusmsg);
13880Sstevel@tonic-gate 	free(c_id->client_cert_file);
13890Sstevel@tonic-gate 	free(c_id->private_key_file);
13900Sstevel@tonic-gate 	free(c_id->random_file);
13910Sstevel@tonic-gate 	free(c_id->file_password);
13920Sstevel@tonic-gate 	c_id->signature = 0;
13930Sstevel@tonic-gate 
13940Sstevel@tonic-gate 	free(c_id);
13950Sstevel@tonic-gate 	return (err_ret);
13960Sstevel@tonic-gate }
13970Sstevel@tonic-gate 
13980Sstevel@tonic-gate /*
13990Sstevel@tonic-gate  * http_get_conn_info - Return current information about the connection
14000Sstevel@tonic-gate  *
14010Sstevel@tonic-gate  *     err = http_get_conn_info(handle);
14020Sstevel@tonic-gate  *
14030Sstevel@tonic-gate  * Arguments:
14040Sstevel@tonic-gate  *	handle	- Handle associated with the connection in question
14050Sstevel@tonic-gate  *
14060Sstevel@tonic-gate  * Returns:
14070Sstevel@tonic-gate  *	non_NULL- Points to structure
14080Sstevel@tonic-gate  *	NULL	- An error exists.  Check http_get_lasterr().
14090Sstevel@tonic-gate  */
14100Sstevel@tonic-gate http_conninfo_t *
http_get_conn_info(http_handle_t handle)14110Sstevel@tonic-gate http_get_conn_info(http_handle_t handle)
14120Sstevel@tonic-gate {
14130Sstevel@tonic-gate 	http_conn_t *c_id = handle;
14140Sstevel@tonic-gate 	http_conninfo_t *info;
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate 	if (!http_check_conn(c_id))
14170Sstevel@tonic-gate 		return (NULL);
14180Sstevel@tonic-gate 
14190Sstevel@tonic-gate 	info = malloc(sizeof (*info));
14200Sstevel@tonic-gate 	if (info == NULL) {
14210Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
14220Sstevel@tonic-gate 		return (NULL);
14230Sstevel@tonic-gate 	}
14240Sstevel@tonic-gate 
14250Sstevel@tonic-gate 	bzero(info, sizeof (*info));
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 	info->uri = c_id->uri;
14280Sstevel@tonic-gate 	info->proxy = c_id->proxy;
14290Sstevel@tonic-gate 	info->keepalive = c_id->keepalive;
14300Sstevel@tonic-gate 	info->read_timeout = c_id->read_timeout;
14310Sstevel@tonic-gate 
14320Sstevel@tonic-gate 	return (info);
14330Sstevel@tonic-gate }
14340Sstevel@tonic-gate 
14350Sstevel@tonic-gate /*
14360Sstevel@tonic-gate  * http_get_lasterr - Return the next error on the last operation
14370Sstevel@tonic-gate  *
14380Sstevel@tonic-gate  *     err = http_get_lasterr(handle, errsrc);
14390Sstevel@tonic-gate  *
14400Sstevel@tonic-gate  * Arguments:
14410Sstevel@tonic-gate  *	handle	- Handle associated with the connection in question
14420Sstevel@tonic-gate  *		  If no valid handle exists yet, this can be NULL.
14430Sstevel@tonic-gate  *		  However, it must be checked with the very next call.
14440Sstevel@tonic-gate  *	errsrc	- Returns the Sources of errors (ERRSRC_* values).
14450Sstevel@tonic-gate  *
14460Sstevel@tonic-gate  * Returns:
14470Sstevel@tonic-gate  *	0	- No error exists
14480Sstevel@tonic-gate  *	<> 0	- The error.
14490Sstevel@tonic-gate  */
14500Sstevel@tonic-gate ulong_t
http_get_lasterr(http_handle_t handle,uint_t * errsrc)14510Sstevel@tonic-gate http_get_lasterr(http_handle_t handle, uint_t *errsrc)
14520Sstevel@tonic-gate {
14530Sstevel@tonic-gate 	http_conn_t *c_id = handle;
14540Sstevel@tonic-gate 	ulong_t src;
14550Sstevel@tonic-gate 	ulong_t err;
14560Sstevel@tonic-gate 
14570Sstevel@tonic-gate 	if (c_id == NULL || c_id->signature != HTTP_CONN_INFO) {
14580Sstevel@tonic-gate 		if (errsrc)
14590Sstevel@tonic-gate 			*errsrc = ERRSRC_LIBHTTP;
14600Sstevel@tonic-gate 		err = early_err;
14610Sstevel@tonic-gate 		early_err = 0;
14620Sstevel@tonic-gate 		return (err);
14630Sstevel@tonic-gate 	}
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate 	GET_ERR(c_id, src, err);
14660Sstevel@tonic-gate 	if (src == 0 && err == 0) {
14670Sstevel@tonic-gate 		if (errsrc)
14680Sstevel@tonic-gate 			*errsrc = ERRSRC_LIBHTTP;
14690Sstevel@tonic-gate 		err = early_err;
14700Sstevel@tonic-gate 		early_err = 0;
14710Sstevel@tonic-gate 		return (err);
14720Sstevel@tonic-gate 	}
14730Sstevel@tonic-gate 	if (errsrc)
14740Sstevel@tonic-gate 		*errsrc = src;
14750Sstevel@tonic-gate 	return (err);
14760Sstevel@tonic-gate }
14770Sstevel@tonic-gate 
14780Sstevel@tonic-gate /*
14790Sstevel@tonic-gate  * http_decode_err - Decode a libssl error
14800Sstevel@tonic-gate  *
14810Sstevel@tonic-gate  *     err = http_decode_err(err, errlib, errfunc, errcode);
14820Sstevel@tonic-gate  *
14830Sstevel@tonic-gate  * Arguments:
14840Sstevel@tonic-gate  *	err	- libssl/libcrypto error returned.
14850Sstevel@tonic-gate  *	errlib	- returns libssl/libcrypto sublibrary that caused the error
14860Sstevel@tonic-gate  *	errfunc	- returns function in that library
14870Sstevel@tonic-gate  *	errcode - returns error code
14880Sstevel@tonic-gate  *
14890Sstevel@tonic-gate  * Returns:
14900Sstevel@tonic-gate  *	None other than the above.
14910Sstevel@tonic-gate  */
14920Sstevel@tonic-gate void
http_decode_err(ulong_t err,int * errlib,int * errfunc,int * errcode)14930Sstevel@tonic-gate http_decode_err(ulong_t err, int *errlib, int *errfunc, int *errcode)
14940Sstevel@tonic-gate {
14950Sstevel@tonic-gate 	if (errlib)
14960Sstevel@tonic-gate 		*errlib = ERR_GET_LIB(err);
14970Sstevel@tonic-gate 	if (errfunc)
14980Sstevel@tonic-gate 		*errfunc = ERR_GET_FUNC(err);
14990Sstevel@tonic-gate 	if (errcode)
15000Sstevel@tonic-gate 		*errcode = ERR_GET_REASON(err);
15010Sstevel@tonic-gate }
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate /* ---------------------- private functions ----------------------- */
15040Sstevel@tonic-gate 
15050Sstevel@tonic-gate /*
15060Sstevel@tonic-gate  * http_req - Issue http request (either HEAD or GET)
15070Sstevel@tonic-gate  *
15080Sstevel@tonic-gate  *     ret = http_req(handle, abs_path, reqtype, curpos, len);
15090Sstevel@tonic-gate  *
15100Sstevel@tonic-gate  * Arguments:
15110Sstevel@tonic-gate  *	handle	- Handle associated with the desired connection
15120Sstevel@tonic-gate  *	abs_path- File name portion of the URI, beginning with a /.  Query,
15130Sstevel@tonic-gate  *		  segment, etc are allowed.
15140Sstevel@tonic-gate  *	type	- HTTP_REQ_TYPE_HEAD or HTTP_REQ_TYPE_GET
15150Sstevel@tonic-gate  *
15160Sstevel@tonic-gate  *	In the case of GET requests,
15170Sstevel@tonic-gate  *	  curpos- -1  - Range not used
15180Sstevel@tonic-gate  *		  >=0 - Beginning of range
15190Sstevel@tonic-gate  *	  len	- 0   - Range ends at the end of the file
15200Sstevel@tonic-gate  *		  >0  - Length of range.
15210Sstevel@tonic-gate  *
15220Sstevel@tonic-gate  * Returns:
15230Sstevel@tonic-gate  *	0	- Success
15240Sstevel@tonic-gate  *	-1	- An error occurred.  Check http_get_lasterr().
15250Sstevel@tonic-gate  */
15260Sstevel@tonic-gate static int
http_req(http_handle_t handle,const char * abs_path,http_req_t type,offset_t curpos,offset_t len)15270Sstevel@tonic-gate http_req(http_handle_t handle, const char *abs_path, http_req_t type,
15281279Svh115876     offset_t curpos, offset_t len)
15290Sstevel@tonic-gate {
15300Sstevel@tonic-gate 	http_conn_t *c_id = handle;
15310Sstevel@tonic-gate 	char	*request;
15320Sstevel@tonic-gate 	char	*reqtypename;
15330Sstevel@tonic-gate 	char	*newreq;
15340Sstevel@tonic-gate 	int	requestlen;
15350Sstevel@tonic-gate 	int	retval;
15360Sstevel@tonic-gate 	int	j;
15370Sstevel@tonic-gate 
15380Sstevel@tonic-gate 	ERR_clear_error();
15390Sstevel@tonic-gate 	if (!http_check_conn(c_id))
15400Sstevel@tonic-gate 		return (-1);
15410Sstevel@tonic-gate 
15420Sstevel@tonic-gate 	if (abs_path == NULL || abs_path[0] == '\0') {
15430Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
15440Sstevel@tonic-gate 		return (-1);
15450Sstevel@tonic-gate 	}
15460Sstevel@tonic-gate 
15470Sstevel@tonic-gate 	/* Determine the name for the request type */
15480Sstevel@tonic-gate 	switch (type) {
15490Sstevel@tonic-gate 	case HTTP_REQ_TYPE_GET:
15500Sstevel@tonic-gate 		reqtypename = "GET";
15510Sstevel@tonic-gate 		if (curpos < 0 && curpos != -1) {
15520Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
15530Sstevel@tonic-gate 			return (-1);
15540Sstevel@tonic-gate 		}
15550Sstevel@tonic-gate 		break;
15560Sstevel@tonic-gate 
15570Sstevel@tonic-gate 	case HTTP_REQ_TYPE_HEAD:
15580Sstevel@tonic-gate 		reqtypename = "HEAD";
15590Sstevel@tonic-gate 		break;
15600Sstevel@tonic-gate 
15610Sstevel@tonic-gate 	default:
15620Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
15630Sstevel@tonic-gate 		return (-1);
15640Sstevel@tonic-gate 	}
15650Sstevel@tonic-gate 
15660Sstevel@tonic-gate 	/* Do rudimentary checks on the absolute path */
15670Sstevel@tonic-gate 	if (abs_path == NULL || *abs_path != '/') {
15680Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADARG);
15690Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT, "http_req: invalid file path");
15700Sstevel@tonic-gate 		if (abs_path != NULL)
15710Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT, " %s", abs_path);
15720Sstevel@tonic-gate 		return (-1);
15730Sstevel@tonic-gate 	}
15740Sstevel@tonic-gate 	(void) strlcpy(CONN_ABSPATH, abs_path, MAXHOSTNAMELEN);
15750Sstevel@tonic-gate 
15760Sstevel@tonic-gate 	/*
15770Sstevel@tonic-gate 	 * Size the request.
15780Sstevel@tonic-gate 	 *
15790Sstevel@tonic-gate 	 * With proxy:
15800Sstevel@tonic-gate 	 *   reqtypename + " http://" + host + ":" + port + path +
15810Sstevel@tonic-gate 	 *						" HTTP/1.1\r\n" +
15820Sstevel@tonic-gate 	 * Without proxy:
15830Sstevel@tonic-gate 	 *   reqtypename + " " + path + " HTTP/1.1\r\n" +
15840Sstevel@tonic-gate 	 */
15850Sstevel@tonic-gate 	requestlen = strlen(reqtypename) + 8 + strlen(CONN_HOSTNAME) + 1 +
15860Sstevel@tonic-gate 	    count_digits(CONN_PORT) + strlen(CONN_ABSPATH) + 11;
15870Sstevel@tonic-gate 
15880Sstevel@tonic-gate 	/*
15890Sstevel@tonic-gate 	 * Plus the rest:
15900Sstevel@tonic-gate 	 *   "Host: " + targethost + ":" + count_digits(port) + "\r\n" +
15910Sstevel@tonic-gate 	 *   "Connection: Keep-Alive\r\n" plus trailing "\r\n\0"
15920Sstevel@tonic-gate 	 */
15930Sstevel@tonic-gate 	requestlen += 6 + strlen(CONN_HOSTNAME) + 1 +
15940Sstevel@tonic-gate 	    count_digits(CONN_PORT) + 2 + 24 + 3;
15950Sstevel@tonic-gate 	if ((request = malloc(requestlen)) == NULL) {
15960Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
15970Sstevel@tonic-gate 		return (-1);
15980Sstevel@tonic-gate 	}
15990Sstevel@tonic-gate 
16000Sstevel@tonic-gate 	/* The request line */
16010Sstevel@tonic-gate 	if (c_id->proxied && c_id->ssl == NULL) {
16020Sstevel@tonic-gate 		j = snprintf(request, requestlen,
16030Sstevel@tonic-gate 		    "%s http://%s:%d%s HTTP/1.1\r\n",
16040Sstevel@tonic-gate 		    reqtypename, CONN_HOSTNAME, CONN_PORT,
16050Sstevel@tonic-gate 		    CONN_ABSPATH);
16060Sstevel@tonic-gate 	} else {
16070Sstevel@tonic-gate 		j = snprintf(request, requestlen, "%s %s HTTP/1.1\r\n",
16080Sstevel@tonic-gate 		    reqtypename, CONN_ABSPATH);
16090Sstevel@tonic-gate 	}
16100Sstevel@tonic-gate 
16110Sstevel@tonic-gate 	/* Ancillary headers */
16120Sstevel@tonic-gate 	j += snprintf(&request[j], requestlen - j, "Host: %s:%d\r\n",
16130Sstevel@tonic-gate 	    CONN_HOSTNAME, CONN_PORT);
16140Sstevel@tonic-gate 	if (!c_id->keepalive)
16150Sstevel@tonic-gate 		j += snprintf(&request[j], requestlen - j,
16160Sstevel@tonic-gate 		    "Connection: close\r\n");
16170Sstevel@tonic-gate 	else
16180Sstevel@tonic-gate 		j += snprintf(&request[j], requestlen - j,
16190Sstevel@tonic-gate 		    "Connection: Keep-Alive\r\n");
16200Sstevel@tonic-gate 	/*
16210Sstevel@tonic-gate 	 * We only send the range header on GET requests
16220Sstevel@tonic-gate 	 *
16230Sstevel@tonic-gate 	 * "Range: bytes=" + from + "-" + end + "\r\n" or
16240Sstevel@tonic-gate 	 * "Range: bytes=" + from + "-"  "\r\n"
16250Sstevel@tonic-gate 	 */
16260Sstevel@tonic-gate 	if (type == HTTP_REQ_TYPE_GET && curpos >= 0) {
16271279Svh115876 		offset_t endpos;
16280Sstevel@tonic-gate 
16290Sstevel@tonic-gate 		requestlen += 13 + count_digits(curpos) + 1 + 2;
16300Sstevel@tonic-gate 		if (len > 0) {
16310Sstevel@tonic-gate 			endpos = curpos + len - 1;
16320Sstevel@tonic-gate 			requestlen += count_digits(endpos);
16330Sstevel@tonic-gate 		}
16340Sstevel@tonic-gate 
16350Sstevel@tonic-gate 		if ((newreq = realloc(request, requestlen)) == NULL) {
16360Sstevel@tonic-gate 			free(request);
16370Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
16380Sstevel@tonic-gate 			return (-1);
16390Sstevel@tonic-gate 		}
16400Sstevel@tonic-gate 		request = newreq;
16410Sstevel@tonic-gate 
16421275Svh115876 		j += sprintf(&request[j], "Range: bytes=%lld-", curpos);
16430Sstevel@tonic-gate 		if (len > 0)
16441275Svh115876 			j += sprintf(&request[j], "%lld", endpos);
16450Sstevel@tonic-gate 		j += sprintf(&request[j], "\r\n");
16460Sstevel@tonic-gate 	}
16470Sstevel@tonic-gate 
16480Sstevel@tonic-gate 	/*
16490Sstevel@tonic-gate 	 * Authorization is added only if provided (RFC 2617, Section 2)
16500Sstevel@tonic-gate 	 *
16510Sstevel@tonic-gate 	 * "Authorization: Basic " + authencstr + "\r\n"
16520Sstevel@tonic-gate 	 */
16530Sstevel@tonic-gate 	if (c_id->basic_auth_userid && c_id->basic_auth_password) {
16540Sstevel@tonic-gate 		char *authstr;
16550Sstevel@tonic-gate 		char *authencstr;
16560Sstevel@tonic-gate 		int authlen;
16570Sstevel@tonic-gate 
16580Sstevel@tonic-gate 		/*
16590Sstevel@tonic-gate 		 * Allow for concat(basic_auth_userid ":" basic_auth_password)
16600Sstevel@tonic-gate 		 */
16610Sstevel@tonic-gate 		authlen = strlen(c_id->basic_auth_userid) + 2 +
1662*13093SRoger.Faulkner@Oracle.COM 		    strlen(c_id->basic_auth_password);
16630Sstevel@tonic-gate 		if ((authstr = malloc(authlen + 1)) == NULL) {
16640Sstevel@tonic-gate 			free(request);
16650Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
16660Sstevel@tonic-gate 			return (-1);
16670Sstevel@tonic-gate 		}
16680Sstevel@tonic-gate 		(void) snprintf(authstr, authlen + 1, "%s:%s",
16690Sstevel@tonic-gate 		    c_id->basic_auth_userid, c_id->basic_auth_password);
16700Sstevel@tonic-gate 
16710Sstevel@tonic-gate 		/* 3 bytes encoded as 4 (round up) with null termination */
16720Sstevel@tonic-gate 		if ((authencstr = malloc((authlen + 2) / 3 * 4 + 1)) == NULL) {
16730Sstevel@tonic-gate 			free(authstr);
16740Sstevel@tonic-gate 			free(request);
16750Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
16760Sstevel@tonic-gate 			return (-1);
16770Sstevel@tonic-gate 		}
16780Sstevel@tonic-gate 
16790Sstevel@tonic-gate 		(void) EVP_EncodeBlock((unsigned char *)authencstr,
1680*13093SRoger.Faulkner@Oracle.COM 		    (unsigned char *)authstr, authlen);
16810Sstevel@tonic-gate 
16820Sstevel@tonic-gate 		/*
16830Sstevel@tonic-gate 		 * Finally do concat(Authorization: Basic " authencstr "\r\n")
16840Sstevel@tonic-gate 		 */
16850Sstevel@tonic-gate 		requestlen += 21 + strlen(authencstr) + 2;
16860Sstevel@tonic-gate 		if ((newreq = realloc(request, requestlen)) == NULL) {
16870Sstevel@tonic-gate 			free(authencstr);
16880Sstevel@tonic-gate 			free(authstr);
16890Sstevel@tonic-gate 			free(request);
16900Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
16910Sstevel@tonic-gate 			return (-1);
16920Sstevel@tonic-gate 		}
16930Sstevel@tonic-gate 		request = newreq;
16940Sstevel@tonic-gate 
16950Sstevel@tonic-gate 		j += snprintf(&request[j], requestlen - j,
16960Sstevel@tonic-gate 		    "Authorization: Basic %s\r\n", authencstr);
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate 		free(authencstr);
16990Sstevel@tonic-gate 		free(authstr);
17000Sstevel@tonic-gate 	}
17010Sstevel@tonic-gate 
17020Sstevel@tonic-gate 	j += sprintf(&request[j], "\r\n");
17030Sstevel@tonic-gate 
17040Sstevel@tonic-gate 	if (verbosemode)
17050Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE, "%s", request);
17060Sstevel@tonic-gate 
17070Sstevel@tonic-gate 	/* send the HTTP request */
17080Sstevel@tonic-gate 	retval = http_srv_send(c_id, request, j);
17090Sstevel@tonic-gate 
17100Sstevel@tonic-gate 	free(request);
17110Sstevel@tonic-gate 	if (retval != j) {
17120Sstevel@tonic-gate 		/* Assume error in was set by send request. */
17130Sstevel@tonic-gate 		return (-1);
17140Sstevel@tonic-gate 	}
17150Sstevel@tonic-gate 
17160Sstevel@tonic-gate 	return (0);
17170Sstevel@tonic-gate }
17180Sstevel@tonic-gate 
17190Sstevel@tonic-gate /*
17200Sstevel@tonic-gate  * password_cb - Callback to get private key password and return it
17210Sstevel@tonic-gate  *               to SSL.  (Used for PEM certificates only.)
17220Sstevel@tonic-gate  *
17230Sstevel@tonic-gate  * 	len = passwd_cb(buf, buflen, rwflag, userdata);
17240Sstevel@tonic-gate  *
17250Sstevel@tonic-gate  *  Arguments:
17260Sstevel@tonic-gate  *     buf	- Buffer for the password
17270Sstevel@tonic-gate  *     buflen	- Length of 'buf'
17280Sstevel@tonic-gate  *     rwflag	- password will be used for reading/decryption (== 0)
17290Sstevel@tonic-gate  *		  or writing/encryption (== 1).
17300Sstevel@tonic-gate  *     userdata	- Points to connection-specific information.
17310Sstevel@tonic-gate  *
17320Sstevel@tonic-gate  *  Returns:
17330Sstevel@tonic-gate  *     > 0	- Length of password that was put into 'buf'.
17340Sstevel@tonic-gate  *     0 	- No password was returned (usually error occurred)
17350Sstevel@tonic-gate  *
17360Sstevel@tonic-gate  * NOTE:  The password code is not thread safe
17370Sstevel@tonic-gate  */
17380Sstevel@tonic-gate /* ARGSUSED */
17390Sstevel@tonic-gate static int
password_cb(char * buf,int buflen,int rwflag,void * userdata)17400Sstevel@tonic-gate password_cb(char *buf, int buflen, int rwflag, void *userdata)
17410Sstevel@tonic-gate {
17420Sstevel@tonic-gate 	http_conn_t *c_id = userdata;
17430Sstevel@tonic-gate 
17440Sstevel@tonic-gate 	if (c_id == NULL || c_id->signature != HTTP_CONN_INFO)
17450Sstevel@tonic-gate 		return (0);
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 	if (c_id->file_password == NULL ||
17480Sstevel@tonic-gate 	    buflen < strlen(c_id->file_password) + 1)
17490Sstevel@tonic-gate 		return (0);
17500Sstevel@tonic-gate 
17510Sstevel@tonic-gate 	return (strlcpy(buf, c_id->file_password, buflen));
17520Sstevel@tonic-gate }
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate /*
17550Sstevel@tonic-gate  * initialize_ctx - Initialize the context for a connection.
17560Sstevel@tonic-gate  *
17570Sstevel@tonic-gate  *       ctx = initialize_ctx(c_id);
17580Sstevel@tonic-gate  *
17590Sstevel@tonic-gate  *  Arguments:
17600Sstevel@tonic-gate  *     None.
17610Sstevel@tonic-gate  *
17620Sstevel@tonic-gate  *  Returns:
17630Sstevel@tonic-gate  *     non-NULL	- Points to ctx structure.
17640Sstevel@tonic-gate  *     NULL	- An error occurred.  Any cleanup is done and error
17650Sstevel@tonic-gate  *                information is in the error stack.
17660Sstevel@tonic-gate  */
17670Sstevel@tonic-gate static SSL_CTX *
initialize_ctx(http_conn_t * c_id)17680Sstevel@tonic-gate initialize_ctx(http_conn_t *c_id)
17690Sstevel@tonic-gate {
17700Sstevel@tonic-gate 	SSL_METHOD	*meth;
17710Sstevel@tonic-gate 	SSL_CTX		*ctx;
17720Sstevel@tonic-gate 
17730Sstevel@tonic-gate 	ERR_clear_error();
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate 	/* Global system initialization */
17760Sstevel@tonic-gate 	if (ssl_init == 0) {
17770Sstevel@tonic-gate 		sunw_crypto_init();
17780Sstevel@tonic-gate 		SSL_load_error_strings();
17790Sstevel@tonic-gate 		ssl_init = 1;
17800Sstevel@tonic-gate 	}
17810Sstevel@tonic-gate 
17820Sstevel@tonic-gate 	/* Create our context */
17830Sstevel@tonic-gate 	meth = SSLv3_client_method();
17840Sstevel@tonic-gate 	if ((ctx = SSL_CTX_new(meth)) == NULL) {
17850Sstevel@tonic-gate 		ulong_t err;
17860Sstevel@tonic-gate 		while ((err = ERR_get_error()) != 0)
17870Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
17880Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
17890Sstevel@tonic-gate 			    "initialize_ctx: SSL_CTX_new returned NULL");
17900Sstevel@tonic-gate 		return (NULL);
17910Sstevel@tonic-gate 	}
17920Sstevel@tonic-gate 
17930Sstevel@tonic-gate 	/*
17940Sstevel@tonic-gate 	 * Ensure that any renegotiations for blocking connections will
17950Sstevel@tonic-gate 	 * be done automatically.  (The alternative is to return partial
17960Sstevel@tonic-gate 	 * reads to the caller and let it oversee the renegotiations.)
17970Sstevel@tonic-gate 	 */
17980Sstevel@tonic-gate 	if (SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY) == 0) {
17990Sstevel@tonic-gate 		ulong_t err;
18000Sstevel@tonic-gate 		while ((err = ERR_get_error()) != 0)
18010Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
18020Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
18030Sstevel@tonic-gate 			    "initialize_ctx: SSL_CTX_set_mode returned 0");
18040Sstevel@tonic-gate 		(void) SSL_CTX_free(ctx);
18050Sstevel@tonic-gate 		return (NULL);
18060Sstevel@tonic-gate 	}
18070Sstevel@tonic-gate 
18080Sstevel@tonic-gate 	/* set cipher list if provided */
18090Sstevel@tonic-gate 	if (cipher_list != NULL) {
18100Sstevel@tonic-gate 		if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) {
18110Sstevel@tonic-gate 			ulong_t err;
18120Sstevel@tonic-gate 			while ((err = ERR_get_error()) != 0)
18130Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBSSL, err);
18140Sstevel@tonic-gate 				libbootlog(BOOTLOG_CRIT,
18150Sstevel@tonic-gate 				    "initialize_ctx: Error in cipher list");
18160Sstevel@tonic-gate 			SSL_CTX_free(ctx);
18170Sstevel@tonic-gate 			return (NULL);
18180Sstevel@tonic-gate 		}
18190Sstevel@tonic-gate 	}
18200Sstevel@tonic-gate 
18210Sstevel@tonic-gate 	/*
18220Sstevel@tonic-gate 	 * We attempt to use the client_certificate_file for the private
18230Sstevel@tonic-gate 	 * key input scheme *only* in the absence of private_key_file. In
18240Sstevel@tonic-gate 	 * this instance the scheme will be the same as that used for the
18250Sstevel@tonic-gate 	 * certificate input.
18260Sstevel@tonic-gate 	 */
18270Sstevel@tonic-gate 
18280Sstevel@tonic-gate 	/* Load our certificates */
18290Sstevel@tonic-gate 	if (c_id->client_cert_file != NULL) {
18300Sstevel@tonic-gate 		if (p12_format) {
18310Sstevel@tonic-gate 			/* Load pkcs12-formated files */
18320Sstevel@tonic-gate 			if (sunw_p12_use_certfile(ctx, c_id->client_cert_file,
18330Sstevel@tonic-gate 			    c_id->file_password)
18340Sstevel@tonic-gate 			    <= 0) {
18350Sstevel@tonic-gate 				ulong_t err;
18360Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
18370Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
18380Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
18390Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read "
18400Sstevel@tonic-gate 					    "PKCS12 certificate file");
18410Sstevel@tonic-gate 				SSL_CTX_free(ctx);
18420Sstevel@tonic-gate 				return (NULL);
18430Sstevel@tonic-gate 			}
18440Sstevel@tonic-gate 		} else {
18450Sstevel@tonic-gate 			/* Load PEM-formated files */
18460Sstevel@tonic-gate 			if (SSL_CTX_use_certificate_file(ctx,
18470Sstevel@tonic-gate 			    c_id->client_cert_file, SSL_FILETYPE_PEM) <= 0) {
18480Sstevel@tonic-gate 				ulong_t err;
18490Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
18500Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
18510Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
18520Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read "
18530Sstevel@tonic-gate 					    "PEM certificate file");
18540Sstevel@tonic-gate 				SSL_CTX_free(ctx);
18550Sstevel@tonic-gate 				return (NULL);
18560Sstevel@tonic-gate 			}
18570Sstevel@tonic-gate 		}
18580Sstevel@tonic-gate 		if (c_id->private_key_file == NULL)
18590Sstevel@tonic-gate 			c_id->private_key_file = c_id->client_cert_file;
18600Sstevel@tonic-gate 	}
18610Sstevel@tonic-gate 
18620Sstevel@tonic-gate 	/* Load our keys */
18630Sstevel@tonic-gate 	if (p12_format) {
18640Sstevel@tonic-gate 		/* Load pkcs12-formated files */
18650Sstevel@tonic-gate 		if (c_id->private_key_file != NULL) {
18660Sstevel@tonic-gate 			if (sunw_p12_use_keyfile(ctx, c_id->private_key_file,
18670Sstevel@tonic-gate 			    c_id->file_password)
18680Sstevel@tonic-gate 			    <= 0) {
18690Sstevel@tonic-gate 				ulong_t err;
18700Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
18710Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
18720Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
18730Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read "
18740Sstevel@tonic-gate 					    "PKCS12 key file");
18750Sstevel@tonic-gate 				SSL_CTX_free(ctx);
18760Sstevel@tonic-gate 				return (NULL);
18770Sstevel@tonic-gate 			}
18780Sstevel@tonic-gate 		}
18790Sstevel@tonic-gate 	} else {
18800Sstevel@tonic-gate 		/* Load PEM-formated files */
18810Sstevel@tonic-gate 		SSL_CTX_set_default_passwd_cb(ctx, password_cb);
18820Sstevel@tonic-gate 		SSL_CTX_set_default_passwd_cb_userdata(ctx, c_id);
18830Sstevel@tonic-gate 		if (c_id->private_key_file != NULL) {
18840Sstevel@tonic-gate 			if (SSL_CTX_use_PrivateKey_file(ctx,
18850Sstevel@tonic-gate 			    c_id->private_key_file, SSL_FILETYPE_PEM) <= 0) {
18860Sstevel@tonic-gate 				ulong_t err;
18870Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
18880Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
18890Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
18900Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read "
18910Sstevel@tonic-gate 					    "PEM key file");
18920Sstevel@tonic-gate 				SSL_CTX_free(ctx);
18930Sstevel@tonic-gate 				return (NULL);
18940Sstevel@tonic-gate 			}
18950Sstevel@tonic-gate 		}
18960Sstevel@tonic-gate 	}
18970Sstevel@tonic-gate 
18980Sstevel@tonic-gate 	/* Load the CAs we trust */
18990Sstevel@tonic-gate 	if (ca_verify_file != NULL) {
19000Sstevel@tonic-gate 		if (p12_format) {
19010Sstevel@tonic-gate 			if (sunw_p12_use_trustfile(ctx, ca_verify_file,
19020Sstevel@tonic-gate 			    c_id->file_password)
19030Sstevel@tonic-gate 			    <= 0) {
19040Sstevel@tonic-gate 				ulong_t err;
19050Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
19060Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
19070Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
19080Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read "
19090Sstevel@tonic-gate 					    "PKCS12 CA list file");
19100Sstevel@tonic-gate 				SSL_CTX_free(ctx);
19110Sstevel@tonic-gate 				return (NULL);
19120Sstevel@tonic-gate 			}
19130Sstevel@tonic-gate 		} else {
19140Sstevel@tonic-gate 			if (SSL_CTX_load_verify_locations(ctx, ca_verify_file,
19150Sstevel@tonic-gate 			    NULL) == 0) {
19160Sstevel@tonic-gate 				ulong_t err;
19170Sstevel@tonic-gate 				while ((err = ERR_get_error()) != 0)
19180Sstevel@tonic-gate 					SET_ERR(c_id, ERRSRC_LIBSSL, err);
19190Sstevel@tonic-gate 					libbootlog(BOOTLOG_CRIT,
19200Sstevel@tonic-gate 					    "initialize_ctx: Couldn't read PEM"
19210Sstevel@tonic-gate 					    " CA list file");
19220Sstevel@tonic-gate 				SSL_CTX_free(ctx);
19230Sstevel@tonic-gate 				return (NULL);
19240Sstevel@tonic-gate 			}
19250Sstevel@tonic-gate 		}
19260Sstevel@tonic-gate 	}
19270Sstevel@tonic-gate 
19280Sstevel@tonic-gate 	SSL_CTX_set_verify_depth(ctx, verify_depth);
19290Sstevel@tonic-gate 
19300Sstevel@tonic-gate 	/* Load randomness */
19310Sstevel@tonic-gate 	if (c_id->random_file != NULL &&
19320Sstevel@tonic-gate 	    RAND_load_file(c_id->random_file, 1024 * 1024) <= 0) {
19330Sstevel@tonic-gate 		ulong_t err;
19340Sstevel@tonic-gate 		while ((err = ERR_get_error()) != 0)
19350Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
19360Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
19370Sstevel@tonic-gate 			    "initialize_ctx: Couldn't load random file");
19380Sstevel@tonic-gate 		SSL_CTX_free(ctx);
19390Sstevel@tonic-gate 		return (NULL);
19400Sstevel@tonic-gate 	}
19410Sstevel@tonic-gate 	if (RAND_status() <= 0) {
19420Sstevel@tonic-gate 		ulong_t err;
19430Sstevel@tonic-gate 		while ((err = ERR_get_error()) != 0)
19440Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
19450Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
19460Sstevel@tonic-gate 			    "initialize_ctx: PRNG not seeded");
19470Sstevel@tonic-gate 		SSL_CTX_free(ctx);
19480Sstevel@tonic-gate 		return (NULL);
19490Sstevel@tonic-gate 	}
19500Sstevel@tonic-gate 
19510Sstevel@tonic-gate 	return (ctx);
19520Sstevel@tonic-gate }
19530Sstevel@tonic-gate 
19540Sstevel@tonic-gate /*
19550Sstevel@tonic-gate  * tcp_connect - Set up a TCP connection.
19560Sstevel@tonic-gate  *
19570Sstevel@tonic-gate  *         sock = tcp_connect(c_id, hostname, port);
19580Sstevel@tonic-gate  *
19590Sstevel@tonic-gate  * Arguments:
19600Sstevel@tonic-gate  *      c_id	 - Structure associated with the desired connection
19610Sstevel@tonic-gate  *	hostname - the host to connect to
19620Sstevel@tonic-gate  *	port	 - the port to connect to
19630Sstevel@tonic-gate  *
19640Sstevel@tonic-gate  * Returns:
19650Sstevel@tonic-gate  *      >= 0	- Socket number.
19660Sstevel@tonic-gate  *      -1	- Error occurred.  Error information is set in the
19670Sstevel@tonic-gate  *                error stack.  Any cleanup is done.
19680Sstevel@tonic-gate  *
19690Sstevel@tonic-gate  * This function established a connection to the target host.  When
19700Sstevel@tonic-gate  * it returns, the connection is ready for a HEAD or GET request.
19710Sstevel@tonic-gate  */
19720Sstevel@tonic-gate static int
tcp_connect(http_conn_t * c_id,const char * hostname,uint16_t port)19730Sstevel@tonic-gate tcp_connect(http_conn_t *c_id, const char *hostname, uint16_t port)
19740Sstevel@tonic-gate {
19750Sstevel@tonic-gate 	struct hostent	*hp;
19760Sstevel@tonic-gate 	struct sockaddr_in addr;
19770Sstevel@tonic-gate 	int	sock;
19780Sstevel@tonic-gate 	int	status;
19790Sstevel@tonic-gate 
19800Sstevel@tonic-gate 	if ((hp = gethostbyname(hostname)) == NULL) {
19810Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_RESOLVE, h_errno);
19820Sstevel@tonic-gate 		return (-1);
19830Sstevel@tonic-gate 	}
19840Sstevel@tonic-gate 
19850Sstevel@tonic-gate 	bzero(&addr, sizeof (addr));
19860Sstevel@tonic-gate 	/* LINTED */
19870Sstevel@tonic-gate 	addr.sin_addr = *(struct in_addr *)hp->h_addr;
19880Sstevel@tonic-gate 	addr.sin_family = AF_INET;
19890Sstevel@tonic-gate 	addr.sin_port = htons(port);
19900Sstevel@tonic-gate 
19910Sstevel@tonic-gate 	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
19920Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
19930Sstevel@tonic-gate 		return (-1);
19940Sstevel@tonic-gate 	}
19950Sstevel@tonic-gate 
19960Sstevel@tonic-gate 	status = connect(sock, (struct sockaddr *)&addr, sizeof (addr));
19970Sstevel@tonic-gate 	if (status < 0) {
19980Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
19990Sstevel@tonic-gate 		(void) socket_close(sock);
20000Sstevel@tonic-gate 		return (-1);
20010Sstevel@tonic-gate 	}
20020Sstevel@tonic-gate 
20030Sstevel@tonic-gate 	c_id->host_addr = addr;	/* save for future sendto calls */
20040Sstevel@tonic-gate 	c_id->fd = sock;
20050Sstevel@tonic-gate 
20060Sstevel@tonic-gate 	return (sock);
20070Sstevel@tonic-gate }
20080Sstevel@tonic-gate 
20090Sstevel@tonic-gate /*
20100Sstevel@tonic-gate  * readline - Get a line from the socket.  Discard the end-of-line
20110Sstevel@tonic-gate  *            (CR or CR/LF or LF).
20120Sstevel@tonic-gate  *
20130Sstevel@tonic-gate  *         ret = readline(c_id, sock, buf, len);
20140Sstevel@tonic-gate  *
20150Sstevel@tonic-gate  * Arguments:
20160Sstevel@tonic-gate  *      c_id	- Structure associated with the desired connection
20170Sstevel@tonic-gate  *      sock	- Socket to read
20180Sstevel@tonic-gate  *      buf   	- Buffer for the line
20190Sstevel@tonic-gate  *      len	- Length of the buffer
20200Sstevel@tonic-gate  *
20210Sstevel@tonic-gate  * Returns:
20220Sstevel@tonic-gate  *      0	- Success.  'buf' contains the line.
20230Sstevel@tonic-gate  *      -1	- Error occurred.  Error information is set in the
20240Sstevel@tonic-gate  *                error stack.
20250Sstevel@tonic-gate  */
20260Sstevel@tonic-gate static int
readline(http_conn_t * c_id,int sock,char * buf,int len)20270Sstevel@tonic-gate readline(http_conn_t *c_id, int sock, char *buf, int len)
20280Sstevel@tonic-gate {
20290Sstevel@tonic-gate 	int	n, r;
20300Sstevel@tonic-gate 	char	*ptr = buf;
20310Sstevel@tonic-gate 
20320Sstevel@tonic-gate 	for (n = 0; n < len; n++) {
20330Sstevel@tonic-gate 		r = socket_read(sock, ptr, 1, c_id->read_timeout);
20340Sstevel@tonic-gate 
20350Sstevel@tonic-gate 		if (r < 0) {
20360Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
20370Sstevel@tonic-gate 			return (-1);
20380Sstevel@tonic-gate 		} else if (r == 0) {
20390Sstevel@tonic-gate 			libbootlog(BOOTLOG_WARNING, "Readline: no data");
20400Sstevel@tonic-gate 			return (0);
20410Sstevel@tonic-gate 		}
20420Sstevel@tonic-gate 
20430Sstevel@tonic-gate 		if (*ptr == '\n') {
20440Sstevel@tonic-gate 			*ptr = '\0';
20450Sstevel@tonic-gate 
20460Sstevel@tonic-gate 			/* Strip off the CR if it's there */
20470Sstevel@tonic-gate 			if (buf[n-1] == '\r') {
20480Sstevel@tonic-gate 				buf[n-1] = '\0';
20490Sstevel@tonic-gate 				n--;
20500Sstevel@tonic-gate 			}
20510Sstevel@tonic-gate 
20520Sstevel@tonic-gate 			return (n);
20530Sstevel@tonic-gate 		}
20540Sstevel@tonic-gate 
20550Sstevel@tonic-gate 		ptr++;
20560Sstevel@tonic-gate 	}
20570Sstevel@tonic-gate 
20580Sstevel@tonic-gate 	libbootlog(BOOTLOG_WARNING, "readline: Buffer too short\n");
20590Sstevel@tonic-gate 	return (0);
20600Sstevel@tonic-gate }
20610Sstevel@tonic-gate 
20620Sstevel@tonic-gate /*
20630Sstevel@tonic-gate  * proxy_connect - Set up a proxied TCP connection to the target host.
20640Sstevel@tonic-gate  *
20650Sstevel@tonic-gate  *         sock = proxy_connect(c_id);
20660Sstevel@tonic-gate  *
20670Sstevel@tonic-gate  * Arguments:
20680Sstevel@tonic-gate  *      c_id  -	Structure associated with the desired connection
20690Sstevel@tonic-gate  *
20700Sstevel@tonic-gate  * Returns:
20710Sstevel@tonic-gate  *      >= 0	- Socket number.
20720Sstevel@tonic-gate  *      -1	- Error occurred.  Error information is set in the
20730Sstevel@tonic-gate  *                error stack.  Any cleanup is done.
20740Sstevel@tonic-gate  *
20750Sstevel@tonic-gate  * This function established a connection to the proxy and then sends
20760Sstevel@tonic-gate  * the request to connect to the target host.  It reads the response
20770Sstevel@tonic-gate  * (the status line and any headers).  When it returns, the connection
20780Sstevel@tonic-gate  * is ready for a HEAD or GET request.
20790Sstevel@tonic-gate  */
20800Sstevel@tonic-gate static int
proxy_connect(http_conn_t * c_id)20810Sstevel@tonic-gate proxy_connect(http_conn_t *c_id)
20820Sstevel@tonic-gate {
20830Sstevel@tonic-gate 	struct sockaddr_in addr;
20840Sstevel@tonic-gate 	int	sock;
20850Sstevel@tonic-gate 	char	buf[1024];
20860Sstevel@tonic-gate 	char	*ptr;
20870Sstevel@tonic-gate 	int	i;
20880Sstevel@tonic-gate 
20890Sstevel@tonic-gate 	if ((sock = tcp_connect(c_id, CONN_PROXY_HOSTNAME,
20900Sstevel@tonic-gate 	    CONN_PROXY_PORT)) < 0) {
20910Sstevel@tonic-gate 		return (-1);
20920Sstevel@tonic-gate 	}
20930Sstevel@tonic-gate 
20940Sstevel@tonic-gate 	if (!CONN_HTTPS) {
20950Sstevel@tonic-gate 		return (sock);
20960Sstevel@tonic-gate 	}
20970Sstevel@tonic-gate 
20980Sstevel@tonic-gate 	/* Now that we're connected, do the proxy request */
20990Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf),
21000Sstevel@tonic-gate 	    "CONNECT %s:%d HTTP/1.0\r\n\r\n", CONN_HOSTNAME, CONN_PORT);
21010Sstevel@tonic-gate 
21020Sstevel@tonic-gate 	/* socket_write sets the errors */
21030Sstevel@tonic-gate 	if (socket_write(sock, buf, strlen(buf), &addr) <= 0) {
21040Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
21050Sstevel@tonic-gate 		(void) socket_close(sock);
21060Sstevel@tonic-gate 		return (-1);
21070Sstevel@tonic-gate 	}
21080Sstevel@tonic-gate 
21090Sstevel@tonic-gate 	/* And read the response */
21100Sstevel@tonic-gate 	i = readline(c_id, sock, buf, sizeof (buf));
21110Sstevel@tonic-gate 	if (i <= 0) {
21120Sstevel@tonic-gate 		if (i == 0)
21130Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NORESP);
21140Sstevel@tonic-gate 			libbootlog(BOOTLOG_CRIT,
21150Sstevel@tonic-gate 			    "proxy_connect: Empty response from proxy");
21160Sstevel@tonic-gate 		(void) socket_close(sock);
21170Sstevel@tonic-gate 		return (-1);
21180Sstevel@tonic-gate 	}
21190Sstevel@tonic-gate 
21200Sstevel@tonic-gate 	ptr = buf;
21210Sstevel@tonic-gate 	if (strncmp(ptr, "HTTP", 4) != 0) {
21220Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOT_1_1);
21230Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
21240Sstevel@tonic-gate 		    "proxy_connect: Unrecognized protocol");
21250Sstevel@tonic-gate 		(void) socket_close(sock);
21260Sstevel@tonic-gate 		return (-1);
21270Sstevel@tonic-gate 	}
21280Sstevel@tonic-gate 
21290Sstevel@tonic-gate 	/* skip to the code */
21300Sstevel@tonic-gate 	ptr += 4;
21310Sstevel@tonic-gate 	while (*ptr != ' ' && *ptr != '\0')
21320Sstevel@tonic-gate 		ptr++;
21330Sstevel@tonic-gate 	while (*ptr == ' ' && *ptr != '\0')
21340Sstevel@tonic-gate 		ptr++;
21350Sstevel@tonic-gate 
21360Sstevel@tonic-gate 	/* make sure it's three digits */
21370Sstevel@tonic-gate 	if (strncmp(ptr, "200", 3) != 0) {
21380Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADRESP);
21390Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
21400Sstevel@tonic-gate 		    "proxy_connect: Received error from proxy server");
21410Sstevel@tonic-gate 		(void) socket_close(sock);
21420Sstevel@tonic-gate 		return (-1);
21430Sstevel@tonic-gate 	}
21440Sstevel@tonic-gate 	ptr += 3;
21450Sstevel@tonic-gate 	if (isdigit(*ptr)) {
21460Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADRESP);
21470Sstevel@tonic-gate 		(void) socket_close(sock);
21480Sstevel@tonic-gate 		return (-1);
21490Sstevel@tonic-gate 	}
21500Sstevel@tonic-gate 
21510Sstevel@tonic-gate 	/* Look for the blank line that signals end of proxy header */
21520Sstevel@tonic-gate 	while ((i = readline(c_id, sock, buf, sizeof (buf))) > 0)
21530Sstevel@tonic-gate 		;
21540Sstevel@tonic-gate 
21550Sstevel@tonic-gate 	if (i < 0) {
21560Sstevel@tonic-gate 		(void) socket_close(sock);
21570Sstevel@tonic-gate 		return (-1);
21580Sstevel@tonic-gate 	}
21590Sstevel@tonic-gate 
21600Sstevel@tonic-gate 	return (sock);
21610Sstevel@tonic-gate }
21620Sstevel@tonic-gate 
21630Sstevel@tonic-gate /*
21640Sstevel@tonic-gate  * check_cert_chain - Check if we have a valid certificate chain.
21650Sstevel@tonic-gate  *
21660Sstevel@tonic-gate  *      ret = check_cert_chain(c_id, host);
21670Sstevel@tonic-gate  *
21680Sstevel@tonic-gate  * Arguments:
21690Sstevel@tonic-gate  *    c_id	- Connection info.
21700Sstevel@tonic-gate  *    host	- Name to compare with the common name in the certificate.
21710Sstevel@tonic-gate  *
21720Sstevel@tonic-gate  * Returns:
21730Sstevel@tonic-gate  *    0		- Certificate chain and common name are both OK.
21740Sstevel@tonic-gate  *    -1	- Certificate chain and/or common name is not valid.
21750Sstevel@tonic-gate  */
21760Sstevel@tonic-gate static int
check_cert_chain(http_conn_t * c_id,char * host)21770Sstevel@tonic-gate check_cert_chain(http_conn_t *c_id, char *host)
21780Sstevel@tonic-gate {
21790Sstevel@tonic-gate 	X509	*peer;
21800Sstevel@tonic-gate 	char	peer_CN[256];
21810Sstevel@tonic-gate 	long	verify_err;
21820Sstevel@tonic-gate 
21830Sstevel@tonic-gate 	if ((verify_err = SSL_get_verify_result(c_id->ssl)) != X509_V_OK) {
21840Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_VERIFERR, verify_err);
21850Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
2186*13093SRoger.Faulkner@Oracle.COM 		    "check_cert_chain: Certificate doesn't verify");
21870Sstevel@tonic-gate 		return (-1);
21880Sstevel@tonic-gate 	}
21890Sstevel@tonic-gate 
21900Sstevel@tonic-gate 	/*
21910Sstevel@tonic-gate 	 * Check the cert chain. The chain length
21920Sstevel@tonic-gate 	 * is automatically checked by OpenSSL when we
21930Sstevel@tonic-gate 	 * set the verify depth in the ctx
21940Sstevel@tonic-gate 	 *
21950Sstevel@tonic-gate 	 * All we need to do here is check that the CN
21960Sstevel@tonic-gate 	 * matches
21970Sstevel@tonic-gate 	 */
21980Sstevel@tonic-gate 
21990Sstevel@tonic-gate 	/* Check the common name */
22000Sstevel@tonic-gate 	if ((peer = SSL_get_peer_certificate(c_id->ssl)) == NULL) {
22010Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOCERT);
22020Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
22030Sstevel@tonic-gate 		    "check_cert_chain: Peer did not present a certificate");
22040Sstevel@tonic-gate 		return (-1);
22050Sstevel@tonic-gate 	}
22060Sstevel@tonic-gate 	(void) X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
2207*13093SRoger.Faulkner@Oracle.COM 	    NID_commonName, peer_CN, 256);
22080Sstevel@tonic-gate 
22090Sstevel@tonic-gate 	if (verbosemode)
22100Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE,
22110Sstevel@tonic-gate 		    "server cert's peer_CN is %s, host is %s", peer_CN, host);
22120Sstevel@tonic-gate 
22130Sstevel@tonic-gate 	if (strcasecmp(peer_CN, host)) {
22140Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMATCH);
22150Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
22160Sstevel@tonic-gate 		    "check_cert_chain: Common name doesn't match host name");
22170Sstevel@tonic-gate 		libbootlog(BOOTLOG_CRIT,
22180Sstevel@tonic-gate 		    "peer_CN = %s, host = %s", peer_CN, host);
22190Sstevel@tonic-gate 		return (-1);
22200Sstevel@tonic-gate 	}
22210Sstevel@tonic-gate 
22220Sstevel@tonic-gate 	return (0);
22230Sstevel@tonic-gate }
22240Sstevel@tonic-gate 
22250Sstevel@tonic-gate /*
22260Sstevel@tonic-gate  * print_ciphers - Print the list of ciphers for debugging.
22270Sstevel@tonic-gate  *
22280Sstevel@tonic-gate  *       print_ciphers(ssl);
22290Sstevel@tonic-gate  *
22300Sstevel@tonic-gate  * Arguments:
22310Sstevel@tonic-gate  *     ssl	- SSL connection.
22320Sstevel@tonic-gate  *
22330Sstevel@tonic-gate  * Returns:
22340Sstevel@tonic-gate  *     none
22350Sstevel@tonic-gate  */
22360Sstevel@tonic-gate static void
print_ciphers(SSL * ssl)22370Sstevel@tonic-gate print_ciphers(SSL *ssl)
22380Sstevel@tonic-gate {
22390Sstevel@tonic-gate 	SSL_CIPHER	*c;
22400Sstevel@tonic-gate 	STACK_OF(SSL_CIPHER)	*sk;
22410Sstevel@tonic-gate 	int	i;
22420Sstevel@tonic-gate 	const char	*name;
22430Sstevel@tonic-gate 
22440Sstevel@tonic-gate 	if (ssl == NULL)
22450Sstevel@tonic-gate 		return;
22460Sstevel@tonic-gate 
22470Sstevel@tonic-gate 	sk = SSL_get_ciphers(ssl);
22480Sstevel@tonic-gate 	if (sk == NULL)
22490Sstevel@tonic-gate 		return;
22500Sstevel@tonic-gate 
22510Sstevel@tonic-gate 	for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
22520Sstevel@tonic-gate 		/* LINTED */
22530Sstevel@tonic-gate 		c = sk_SSL_CIPHER_value(sk, i);
22540Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE, "%08lx %s", c->id, c->name);
22550Sstevel@tonic-gate 	}
22560Sstevel@tonic-gate 	name = SSL_get_cipher_name(ssl);
22570Sstevel@tonic-gate 	if (name == NULL)
22580Sstevel@tonic-gate 		name = "";
22590Sstevel@tonic-gate 	libbootlog(BOOTLOG_VERBOSE, "Current cipher = %s", name);
22600Sstevel@tonic-gate }
22610Sstevel@tonic-gate 
22620Sstevel@tonic-gate /*
22630Sstevel@tonic-gate  * read_headerlines - Get the header lines from the server.  This reads
22640Sstevel@tonic-gate  *              lines until it gets a empty line indicating end of headers.
22650Sstevel@tonic-gate  *
22660Sstevel@tonic-gate  *       ret = read_headerlines(c_id);
22670Sstevel@tonic-gate  *
22680Sstevel@tonic-gate  * Arguments:
22690Sstevel@tonic-gate  *     c_id	- Info about the connection being read.
22700Sstevel@tonic-gate  *     bread	- TRUE if the headerlines are part of the message body.
22710Sstevel@tonic-gate  *
22720Sstevel@tonic-gate  * Returns:
22730Sstevel@tonic-gate  *     0	- Header lines were read.
22740Sstevel@tonic-gate  *     -1	- Error occurred.  The errors information is already in
22750Sstevel@tonic-gate  *                the error stack.
22760Sstevel@tonic-gate  *
22770Sstevel@tonic-gate  *  Read the lines.  If the current line begins with a space or tab, it is
22780Sstevel@tonic-gate  *  a continuation.  Take the new line and append it to the end of the
22790Sstevel@tonic-gate  *  previous line rather than making an entry for another line in
22800Sstevel@tonic-gate  *  c_id->resphdr.
22810Sstevel@tonic-gate  *
22820Sstevel@tonic-gate  *  Note that I/O errors are put into the error stack by http_srv_recv(),
2283*13093SRoger.Faulkner@Oracle.COM  *  which is called by getaline().
22840Sstevel@tonic-gate  */
22850Sstevel@tonic-gate static int
read_headerlines(http_conn_t * c_id,boolean_t bread)22860Sstevel@tonic-gate read_headerlines(http_conn_t *c_id, boolean_t bread)
22870Sstevel@tonic-gate {
22880Sstevel@tonic-gate 	char	line[MAXHOSTNAMELEN];
22890Sstevel@tonic-gate 	char	**new_buf;
22900Sstevel@tonic-gate 	char	*ptr;
22910Sstevel@tonic-gate 	int	next;
22920Sstevel@tonic-gate 	int	cur;
22930Sstevel@tonic-gate 	int	n;
22940Sstevel@tonic-gate 
22950Sstevel@tonic-gate 	/* process headers, stop when we get to an empty line */
22960Sstevel@tonic-gate 	cur = 0;
22970Sstevel@tonic-gate 	next = 0;
2298*13093SRoger.Faulkner@Oracle.COM 	while ((n = getaline(c_id, line, sizeof (line), bread)) > 0) {
22990Sstevel@tonic-gate 
23000Sstevel@tonic-gate 		if (verbosemode)
23010Sstevel@tonic-gate 			libbootlog(BOOTLOG_VERBOSE,
23020Sstevel@tonic-gate 			    "read_headerlines: %s", line);
23030Sstevel@tonic-gate 		/*
23040Sstevel@tonic-gate 		 * See if this is a continuation line (first col is a
23050Sstevel@tonic-gate 		 * space or a tab)
23060Sstevel@tonic-gate 		 */
23070Sstevel@tonic-gate 		if (line[0] != ' ' && line[0] != '	') {
23080Sstevel@tonic-gate 			cur = next;
23090Sstevel@tonic-gate 			next ++;
23100Sstevel@tonic-gate 			new_buf =
23110Sstevel@tonic-gate 			    realloc(c_id->resphdr, (cur + 1) * sizeof (void *));
23120Sstevel@tonic-gate 			if (new_buf == NULL) {
23130Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
23140Sstevel@tonic-gate 				return (-1);
23150Sstevel@tonic-gate 			}
23160Sstevel@tonic-gate 			c_id->resphdr = new_buf;
23170Sstevel@tonic-gate 
23180Sstevel@tonic-gate 			c_id->resphdr[cur] = strdup(line);
23190Sstevel@tonic-gate 			if (c_id->resphdr[cur] == NULL) {
23200Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
23210Sstevel@tonic-gate 				return (-1);
23220Sstevel@tonic-gate 			}
23230Sstevel@tonic-gate 		} else {
23240Sstevel@tonic-gate 			ptr = line;
23250Sstevel@tonic-gate 			while (isspace(*ptr))
23260Sstevel@tonic-gate 				ptr ++;
23270Sstevel@tonic-gate 			c_id->resphdr[cur] = realloc(c_id->resphdr[cur],
23280Sstevel@tonic-gate 			    strlen(c_id->resphdr[cur]) + strlen(ptr) + 1);
23290Sstevel@tonic-gate 			if (c_id->resphdr[cur] == NULL) {
23300Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOMEM);
23310Sstevel@tonic-gate 				return (-1);
23320Sstevel@tonic-gate 			}
23330Sstevel@tonic-gate 			(void) strcat(c_id->resphdr[cur], ptr);
23340Sstevel@tonic-gate 		}
23350Sstevel@tonic-gate 		ptr = &(c_id->resphdr[cur][strlen(c_id->resphdr[cur]) - 1]);
23360Sstevel@tonic-gate 		while (ptr > c_id->resphdr[cur] && isspace(*ptr))
23370Sstevel@tonic-gate 			ptr --;
23380Sstevel@tonic-gate 	}
23390Sstevel@tonic-gate 	c_id->resp.nresphdrs = next;
23400Sstevel@tonic-gate 
23410Sstevel@tonic-gate 	/* Cause of any I/O error was already put into error stack. */
23420Sstevel@tonic-gate 	return (n >= 0 ? 0 : -1);
23430Sstevel@tonic-gate }
23440Sstevel@tonic-gate 
23450Sstevel@tonic-gate static void
free_response(http_conn_t * c_id,int free_boundary)23460Sstevel@tonic-gate free_response(http_conn_t *c_id, int free_boundary)
23470Sstevel@tonic-gate {
23480Sstevel@tonic-gate 	int i;
23490Sstevel@tonic-gate 
23500Sstevel@tonic-gate 	/* free memory from previous calls */
23510Sstevel@tonic-gate 	if (c_id->resp.statusmsg != NULL) {
23520Sstevel@tonic-gate 		free(c_id->resp.statusmsg);
23530Sstevel@tonic-gate 		c_id->resp.statusmsg = NULL;
23540Sstevel@tonic-gate 	}
23550Sstevel@tonic-gate 	for (i = 0; i < c_id->resp.nresphdrs; i++) {
23560Sstevel@tonic-gate 		free(c_id->resphdr[i]);
23570Sstevel@tonic-gate 		c_id->resphdr[i] = NULL;
23580Sstevel@tonic-gate 	}
23590Sstevel@tonic-gate 	c_id->resp.nresphdrs = 0;
23600Sstevel@tonic-gate 	if (c_id->resphdr != NULL) {
23610Sstevel@tonic-gate 		free(c_id->resphdr);
23620Sstevel@tonic-gate 		c_id->resphdr = NULL;
23630Sstevel@tonic-gate 	}
23640Sstevel@tonic-gate 
23650Sstevel@tonic-gate 	if (free_boundary && c_id->boundary) {
23660Sstevel@tonic-gate 		free(c_id->boundary);
23670Sstevel@tonic-gate 		c_id->boundary = NULL;
23680Sstevel@tonic-gate 		c_id->is_multipart = B_FALSE;
23690Sstevel@tonic-gate 	}
23700Sstevel@tonic-gate }
23710Sstevel@tonic-gate 
23720Sstevel@tonic-gate static int
free_ctx_ssl(http_conn_t * c_id)23730Sstevel@tonic-gate free_ctx_ssl(http_conn_t *c_id)
23740Sstevel@tonic-gate {
23750Sstevel@tonic-gate 	int err_ret = 0;
23760Sstevel@tonic-gate 
23770Sstevel@tonic-gate 	if (c_id->ssl != NULL) {
23780Sstevel@tonic-gate 		if (SSL_shutdown(c_id->ssl) <= 0) {
23790Sstevel@tonic-gate 			ulong_t err;
23800Sstevel@tonic-gate 			while ((err = ERR_get_error()) != 0)
23810Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBSSL, err);
23820Sstevel@tonic-gate 			err_ret = -1;
23830Sstevel@tonic-gate 		}
23840Sstevel@tonic-gate 		SSL_free(c_id->ssl);
23850Sstevel@tonic-gate 		c_id->ssl = NULL;
23860Sstevel@tonic-gate 	}
23870Sstevel@tonic-gate 
23880Sstevel@tonic-gate 	if (c_id->fd != -1 && socket_close(c_id->fd) < 0) {
23890Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_SYSTEM, errno);
23900Sstevel@tonic-gate 		err_ret = -1;
23910Sstevel@tonic-gate 	}
23920Sstevel@tonic-gate 	c_id->fd = -1;
23930Sstevel@tonic-gate 
23940Sstevel@tonic-gate 	if (c_id->ctx != NULL) {
23950Sstevel@tonic-gate 		SSL_CTX_free(c_id->ctx);
23960Sstevel@tonic-gate 		c_id->ctx = NULL;
23970Sstevel@tonic-gate 	}
23980Sstevel@tonic-gate 
23990Sstevel@tonic-gate 	return (err_ret);
24000Sstevel@tonic-gate }
24010Sstevel@tonic-gate 
24020Sstevel@tonic-gate /*
24030Sstevel@tonic-gate  * get_chunk_header - Get a chunk header line
24040Sstevel@tonic-gate  *
24050Sstevel@tonic-gate  * Arguments:
24060Sstevel@tonic-gate  *   c_id   - Structure describing the connection in question.
24070Sstevel@tonic-gate  *
24080Sstevel@tonic-gate  * Returns:
24090Sstevel@tonic-gate  *  >=0	- Length of next chunk
24100Sstevel@tonic-gate  *  -1	- Error occurred.  The error information is in the error stack.
24110Sstevel@tonic-gate  */
24120Sstevel@tonic-gate static int
get_chunk_header(http_conn_t * c_id)24130Sstevel@tonic-gate get_chunk_header(http_conn_t *c_id)
24140Sstevel@tonic-gate {
24150Sstevel@tonic-gate 	char	line[MAXHOSTNAMELEN];
24160Sstevel@tonic-gate 	char	*ptr;
24170Sstevel@tonic-gate 	int	value;
24180Sstevel@tonic-gate 	int	ok;
24190Sstevel@tonic-gate 	int	i;
24200Sstevel@tonic-gate 
24210Sstevel@tonic-gate 	/*
24220Sstevel@tonic-gate 	 * Determine whether an extra crlf pair will precede the
24230Sstevel@tonic-gate 	 * chunk header.  For the first one, there is no preceding
24240Sstevel@tonic-gate 	 * crlf.  For later chunks, there is one crlf.
24250Sstevel@tonic-gate 	 */
24260Sstevel@tonic-gate 	if (c_id->is_firstchunk) {
24270Sstevel@tonic-gate 		ok = 1;
24280Sstevel@tonic-gate 		c_id->is_firstchunk = B_FALSE;
24290Sstevel@tonic-gate 	} else {
2430*13093SRoger.Faulkner@Oracle.COM 		ok = ((i = getaline(c_id, line, sizeof (line), B_FALSE)) == 0);
24310Sstevel@tonic-gate 	}
24320Sstevel@tonic-gate 
24330Sstevel@tonic-gate 	if (ok)
2434*13093SRoger.Faulkner@Oracle.COM 		i = getaline(c_id, line, sizeof (line), B_FALSE);
24350Sstevel@tonic-gate 	if (!ok || i < 0) {
24360Sstevel@tonic-gate 		/*
24370Sstevel@tonic-gate 		 * If I/O error, the Cause was already put into
24380Sstevel@tonic-gate 		 * error stack.  This is an additional error.
24390Sstevel@tonic-gate 		 */
24400Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_NOHEADER);
24410Sstevel@tonic-gate 		return (-1);
24420Sstevel@tonic-gate 	}
24430Sstevel@tonic-gate 
24440Sstevel@tonic-gate 	if (verbosemode)
24450Sstevel@tonic-gate 		libbootlog(BOOTLOG_VERBOSE, "get_chunk_header: <%s>", line);
24460Sstevel@tonic-gate 
24470Sstevel@tonic-gate 
24480Sstevel@tonic-gate 	/*
24490Sstevel@tonic-gate 	 * The first (and probably only) field in the line is the hex
24500Sstevel@tonic-gate 	 * length of the chunk.
24510Sstevel@tonic-gate 	 */
24520Sstevel@tonic-gate 	ptr = line;
24530Sstevel@tonic-gate 	value = 0;
24540Sstevel@tonic-gate 	while (*ptr != '\0' && (i = hexdigit(*ptr)) >= 0) {
24550Sstevel@tonic-gate 		value = (value << 4) + i;
24560Sstevel@tonic-gate 		ptr ++;
24570Sstevel@tonic-gate 	}
24580Sstevel@tonic-gate 
24590Sstevel@tonic-gate 	return (value);
24600Sstevel@tonic-gate }
24610Sstevel@tonic-gate 
24620Sstevel@tonic-gate /*
24630Sstevel@tonic-gate  * init_bread - Initialize the counters used to read message bodies.
24640Sstevel@tonic-gate  *
24650Sstevel@tonic-gate  * Arguments:
24660Sstevel@tonic-gate  *   c_id   - Structure describing the connection in question.
24670Sstevel@tonic-gate  *
24680Sstevel@tonic-gate  * Returns:
24690Sstevel@tonic-gate  *   0	- Success
24700Sstevel@tonic-gate  *  -1	- Error occurred.  The error information is in the error stack.
24710Sstevel@tonic-gate  *
24720Sstevel@tonic-gate  *  This routine will determine whether the message body being received is
24730Sstevel@tonic-gate  *  chunked or non-chunked. Once determined, the counters used to read
24740Sstevel@tonic-gate  *  message bodies will be initialized.
24750Sstevel@tonic-gate  */
24760Sstevel@tonic-gate static int
init_bread(http_conn_t * c_id)24770Sstevel@tonic-gate init_bread(http_conn_t *c_id)
24780Sstevel@tonic-gate {
24790Sstevel@tonic-gate 	char	*hdr;
24800Sstevel@tonic-gate 	char	*ptr;
24810Sstevel@tonic-gate 	boolean_t sized = B_FALSE;
24820Sstevel@tonic-gate 
24830Sstevel@tonic-gate 	/*
24840Sstevel@tonic-gate 	 * Assume non-chunked reads until proven otherwise.
24850Sstevel@tonic-gate 	 */
24860Sstevel@tonic-gate 	c_id->is_chunked = B_FALSE;
24870Sstevel@tonic-gate 	c_id->is_firstchunk = B_FALSE;
24880Sstevel@tonic-gate 	hdr = http_get_header_value(c_id, "Content-Length");
24890Sstevel@tonic-gate 	if (hdr != NULL) {
24900Sstevel@tonic-gate 		c_id->body_size = strtol(hdr, NULL, 10);
24910Sstevel@tonic-gate 		if (c_id->body_size == 0 && errno != 0) {
24920Sstevel@tonic-gate 			free(hdr);
24930Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADSIZE);
24940Sstevel@tonic-gate 			return (-1);
24950Sstevel@tonic-gate 		}
24960Sstevel@tonic-gate 		free(hdr);
24970Sstevel@tonic-gate 		sized = B_TRUE;
24980Sstevel@tonic-gate 	}
24990Sstevel@tonic-gate 
25000Sstevel@tonic-gate 	/*
25010Sstevel@tonic-gate 	 * If size was not determined above, then see if this is a
25020Sstevel@tonic-gate 	 * chunked message. Keep in mind that the first chunk size is
25030Sstevel@tonic-gate 	 * "special".
25040Sstevel@tonic-gate 	 */
25050Sstevel@tonic-gate 	if (!sized) {
25060Sstevel@tonic-gate 		hdr = http_get_header_value(c_id, "Transfer-Encoding");
25070Sstevel@tonic-gate 		if (hdr != NULL) {
25080Sstevel@tonic-gate 			ptr = eat_ws(hdr);
25090Sstevel@tonic-gate 			if (startswith((const char **)&ptr, "chunked;") ||
25100Sstevel@tonic-gate 			    strcasecmp(ptr, "chunked") == 0) {
25110Sstevel@tonic-gate 				c_id->is_firstchunk = B_TRUE;
25120Sstevel@tonic-gate 				c_id->is_chunked = B_TRUE;
25130Sstevel@tonic-gate 			}
25140Sstevel@tonic-gate 			free(hdr);
25150Sstevel@tonic-gate 			if (c_id->is_chunked) {
25160Sstevel@tonic-gate 				c_id->body_size = get_chunk_header(c_id);
25170Sstevel@tonic-gate 				if (c_id->body_size == -1) {
25180Sstevel@tonic-gate 					/*
25190Sstevel@tonic-gate 					 * Error stack was already set at a
25200Sstevel@tonic-gate 					 * lower level.
25210Sstevel@tonic-gate 					 */
25220Sstevel@tonic-gate 					return (-1);
25230Sstevel@tonic-gate 				}
25240Sstevel@tonic-gate 				sized = B_TRUE;
25250Sstevel@tonic-gate 			}
25260Sstevel@tonic-gate 		}
25270Sstevel@tonic-gate 	}
25280Sstevel@tonic-gate 
25290Sstevel@tonic-gate 	/*
25300Sstevel@tonic-gate 	 * Well, isn't this a fine predicament? It wasn't chunked or
25310Sstevel@tonic-gate 	 * non-chunked as far as we can tell.
25320Sstevel@tonic-gate 	 */
25330Sstevel@tonic-gate 	if (!sized) {
25340Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_BADSIZE);
25350Sstevel@tonic-gate 		return (-1);
25360Sstevel@tonic-gate 	}
25370Sstevel@tonic-gate 
25380Sstevel@tonic-gate 	c_id->body_read = 0;
25390Sstevel@tonic-gate 	c_id->body_size_tot = c_id->body_size;
25400Sstevel@tonic-gate 	c_id->body_read_tot = 0;
25410Sstevel@tonic-gate 
25420Sstevel@tonic-gate 	return (0);
25430Sstevel@tonic-gate }
25440Sstevel@tonic-gate 
25450Sstevel@tonic-gate /*
25460Sstevel@tonic-gate  * get_msgcnt - Get the number of bytes left in the message body or chunk.
25470Sstevel@tonic-gate  *
25480Sstevel@tonic-gate  * Arguments:
25490Sstevel@tonic-gate  *   c_id   - Structure describing the connection in question.
25500Sstevel@tonic-gate  *   msgcnt - Where to store the message count.
25510Sstevel@tonic-gate  *
25520Sstevel@tonic-gate  * Returns:
25530Sstevel@tonic-gate  *   0	- Success
25540Sstevel@tonic-gate  *  -1	- Error occurred.  The error information is in the error stack.
25550Sstevel@tonic-gate  *
25560Sstevel@tonic-gate  *  Note that if the message being read is not chunked, then the byte count
25570Sstevel@tonic-gate  *  is simply the message size minus the bytes read thus far. In the case of
25580Sstevel@tonic-gate  *  chunked messages, the byte count returned will be the number of bytes
25590Sstevel@tonic-gate  *  left in the chunk. If the current chunk has been exhausted, then this
25600Sstevel@tonic-gate  *  routine will determine the size of the next chunk. When the next chunk
25610Sstevel@tonic-gate  *  size is zero, the message has been read in its entirety.
25620Sstevel@tonic-gate  */
25630Sstevel@tonic-gate static int
get_msgcnt(http_conn_t * c_id,ssize_t * msgcnt)25640Sstevel@tonic-gate get_msgcnt(http_conn_t *c_id, ssize_t *msgcnt)
25650Sstevel@tonic-gate {
25660Sstevel@tonic-gate 	/*
25670Sstevel@tonic-gate 	 * If there are more bytes in the message, then return.
25680Sstevel@tonic-gate 	 */
25690Sstevel@tonic-gate 	*msgcnt = c_id->body_size - c_id->body_read;
25700Sstevel@tonic-gate 	if (*msgcnt != 0) {
25710Sstevel@tonic-gate 		return (0);
25720Sstevel@tonic-gate 	}
25730Sstevel@tonic-gate 	/*
25740Sstevel@tonic-gate 	 * If this is not a chunked message and the body has been
25750Sstevel@tonic-gate 	 * read, then we're done.
25760Sstevel@tonic-gate 	 */
25770Sstevel@tonic-gate 	if (!c_id->is_chunked) {
25780Sstevel@tonic-gate 		return (0);
25790Sstevel@tonic-gate 	}
25800Sstevel@tonic-gate 
25810Sstevel@tonic-gate 	/*
25820Sstevel@tonic-gate 	 * We're looking at a chunked message whose immediate
25830Sstevel@tonic-gate 	 * chunk has been totally processed. See if there is
25840Sstevel@tonic-gate 	 * another chunk.
25850Sstevel@tonic-gate 	 */
25860Sstevel@tonic-gate 	c_id->body_size = get_chunk_header(c_id);
25870Sstevel@tonic-gate 	if (c_id->body_size == -1) {
25880Sstevel@tonic-gate 		/*
25890Sstevel@tonic-gate 		 * Error stack was already set at a
25900Sstevel@tonic-gate 		 * lower level.
25910Sstevel@tonic-gate 		 */
25920Sstevel@tonic-gate 		return (-1);
25930Sstevel@tonic-gate 	}
25940Sstevel@tonic-gate 
25950Sstevel@tonic-gate 	/*
25960Sstevel@tonic-gate 	 * No bytes of this chunk have been processed yet.
25970Sstevel@tonic-gate 	 */
25980Sstevel@tonic-gate 	c_id->body_read = 0;
25990Sstevel@tonic-gate 
26000Sstevel@tonic-gate 	/*
26010Sstevel@tonic-gate 	 * A zero length chunk signals the end of the
26020Sstevel@tonic-gate 	 * message body and chunking.
26030Sstevel@tonic-gate 	 */
26040Sstevel@tonic-gate 	if (c_id->body_size == 0) {
26050Sstevel@tonic-gate 		c_id->is_chunked = B_FALSE;
26060Sstevel@tonic-gate 		return (0);
26070Sstevel@tonic-gate 	}
26080Sstevel@tonic-gate 
26090Sstevel@tonic-gate 	/*
26100Sstevel@tonic-gate 	 * There is another chunk.
26110Sstevel@tonic-gate 	 */
26120Sstevel@tonic-gate 	c_id->body_size_tot += c_id->body_size;
26130Sstevel@tonic-gate 	*msgcnt = c_id->body_size - c_id->body_read;
26140Sstevel@tonic-gate 
26150Sstevel@tonic-gate 	return (0);
26160Sstevel@tonic-gate }
26170Sstevel@tonic-gate 
26180Sstevel@tonic-gate /*
2619*13093SRoger.Faulkner@Oracle.COM  * getaline - Get lines of data from the HTTP response, up to 'len' bytes.
26200Sstevel@tonic-gate  *	  NOTE: the line will not end with a NULL if all 'len' bytes
26210Sstevel@tonic-gate  *	  were read.
26220Sstevel@tonic-gate  *
26230Sstevel@tonic-gate  * Arguments:
26240Sstevel@tonic-gate  *   c_id   - Structure describing the connection in question.
26250Sstevel@tonic-gate  *   line   - Where to store the data.
26260Sstevel@tonic-gate  *   len    - Maximum number of bytes in the line.
26270Sstevel@tonic-gate  *   bread  - TRUE if the lines are part of the message body.
26280Sstevel@tonic-gate  *
26290Sstevel@tonic-gate  * Returns:
26300Sstevel@tonic-gate  *   >=0    - The number of bytes successfully read.
26310Sstevel@tonic-gate  *   <0	    - An error occurred.  This is (the number of bytes gotten + 1),
26320Sstevel@tonic-gate  *	      negated.  In other words, if 'n' bytes were read and then an
26330Sstevel@tonic-gate  *	      error occurred, this will return (-(n+1)).  So zero bytes read
26340Sstevel@tonic-gate  *	      and then an error occurs, this will return -1.  If 1 bytes
26350Sstevel@tonic-gate  *	      was read, it will return -2, etc.
26360Sstevel@tonic-gate  *
26370Sstevel@tonic-gate  *	      Specifics of the error can be gotten using http_get_lasterr();
26380Sstevel@tonic-gate  *
26390Sstevel@tonic-gate  *  Note that I/O errors are put into the error stack by http_srv_recv().1
26400Sstevel@tonic-gate  */
26410Sstevel@tonic-gate static int
getaline(http_conn_t * c_id,char * line,int len,boolean_t bread)2642*13093SRoger.Faulkner@Oracle.COM getaline(http_conn_t *c_id, char *line, int len, boolean_t bread)
26430Sstevel@tonic-gate {
26440Sstevel@tonic-gate 	int	i = 0;
26450Sstevel@tonic-gate 	ssize_t	msgcnt = 0;
26460Sstevel@tonic-gate 	ssize_t	cnt;
26470Sstevel@tonic-gate 
26480Sstevel@tonic-gate 	while (i < len) {
26490Sstevel@tonic-gate 		/*
26500Sstevel@tonic-gate 		 * Special processing required for message body reads.
26510Sstevel@tonic-gate 		 */
26520Sstevel@tonic-gate 		if (bread) {
26530Sstevel@tonic-gate 			/*
26540Sstevel@tonic-gate 			 * See if there is another chunk. Obviously, in the
26550Sstevel@tonic-gate 			 * case of non-chunked messages, there won't be.
26560Sstevel@tonic-gate 			 * But in either case, chunked or not, if msgcnt
26570Sstevel@tonic-gate 			 * is still zero after the call to get_msgcnt(),
26580Sstevel@tonic-gate 			 * then we're done.
26590Sstevel@tonic-gate 			 */
26600Sstevel@tonic-gate 			if (msgcnt == 0) {
26610Sstevel@tonic-gate 				if (get_msgcnt(c_id, &msgcnt) == -1) {
26620Sstevel@tonic-gate 					return (-(i+1));
26630Sstevel@tonic-gate 				}
26640Sstevel@tonic-gate 				if (msgcnt == 0) {
26650Sstevel@tonic-gate 					break;
26660Sstevel@tonic-gate 				}
26670Sstevel@tonic-gate 			}
26680Sstevel@tonic-gate 			cnt = MIN(msgcnt, sizeof (c_id->inbuf.buf));
26690Sstevel@tonic-gate 		} else {
26700Sstevel@tonic-gate 			cnt = sizeof (c_id->inbuf.buf);
26710Sstevel@tonic-gate 		}
26720Sstevel@tonic-gate 
26730Sstevel@tonic-gate 		/* read more data if buffer empty */
26740Sstevel@tonic-gate 		if (c_id->inbuf.i == c_id->inbuf.n) {
26750Sstevel@tonic-gate 			c_id->inbuf.i = 0;
26760Sstevel@tonic-gate 			c_id->inbuf.n = http_srv_recv(c_id, c_id->inbuf.buf,
26770Sstevel@tonic-gate 			    cnt);
26780Sstevel@tonic-gate 			if (c_id->inbuf.n == 0) {
26790Sstevel@tonic-gate 				return (i);
26800Sstevel@tonic-gate 			}
26810Sstevel@tonic-gate 			if (c_id->inbuf.n < 0) {
26820Sstevel@tonic-gate 				return (-(i+1));
26830Sstevel@tonic-gate 			}
26840Sstevel@tonic-gate 		}
26850Sstevel@tonic-gate 		/* skip CR */
26860Sstevel@tonic-gate 		if (c_id->inbuf.buf[c_id->inbuf.i] == '\r') {
26870Sstevel@tonic-gate 			INC_BREAD_CNT(bread, msgcnt);
26880Sstevel@tonic-gate 			c_id->inbuf.i++;
26890Sstevel@tonic-gate 			continue;
26900Sstevel@tonic-gate 		}
26910Sstevel@tonic-gate 		if (c_id->inbuf.buf[c_id->inbuf.i] == '\n') {
26920Sstevel@tonic-gate 			INC_BREAD_CNT(bread, msgcnt);
26930Sstevel@tonic-gate 			c_id->inbuf.i++;
26940Sstevel@tonic-gate 			line[i] = '\0';
26950Sstevel@tonic-gate 			return (i);
26960Sstevel@tonic-gate 		}
26970Sstevel@tonic-gate 		/* copy buf from internal buffer */
26980Sstevel@tonic-gate 		INC_BREAD_CNT(bread, msgcnt);
26990Sstevel@tonic-gate 		line[i++] = c_id->inbuf.buf[c_id->inbuf.i++];
27000Sstevel@tonic-gate 	}
27010Sstevel@tonic-gate 	return (i);
27020Sstevel@tonic-gate }
27030Sstevel@tonic-gate 
27040Sstevel@tonic-gate /*
27050Sstevel@tonic-gate  * getbytes - Get a block from the HTTP response. Used for the HTTP body.
27060Sstevel@tonic-gate  *
27070Sstevel@tonic-gate  * Arguments:
27080Sstevel@tonic-gate  *   c_id   - Structure describing the connection in question.
27090Sstevel@tonic-gate  *   line   - Where to store the data.
27100Sstevel@tonic-gate  *   len    - Maximum number of bytes in the block.
27110Sstevel@tonic-gate  *
27120Sstevel@tonic-gate  * Returns:
27130Sstevel@tonic-gate  *   >=0    - The number of bytes successfully read.
27140Sstevel@tonic-gate  *   <0	    - An error occurred.  This is (the number of bytes gotten + 1),
27150Sstevel@tonic-gate  *	      negated.  In other words, if 'n' bytes were read and then an
27160Sstevel@tonic-gate  *	      error occurred, this will return (-(n+1)).  So zero bytes read
27170Sstevel@tonic-gate  *	      and then an error occurs, this will return -1.  If 1 bytes
27180Sstevel@tonic-gate  *	      was read, it will return -2, etc.
27190Sstevel@tonic-gate  *
27200Sstevel@tonic-gate  *	      Specifics of the error can be gotten using http_get_lasterr();
27210Sstevel@tonic-gate  *
27220Sstevel@tonic-gate  *  Note that all reads performed here assume that a message body is being
27230Sstevel@tonic-gate  *  read. If this changes in the future, then the logic should more closely
2724*13093SRoger.Faulkner@Oracle.COM  *  resemble getaline().
27250Sstevel@tonic-gate  *
27260Sstevel@tonic-gate  *  Note that I/O errors are put into the error stack by http_srv_recv().
27270Sstevel@tonic-gate  */
27280Sstevel@tonic-gate static int
getbytes(http_conn_t * c_id,char * line,int len)27290Sstevel@tonic-gate getbytes(http_conn_t *c_id, char *line, int len)
27300Sstevel@tonic-gate {
27310Sstevel@tonic-gate 	int	i = 0;
27320Sstevel@tonic-gate 	ssize_t	msgcnt = 0;
27330Sstevel@tonic-gate 	ssize_t	cnt;
27340Sstevel@tonic-gate 	int	nbytes;
27350Sstevel@tonic-gate 
27360Sstevel@tonic-gate 	while (i < len) {
27370Sstevel@tonic-gate 		/*
27380Sstevel@tonic-gate 		 * See if there is another chunk. Obviously, in the
27390Sstevel@tonic-gate 		 * case of non-chunked messages, there won't be.
27400Sstevel@tonic-gate 		 * But in either case, chunked or not, if msgcnt
27410Sstevel@tonic-gate 		 * is still zero after the call to get_msgcnt(), then
27420Sstevel@tonic-gate 		 * we're done.
27430Sstevel@tonic-gate 		 */
27440Sstevel@tonic-gate 		if (msgcnt == 0) {
27450Sstevel@tonic-gate 			if (get_msgcnt(c_id, &msgcnt) == -1) {
27460Sstevel@tonic-gate 				return (-(i+1));
27470Sstevel@tonic-gate 			}
27480Sstevel@tonic-gate 			if (msgcnt == 0) {
27490Sstevel@tonic-gate 				break;
27500Sstevel@tonic-gate 			}
27510Sstevel@tonic-gate 		}
27520Sstevel@tonic-gate 
27530Sstevel@tonic-gate 		cnt = MIN(msgcnt, len - i);
27540Sstevel@tonic-gate 
27550Sstevel@tonic-gate 		if (c_id->inbuf.n != c_id->inbuf.i) {
27560Sstevel@tonic-gate 			nbytes = (int)MIN(cnt, c_id->inbuf.n - c_id->inbuf.i);
27570Sstevel@tonic-gate 			(void) memcpy(line, &c_id->inbuf.buf[c_id->inbuf.i],
2758*13093SRoger.Faulkner@Oracle.COM 			    nbytes);
27590Sstevel@tonic-gate 			c_id->inbuf.i += nbytes;
27600Sstevel@tonic-gate 		} else {
27610Sstevel@tonic-gate 			nbytes = http_srv_recv(c_id, line, cnt);
27620Sstevel@tonic-gate 			if (nbytes == 0) {
27630Sstevel@tonic-gate 				return (i);
27640Sstevel@tonic-gate 			}
27650Sstevel@tonic-gate 			if (nbytes < 0) {
27660Sstevel@tonic-gate 				return (-(i+1));
27670Sstevel@tonic-gate 			}
27680Sstevel@tonic-gate 		}
27690Sstevel@tonic-gate 
27700Sstevel@tonic-gate 		i += nbytes;
27710Sstevel@tonic-gate 		line += nbytes;
27720Sstevel@tonic-gate 		msgcnt -= nbytes;
27730Sstevel@tonic-gate 		c_id->body_read += nbytes;
27740Sstevel@tonic-gate 		c_id->body_read_tot += nbytes;
27750Sstevel@tonic-gate 	}
27760Sstevel@tonic-gate 
27770Sstevel@tonic-gate 	return (i);
27780Sstevel@tonic-gate }
27790Sstevel@tonic-gate 
27800Sstevel@tonic-gate static int
http_srv_send(http_conn_t * c_id,const void * buf,size_t nbyte)27810Sstevel@tonic-gate http_srv_send(http_conn_t *c_id, const void *buf, size_t nbyte)
27820Sstevel@tonic-gate {
27830Sstevel@tonic-gate 	int	retval;
27840Sstevel@tonic-gate 
27850Sstevel@tonic-gate 	if (c_id->ssl != NULL) {
27860Sstevel@tonic-gate 		if ((retval = SSL_write(c_id->ssl, buf, nbyte)) <= 0) {
27870Sstevel@tonic-gate 			handle_ssl_error(c_id, retval);
27880Sstevel@tonic-gate 		}
27890Sstevel@tonic-gate 		return (retval);
27900Sstevel@tonic-gate 	} else {
27910Sstevel@tonic-gate 		retval = socket_write(c_id->fd, buf, nbyte, &c_id->host_addr);
27920Sstevel@tonic-gate 		if (retval < 0) {
27930Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
27940Sstevel@tonic-gate 			return (-1);
27950Sstevel@tonic-gate 		}
27960Sstevel@tonic-gate 		return (retval);
27970Sstevel@tonic-gate 	}
27980Sstevel@tonic-gate }
27990Sstevel@tonic-gate 
28000Sstevel@tonic-gate static int
http_srv_recv(http_conn_t * c_id,void * buf,size_t nbyte)28010Sstevel@tonic-gate http_srv_recv(http_conn_t *c_id, void *buf, size_t nbyte)
28020Sstevel@tonic-gate {
28030Sstevel@tonic-gate 	int	retval;
28040Sstevel@tonic-gate 
28050Sstevel@tonic-gate 	if (c_id->ssl != NULL) {
28060Sstevel@tonic-gate 		if ((retval = SSL_read(c_id->ssl, buf, nbyte)) <= 0) {
28070Sstevel@tonic-gate 			handle_ssl_error(c_id, retval);
28080Sstevel@tonic-gate 		}
28090Sstevel@tonic-gate 		return (retval);
28100Sstevel@tonic-gate 	} else {
28110Sstevel@tonic-gate 		retval = socket_read(c_id->fd, buf, nbyte, c_id->read_timeout);
28120Sstevel@tonic-gate 		if (retval < 0) {
28130Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
28140Sstevel@tonic-gate 			return (-1);
28150Sstevel@tonic-gate 		}
28160Sstevel@tonic-gate 		return (retval);
28170Sstevel@tonic-gate 	}
28180Sstevel@tonic-gate }
28190Sstevel@tonic-gate 
28200Sstevel@tonic-gate static boolean_t
http_check_conn(http_conn_t * c_id)28210Sstevel@tonic-gate http_check_conn(http_conn_t *c_id)
28220Sstevel@tonic-gate {
28230Sstevel@tonic-gate 	early_err = 0;
28240Sstevel@tonic-gate 	if (c_id == NULL || c_id->signature != HTTP_CONN_INFO) {
28250Sstevel@tonic-gate 		early_err = EHTTP_BADARG;
28260Sstevel@tonic-gate 		return (B_FALSE);
28270Sstevel@tonic-gate 	}
28280Sstevel@tonic-gate 	RESET_ERR(c_id);
28290Sstevel@tonic-gate 	return (B_TRUE);
28300Sstevel@tonic-gate }
28310Sstevel@tonic-gate 
28320Sstevel@tonic-gate static void
handle_ssl_error(http_conn_t * c_id,int retval)28330Sstevel@tonic-gate handle_ssl_error(http_conn_t *c_id, int retval)
28340Sstevel@tonic-gate {
28350Sstevel@tonic-gate 	ulong_t err;
28360Sstevel@tonic-gate 
28370Sstevel@tonic-gate 	err = SSL_get_error(c_id->ssl, retval);
28380Sstevel@tonic-gate 
28390Sstevel@tonic-gate 	switch (err) {
28400Sstevel@tonic-gate 	case SSL_ERROR_NONE:
28410Sstevel@tonic-gate 		return;
28420Sstevel@tonic-gate 
28430Sstevel@tonic-gate 	case SSL_ERROR_ZERO_RETURN:
28440Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_CONCLOSED);
28450Sstevel@tonic-gate 		return;
28460Sstevel@tonic-gate 
28470Sstevel@tonic-gate 	case SSL_ERROR_WANT_READ:
28480Sstevel@tonic-gate 	case SSL_ERROR_WANT_WRITE:
28490Sstevel@tonic-gate 	case SSL_ERROR_WANT_CONNECT:
28500Sstevel@tonic-gate 	case SSL_ERROR_WANT_X509_LOOKUP:
28510Sstevel@tonic-gate 		SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_UNEXPECTED);
28520Sstevel@tonic-gate 		return;
28530Sstevel@tonic-gate 
28540Sstevel@tonic-gate 	case SSL_ERROR_SYSCALL:
28550Sstevel@tonic-gate 		err = ERR_get_error();
28560Sstevel@tonic-gate 		if (err == 0)
28570Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBHTTP, EHTTP_EOFERR);
28580Sstevel@tonic-gate 		else if (err == (ulong_t)-1)
28590Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_SYSTEM, errno);
28600Sstevel@tonic-gate 		else {
28610Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
28620Sstevel@tonic-gate 			while ((err = ERR_get_error()) != 0)
28630Sstevel@tonic-gate 				SET_ERR(c_id, ERRSRC_LIBSSL, err);
28640Sstevel@tonic-gate 		}
28650Sstevel@tonic-gate 		return;
28660Sstevel@tonic-gate 
28670Sstevel@tonic-gate 	case SSL_ERROR_SSL:
28680Sstevel@tonic-gate 		while ((err = ERR_get_error()) != 0) {
28690Sstevel@tonic-gate 			SET_ERR(c_id, ERRSRC_LIBSSL, err);
28700Sstevel@tonic-gate 		}
28710Sstevel@tonic-gate 		return;
28720Sstevel@tonic-gate 	}
28730Sstevel@tonic-gate }
28740Sstevel@tonic-gate 
28750Sstevel@tonic-gate static int
count_digits(int value)28760Sstevel@tonic-gate count_digits(int value)
28770Sstevel@tonic-gate {
28780Sstevel@tonic-gate 	int	count = 1;
28790Sstevel@tonic-gate 
28800Sstevel@tonic-gate 	if (value < 0) {
28810Sstevel@tonic-gate 		count++;
28820Sstevel@tonic-gate 		value = -value;
28830Sstevel@tonic-gate 	}
28840Sstevel@tonic-gate 
28850Sstevel@tonic-gate 	while (value > 9) {
28860Sstevel@tonic-gate 		value /= 10;
28870Sstevel@tonic-gate 		count++;
28880Sstevel@tonic-gate 	}
28890Sstevel@tonic-gate 	return (count);
28900Sstevel@tonic-gate }
28910Sstevel@tonic-gate 
28920Sstevel@tonic-gate static int
hexdigit(char ch)28930Sstevel@tonic-gate hexdigit(char ch)
28940Sstevel@tonic-gate {
28950Sstevel@tonic-gate 	if (ch >= '0' && ch <= '9')
28960Sstevel@tonic-gate 		return (ch - '0');
28970Sstevel@tonic-gate 	if (ch >= 'A' && ch <= 'F')
28980Sstevel@tonic-gate 		return (ch - 'A' + 10);
28990Sstevel@tonic-gate 	if (ch >= 'a' && ch <= 'f')
29000Sstevel@tonic-gate 		return (ch - 'a' + 10);
29010Sstevel@tonic-gate 	return (-1);
29020Sstevel@tonic-gate }
29030Sstevel@tonic-gate 
29040Sstevel@tonic-gate static char *
eat_ws(const char * buf)29050Sstevel@tonic-gate eat_ws(const char *buf)
29060Sstevel@tonic-gate {
29070Sstevel@tonic-gate 	char *ptr = (char *)buf;
29080Sstevel@tonic-gate 
29090Sstevel@tonic-gate 	while (isspace(*ptr))
29100Sstevel@tonic-gate 		ptr++;
29110Sstevel@tonic-gate 
29120Sstevel@tonic-gate 	return (ptr);
29130Sstevel@tonic-gate }
29140Sstevel@tonic-gate 
29150Sstevel@tonic-gate static boolean_t
startswith(const char ** strp,const char * starts)29160Sstevel@tonic-gate startswith(const char **strp, const char *starts)
29170Sstevel@tonic-gate {
29180Sstevel@tonic-gate 	int len = strlen(starts);
29190Sstevel@tonic-gate 
29200Sstevel@tonic-gate 	if (strncasecmp(*strp, starts, len) == 0) {
29210Sstevel@tonic-gate 		*strp += len;
29220Sstevel@tonic-gate 		return (B_TRUE);
29230Sstevel@tonic-gate 	}
29240Sstevel@tonic-gate 	return (B_FALSE);
29250Sstevel@tonic-gate }
2926