boot_http.c revision e00d72467ba84dfb2c5b9e5026b7844f34c9286f
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <netdb.h>
/* this must be included after ssl.h to avoid re-defining 'offsetof' */
#include <sys/sysmacros.h>
#include <boot_http.h>
#include <socket_inet.h>
#include <p12access.h>
#include "bootlog.h"
#define BOOT_HTTP_MAJOR_VERSION 1
#define BOOT_HTTP_MINOR_VERSION 0
#define BOOT_HTTP_MICRO_VERSION 0
static boot_http_ver_t boot_http_ver = {
};
static int early_err; /* Error from before error occurred */
typedef struct {
int i; /* current position in buffer */
int n; /* number of bytes in buffer */
} buf_struct_t;
typedef struct {
} errent_t;
typedef enum {
HTTP_REQ_TYPE_HEAD = 1,
} http_req_t;
typedef struct http_conn_t {
int fd; /* Connection's fd... */
void *ssl; /* Handle to ssl data structure */
int read_timeout; /* Timeout to use on read requests in sec */
char *basic_auth_userid; /* Basic authentication user ID */
char *basic_auth_password; /* and password */
char is_multipart; /* B_TRUE if doing multipart/mixed download */
char is_firstpart; /* B_TRUE if first part in a multipart xfer */
char is_firstchunk; /* B_TRUE if first chunk in chunked xfer */
char is_chunked; /* B_TRUE if message body is chunked */
char *random_file; /* File with seed info for pseudo random */
/* number generator */
char *client_cert_file; /* File holding client's certificate */
char *private_key_file; /* File with the private key */
char *file_password; /* file with password to key or pkcs12 file. */
char **resphdr; /* Array of header response lines */
char *boundary; /* Boundary text (multipart downloads only) */
/* (libssl can return multiple errors on one */
/* operation) */
} http_conn_t;
/*
* Convenient macros for accessing fields in connection structure.
*/
else \
/*
* Macro used to increment message body read counters
*/
#define INC_BREAD_CNT(bool, bcnt) \
if (bool) { \
bcnt--; \
c_id->body_read_tot++; \
}
static int ssl_init = 0; /* 1 when ssl has been initialized */
static char *ca_verify_file; /* List of trusted CA's */
static int p12_format = 0; /* Default to PEM format */
/* prototypes for local functions */
off64_t);
static int readline(http_conn_t *, int, char *, int);
static int proxy_connect(http_conn_t *);
static int check_cert_chain(http_conn_t *, char *);
static void print_ciphers(SSL *);
static void free_response(http_conn_t *, int);
static int free_ctx_ssl(http_conn_t *);
static int get_chunk_header(http_conn_t *);
static int init_bread(http_conn_t *);
static int getbytes(http_conn_t *, char *, int);
static void handle_ssl_error(http_conn_t *, int);
static int count_digits(int);
static int hexdigit(char);
static char *eat_ws(const char *);
/* ---------------------- public functions ----------------------- */
/*
* http_set_p12_format - Set flag indicating that certs & keys will be in
* pkcs12 format.
*
* Default is PEM certs. When this is called, the default can be changed to
* pcs12 format.
*/
void
{
p12_format = on_off;
}
/*
* http_get_version - Get current boot http support version
*
* pVer = http_get_version();
*
* Arguments:
* None.
*
* Returns:
* Pointer to struct with version information.
*
* Returns the version of the http support in the current library. This
* is a struct with unsigned integsrs for <major>, <minor> and
* <micro> version numbers. <major> changes when an incompatible change
* is made. <minor> changes when an upwardly-compatible API change is
* made. <micro> consists of bug fixes, etc.
*/
boot_http_ver_t const *
http_get_version(void)
{
return (&boot_http_ver);
}
/*
*
* http_set_verbose(on_off);
*
* Arguments:
* on_off - When TRUE, turn verbose mode one. When FALSE, turn
* verbose off.
*
* Returns:
* None.
*
* When enabled, information is logged to bootlog (or the Solaris equivalent).
*/
void
{
}
/*
* http_set_cipher_list - Change the list of ciphers that can be used.
*
* ret = http_set_cipher_list(handle, list);
*
* Arguments:
* list - List of ciphers that can be used.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*/
int
http_set_cipher_list(const char *list)
{
early_err = 0;
return (-1);
}
}
cipher_list = (char *)list;
return (0);
}
/*
* http_srv_init - Set up a structure for a connection.
*
* handle = http_srv_init(url);
*
* Arguments:
* url - the structure that contains the URI.
*
* Returns:
* != NULL - A handle for referring to this connection.
* == NULL - An error occurred. Get the exact error from
* http_get_lasterr().
*/
{
early_err = 0;
return (NULL);
}
return (NULL);
}
/* Do this at the end, just in case.... */
return (c_id);
}
/*
* http_conn_is_https - Determine whether the scheme is http or https.
*
* B_TRUE - Connection is an SSL connection.
* B_FALSE - Connection isn't SSL.
*
* ret = http_conn_is_https(handle, boolean_t *bool);
*
* Arguments:
* handle - Handle associated with the desired connection
* bool - Ptr to boolean in which to place result
*
* Returns:
* 0 - Success
* -1 - Some error occurred.
*/
int
{
if (!http_check_conn(c_id))
return (-1);
*bool = CONN_HTTPS;
return (0);
}
/*
*
* ret = http_set_proxy(handle, proxy);
*
* Arguments:
* handle - Handle associated with the desired connection
* proxy - The hostport definition for the proxy. If NULL,
* The next connect will not use a proxy.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*/
int
{
if (!http_check_conn(c_id))
return (-1);
} else {
CONN_PROXY_HOSTNAME[0] = '\0';
}
return (0);
}
/*
* http_set_keepalive - Set keepalive for this connection.
*
* http_set_keepalive(handle, on_off);
*
* Arguments:
* handle - Handle associated with the desired connection
* on_off - Boolean turning keepalive on (TRUE) or off (FALSE)
*
* Returns:
* 0 - Success.
* -1 - An error occurred. Check http_get_lasterr().
*
* This setting takes effect next time a connection is opened using this
* handle.
*/
int
{
if (!http_check_conn(c_id))
return (-1);
return (0);
}
/*
* http_set_socket_read_timeout - Set the timeout reads
*
* http_set_socket_read_timeout(handle, timeout);
*
* Arguments:
* handle - Handle associated with the desired connection
* timeout - Timeout, in seconds. Zero will default to 10 second
* timeouts.
*
* Returns:
* 0 - Success.
* -1 - An error occurred. Check http_get_lasterr().
*
* This setting takes effect beginning with the next read operation on this
* connection.
*/
int
{
if (!http_check_conn(c_id))
return (-1);
return (0);
}
/*
* http_set_basic_auth - Set the basic authorization user ID and password
*
* ret = http_set_basic_auth(handle, userid, password);
*
* Arguments:
* handle - Handle associated with the desired connection
* password- Password which goes with the user ID
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*
* This must be set before a https connection is made.
*/
int
const char *password)
{
if (!http_check_conn(c_id))
return (-1);
return (-1);
}
return (-1);
}
return (0);
}
/*
* http_set_random_file - See the pseudo random number generator with file data
*
* ret = http_set_random_file(handle, filename);
*
* Arguments:
* handle - Handle associated with the desired connection
* filename
* - filename (including path) with random number seed.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*
* This must be set before a https connection is made.
*/
int
{
if (!http_check_conn(c_id))
return (-1);
return (-1);
}
}
return (0);
}
/*
* http_set_certificate_authority_file - Set the CA file.
*
* ret = http_set_certificate_authority_file(filename);
*
* Arguments:
* filename- File with the certificate authority certs
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*
* This must be set before https connections to the servers is done.
*/
int
http_set_certificate_authority_file(const char *fname)
{
early_err = 0;
return (-1);
}
}
ca_verify_file = (char *)fname;
return (0);
}
/*
* http_set_client_certificate_file - Set the file containing the PKCS#12
* client certificate and optionally its certificate chain.
*
* ret = http_set_client_certificate_file(handle, filename);
*
* Arguments:
* handle - Handle associated with the desired connection
* filename- File (including path) containing certificate, etc.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*
* This must be set before the handle is used to make a https connection
* which will require a client certificate.
*/
int
{
if (!http_check_conn(c_id))
return (-1);
return (-1);
}
}
return (0);
}
/*
* http_set_password - Set the password for the private key or pkcs12 file.
*
* ret = http_set_password(handle, password);
*
* Arguments:
* handle - Handle associated with the desired connection
* password- Password for the client's private key file or pkcs12 file.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*
* This must be set before the handle is used to make a https connection.
*/
int
{
if (!http_check_conn(c_id))
return (-1);
return (-1);
}
}
return (0);
}
/*
* http_set_key_file_password - Set the password for the private key
* file.
*
* ret = http_set_key_file_password(handle, password);
*
* Arguments:
* handle - Handle associated with the desired connection
* password- Password for the client's private key file.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*
* This must be set before the handle is used to make a https connection.
*/
int
{
}
/*
* http_set_private_key_file - Set the file containing the PKCS#12
* private key for this client.
*
* ret = http_set_private_key_file(handle, filename);
*
* Arguments:
* handle - Handle associated with the desired connection
* filename- File (including path) containing the private key.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*
* This must be set before the handle is used to make a https connection.
*/
int
{
if (!http_check_conn(c_id))
return (-1);
return (-1);
}
}
return (0);
}
/*
* http_srv_connect - Establish a connection to the server
*
* ret = http_srv_connect(handle);
*
* Arguments:
* handle - Handle associated with the desired connection
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr() for specifics.
*/
int
{
int retval;
if (!http_check_conn(c_id))
return (-1);
if (CONN_HTTPS) {
/* Build our SSL context (this function sets any errors) */
"http_srv_connect: initialize_ctx returned NULL");
return (-1);
}
}
/* Connect the TCP socket */
} else {
}
"http_srv_connect: %s returned %d",
return (-1);
}
if (CONN_HTTPS) {
/* Connect the SSL socket */
while ((err = ERR_get_error()) != 0)
"http_srv_connect: SSL_new returned "
"NULL");
(void) free_ctx_ssl(c_id);
return (-1);
}
if (verbosemode)
/* Ensure automatic negotiations will do things right */
while ((err = ERR_get_error()) != 0)
"http_srv_connect: SSL_set_fd returned 0");
(void) free_ctx_ssl(c_id);
return (-1);
}
"http_srv_connect: SSL_connect");
(void) free_ctx_ssl(c_id);
return (-1);
}
(void) free_ctx_ssl(c_id);
return (-1);
}
if (verbosemode)
}
return (0);
}
/*
* http_head_request - Issue http HEAD request
*
* ret = http_head_request(handle, abs_path);
*
* Arguments:
* handle - Handle associated with the desired connection
* abs_path- File name portion of the URI, beginning with a /. Query,
* segment, etc are allowed.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*/
int
{
}
/*
* http_get_request - Issue http GET request without a range.
*
* ret = http_get_request(handle, abs_path);
*
* Arguments:
* handle - Handle associated with the desired connection
* abs_path- File name portion of the URI, beginning with a /. Query,
* segment, etc are allowed.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*/
int
{
}
/*
* http_get_range_request - Issue http GET request using a range.
*
* ret = http_get_range_request(handle, abs_path, curpos, len);
*
* Arguments:
* handle - Handle associated with the desired connection
* abs_path- File name portion of the URI, beginning with a /. Query,
* segment, etc are allowed.
* curpos - >=0 - Beginning of range
* len - = 0 - Range ends at the end of the file
* > 0 - Length of range.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*/
int
{
if (!http_check_conn(c_id))
return (-1);
if (curpos < 0) {
return (-1);
}
}
/*
* http_free_respinfo - Free a respinfo structure
*
* ret = http_free_respinfo(resp);
*
* Arguments:
* resp - respinfo structure presumably allocated by
* http_process_headers() or http_process_part_headers()
*
* Note that if resp is NULL, then this results in a NOOP.
*
*/
void
{
return;
}
}
}
/*
* http_process_headers - Read in the header lines from the response
*
* ret = http_process_headers(handle, resp);
*
* Arguments:
* handle - Handle associated with the connection where the request
* was made.
* resp - Summary information about the response.
*
* Returns:
* 0 - Success
* < 0 - An error occurred. Specifics of the error can
* be gotten using http_get_lasterr().
*
* Process the HTTP headers in the response. Check for a valid response
* status line. Allocate and return response information via the 'resp'
* argument. Header lines are stored locally, are are returned using calls
* to http_get_response_header() and http_get_header_value().
*
* Note that the errors will be set in the http_conn_t struct before the
* function which detected the error returns.
*
* Note that if resp is non-NULL, then upon a successful return, information
* about the status line, the code in the status line and the number of
* header lines are returned in the http_respinfo_t structure. The caller is
* responsible for freeing the resources allocated to this structure via
* http_free_respinfo().
*
* Note that the counters used to read message bodies are initialized here.
*
* Calling this function replaces the header information which is
* queried using http_get_response_header() and http_get_header_value().
* Once this function is called, headers read by the previous call
* to http_process_headers() or http_process_part_headers() is lost.
*/
int
{
char line[MAXHOSTNAMELEN];
char *ptr;
int i;
if (!http_check_conn(c_id))
return (-1);
return (-1);
}
}
/*
* check the response status line, expecting
* HTTP/1.1 200 OK
*/
if (i == 0) {
}
return (0);
}
if (i < 0) {
/*
* Cause of I/O error was already put into
* error stack. This is an additional error.
*/
return (-1);
}
if (verbosemode)
return (-1);
}
/* skip to the code */
ptr += 8;
ptr++;
/* make sure it's three digits */
i = 0;
i++;
if (i != 3) {
return (-1);
}
/* skip to the message */
ptr += 3;
ptr++;
/* save the message */
return (-1);
}
/*
* Error stack was already set at a lower level.
* 'statusmsg' will be cleaned up next time
* headers are read.
*/
return (-1);
}
/*
* headers. If so, get the boundary string.
*/
char *ptr2;
ptr2 ++;
ptr2 ++;
if (ptr2[0] == '"') {
ptr2 ++;
}
return (-1);
}
}
}
}
/*
* Initialize the counters used to process message bodies.
*/
if (init_bread(c_id) != 0) {
/*
* Error stack was already set at a lower level.
*/
return (-1);
}
/* Copy fields to the caller's structure */
return (-1);
}
}
return (0);
}
/*
* http_process_part_headers - Read in part boundary and header lines for the
* next part of a multipart message.
*
* ret = http_process_part_headers(handle, resp);
*
* Arguments:
* handle - Handle associated with the connection where the request
* was made.
* resp - Return address for summary information about the
* header block.
*
* Returns:
* = 1 - The end part was found.
* = 0 - Success, with header info returned in 'resp'
* = -1 - An error occurred. Specifics of the error can
* be gotten using http_get_lasterr().
*
* This function reads any \r\n sequences (empty lines) and expects to get
* a boundary line as the next non-empty line. It then reads header lines
* (content-length, etc) until it gets another empty lines, which ends the
* header section.
*
* Note that if resp is non-NULL, then upon a successful return, information
* about the the number of header lines is returned in the http_respinfo_t
* structure. The caller is responsible for freeing the resources allocated
* to this structure via http_free_respinfo().
*
* Headers values can be returned using http_get_response_header() and
* http_get_header_value().
*
* Calling this function replaces the header information which is
* queried using http_get_response_header() and http_get_header_value().
* Once this function is called, information returned by the previous call
* to http_process_headers() or http_process_part_headers() is gone.
*/
int
{
char line[MAXHOSTNAMELEN];
int count;
int limit;
int i;
if (!http_check_conn(c_id))
return (-1);
if (c_id->is_multipart == 0) {
return (-1);
}
/*
* Figure out how many empty lines to allow. Before the first
* boundary of the transmission, there can be any number of
* empty lines (from 0 up). Limit these to some reasonable
* failsafe.
*
* For the 2nd and later boundaries, there is supposed to be
* one crlf pair. However, many implementations don't require
* it. So don't require it.
*/
if (c_id->is_firstpart) {
} else
limit = 1;
/* Look for the boundary line. */
count = 0;
count ++;
/*
* If I/O error, cause was already put into
* error stack. This is an additional error.
*/
return (-1);
}
if (verbosemode)
"http_process_part_headers: %s", line);
/* Look for boundary line - '--<boundary text> */
/* No boundary line.... */
return (-1);
}
/* Is this the end-of-parts boundary (ends with a trailing '--') */
return (1);
}
/* Error stack was already set at a lower level. */
return (-1);
}
/* Copy fields to the caller's structure */
return (-1);
}
}
return (0);
}
/*
* http_get_response_header - Get a line from the response header
*
* ret = http_get_response_header(handle, whichline);
*
* Arguments:
* handle - Handle associated with the desired connection
* whichline - Which line of the header to return. This must be between
* zero and resp.nresphdrs which was returned by the call to
* http_process_headers().
*
* Returns:
* ptr - Points to a copy of the header line.
* NULL - An error occurred. Check http_get_lasterr().
*/
char *
{
char *res;
if (!http_check_conn(c_id))
return (NULL);
return (NULL);
}
return (NULL);
}
return (res);
}
/*
* http_get_header_value - Get the value of a header line.
*
* ret = http_get_header_value(handle, what);
*
* Arguments:
* handle - Handle associated with the desired connection
* what - The field name to look up.
*
* Returns:
* ptr - Points to a copy of the header value.
* NULL - An error occurred. Check http_get_lasterr().
*/
char *
{
char *ptr;
char *res;
int i;
int n;
if (!http_check_conn(c_id))
return (NULL);
return (NULL);
}
n = strlen(field_name);
ptr += n + 1;
ptr++;
return (NULL);
}
return (res);
}
}
return (NULL);
}
/*
* http_read_body - Read the HTTP response body.
*
* ret = http_read_body(handle, recv_buf_ptr, recv_buf_size);
*
* Arguments:
* handle - Handle associated with the relevant connection
* recv_buf_ptr - Points to buffer to receive buffer
* recv_buf_size - Length in bytes of buffer.
*
* Returns:
* n - Number of bytes read..
* < 0 - An error occurred. This is (the number of bytes gotten + 1),
* negated. In other words, if 'n' bytes were read and then an
* error occurred, this will return (-(n+1)). So zero bytes
* were read and then an error occurs, this will return -1. If
* 1 byte was read, it will return -2, etc. Specifics of the
* error can be gotten using http_get_lasterr().
*
* Note that the errors will be set in the http_conn_t struct before the
* function which detected the error returns.
*/
int
{
if (!http_check_conn(c_id))
return (-1);
return (-1);
}
}
/*
* http_srv_disconnect - Get rid of the connection to the server without
* freeing the http_conn_t structure.
*
* ret = http_srv_disconnect(handle);
*
* Arguments:
* handle - Handle associated with the connection
*
* Returns:
* 0 - Success
* -1 - An error occurred. Specifics of the error can
* be gotten using http_get_lasterr().
*/
int
{
int err_ret;
if (!http_check_conn(c_id))
return (-1);
return (err_ret);
}
/*
* http_srv_close - Close the connection and clean up the http_conn_t
* structure.
*
* http_srv_close(handle);
*
* Arguments:
* handle - Handle associated with the desired connection
*
* Returns:
* 0 - Success
* -1 - An error occurred. Specifics of the error can
* be gotten using http_get_lasterr().
*/
int
{
int err_ret = 0;
if (!http_check_conn(c_id))
return (-1);
return (err_ret);
}
/*
* http_get_conn_info - Return current information about the connection
*
* err = http_get_conn_info(handle);
*
* Arguments:
* handle - Handle associated with the connection in question
*
* Returns:
* non_NULL- Points to structure
* NULL - An error exists. Check http_get_lasterr().
*/
{
if (!http_check_conn(c_id))
return (NULL);
return (NULL);
}
return (info);
}
/*
* http_get_lasterr - Return the next error on the last operation
*
* err = http_get_lasterr(handle, errsrc);
*
* Arguments:
* handle - Handle associated with the connection in question
* If no valid handle exists yet, this can be NULL.
* However, it must be checked with the very next call.
* errsrc - Returns the Sources of errors (ERRSRC_* values).
*
* Returns:
* 0 - No error exists
* <> 0 - The error.
*/
{
if (errsrc)
*errsrc = ERRSRC_LIBHTTP;
early_err = 0;
return (err);
}
if (errsrc)
*errsrc = ERRSRC_LIBHTTP;
early_err = 0;
return (err);
}
if (errsrc)
return (err);
}
/*
* http_decode_err - Decode a libssl error
*
* err = http_decode_err(err, errlib, errfunc, errcode);
*
* Arguments:
* errfunc - returns function in that library
* errcode - returns error code
*
* Returns:
* None other than the above.
*/
void
{
if (errlib)
if (errfunc)
if (errcode)
}
/* ---------------------- private functions ----------------------- */
/*
* http_req - Issue http request (either HEAD or GET)
*
* ret = http_req(handle, abs_path, reqtype, curpos, len);
*
* Arguments:
* handle - Handle associated with the desired connection
* abs_path- File name portion of the URI, beginning with a /. Query,
* segment, etc are allowed.
* type - HTTP_REQ_TYPE_HEAD or HTTP_REQ_TYPE_GET
*
* In the case of GET requests,
* curpos- -1 - Range not used
* >=0 - Beginning of range
* len - 0 - Range ends at the end of the file
* >0 - Length of range.
*
* Returns:
* 0 - Success
* -1 - An error occurred. Check http_get_lasterr().
*/
static int
{
char *request;
char *reqtypename;
char *newreq;
int requestlen;
int retval;
int j;
if (!http_check_conn(c_id))
return (-1);
return (-1);
}
/* Determine the name for the request type */
switch (type) {
case HTTP_REQ_TYPE_GET:
reqtypename = "GET";
return (-1);
}
break;
case HTTP_REQ_TYPE_HEAD:
reqtypename = "HEAD";
break;
default:
return (-1);
}
/* Do rudimentary checks on the absolute path */
return (-1);
}
/*
* Size the request.
*
* With proxy:
* reqtypename + " http://" + host + ":" + port + path +
* " HTTP/1.1\r\n" +
* Without proxy:
* reqtypename + " " + path + " HTTP/1.1\r\n" +
*/
/*
* Plus the rest:
* "Host: " + targethost + ":" + count_digits(port) + "\r\n" +
* "Connection: Keep-Alive\r\n" plus trailing "\r\n\0"
*/
return (-1);
}
/* The request line */
"%s http://%s:%d%s HTTP/1.1\r\n",
} else {
}
/* Ancillary headers */
"Connection: close\r\n");
else
"Connection: Keep-Alive\r\n");
/*
* We only send the range header on GET requests
*
* "Range: bytes=" + from + "-" + end + "\r\n" or
* "Range: bytes=" + from + "-" "\r\n"
*/
if (len > 0) {
}
return (-1);
}
if (len > 0)
}
/*
* Authorization is added only if provided (RFC 2617, Section 2)
*
* "Authorization: Basic " + authencstr + "\r\n"
*/
char *authstr;
char *authencstr;
int authlen;
/*
* Allow for concat(basic_auth_userid ":" basic_auth_password)
*/
return (-1);
}
/* 3 bytes encoded as 4 (round up) with null termination */
return (-1);
}
(void) EVP_EncodeBlock((unsigned char *)authencstr,
/*
* Finally do concat(Authorization: Basic " authencstr "\r\n")
*/
return (-1);
}
"Authorization: Basic %s\r\n", authencstr);
}
if (verbosemode)
/* send the HTTP request */
if (retval != j) {
/* Assume error in was set by send request. */
return (-1);
}
return (0);
}
/*
* password_cb - Callback to get private key password and return it
* to SSL. (Used for PEM certificates only.)
*
* len = passwd_cb(buf, buflen, rwflag, userdata);
*
* Arguments:
* buf - Buffer for the password
* buflen - Length of 'buf'
* rwflag - password will be used for reading/decryption (== 0)
* or writing/encryption (== 1).
* userdata - Points to connection-specific information.
*
* Returns:
* > 0 - Length of password that was put into 'buf'.
* 0 - No password was returned (usually error occurred)
*
* NOTE: The password code is not thread safe
*/
/* ARGSUSED */
static int
{
return (0);
return (0);
}
/*
* initialize_ctx - Initialize the context for a connection.
*
* ctx = initialize_ctx(c_id);
*
* Arguments:
* None.
*
* Returns:
* non-NULL - Points to ctx structure.
* NULL - An error occurred. Any cleanup is done and error
* information is in the error stack.
*/
static SSL_CTX *
{
/* Global system initialization */
if (ssl_init == 0) {
ssl_init = 1;
}
/* Create our context */
meth = SSLv3_client_method();
while ((err = ERR_get_error()) != 0)
"initialize_ctx: SSL_CTX_new returned NULL");
return (NULL);
}
/*
* Ensure that any renegotiations for blocking connections will
* be done automatically. (The alternative is to return partial
* reads to the caller and let it oversee the renegotiations.)
*/
while ((err = ERR_get_error()) != 0)
"initialize_ctx: SSL_CTX_set_mode returned 0");
(void) SSL_CTX_free(ctx);
return (NULL);
}
/* set cipher list if provided */
if (cipher_list != NULL) {
while ((err = ERR_get_error()) != 0)
"initialize_ctx: Error in cipher list");
return (NULL);
}
}
/*
* We attempt to use the client_certificate_file for the private
* key input scheme *only* in the absence of private_key_file. In
* this instance the scheme will be the same as that used for the
* certificate input.
*/
/* Load our certificates */
if (p12_format) {
/* Load pkcs12-formated files */
<= 0) {
while ((err = ERR_get_error()) != 0)
"initialize_ctx: Couldn't read "
"PKCS12 certificate file");
return (NULL);
}
} else {
/* Load PEM-formated files */
while ((err = ERR_get_error()) != 0)
"initialize_ctx: Couldn't read "
"PEM certificate file");
return (NULL);
}
}
}
/* Load our keys */
if (p12_format) {
/* Load pkcs12-formated files */
<= 0) {
while ((err = ERR_get_error()) != 0)
"initialize_ctx: Couldn't read "
"PKCS12 key file");
return (NULL);
}
}
} else {
/* Load PEM-formated files */
while ((err = ERR_get_error()) != 0)
"initialize_ctx: Couldn't read "
"PEM key file");
return (NULL);
}
}
}
/* Load the CAs we trust */
if (ca_verify_file != NULL) {
if (p12_format) {
<= 0) {
while ((err = ERR_get_error()) != 0)
"initialize_ctx: Couldn't read "
"PKCS12 CA list file");
return (NULL);
}
} else {
NULL) == 0) {
while ((err = ERR_get_error()) != 0)
"initialize_ctx: Couldn't read PEM"
" CA list file");
return (NULL);
}
}
}
/* Load randomness */
while ((err = ERR_get_error()) != 0)
"initialize_ctx: Couldn't load random file");
return (NULL);
}
if (RAND_status() <= 0) {
while ((err = ERR_get_error()) != 0)
"initialize_ctx: PRNG not seeded");
return (NULL);
}
return (ctx);
}
/*
* tcp_connect - Set up a TCP connection.
*
* sock = tcp_connect(c_id, hostname, port);
*
* Arguments:
* c_id - Structure associated with the desired connection
* hostname - the host to connect to
* port - the port to connect to
*
* Returns:
* >= 0 - Socket number.
* -1 - Error occurred. Error information is set in the
* error stack. Any cleanup is done.
*
* This function established a connection to the target host. When
* it returns, the connection is ready for a HEAD or GET request.
*/
static int
{
struct sockaddr_in addr;
int sock;
int status;
return (-1);
}
/* LINTED */
return (-1);
}
if (status < 0) {
(void) socket_close(sock);
return (-1);
}
return (sock);
}
/*
* readline - Get a line from the socket. Discard the end-of-line
*
* ret = readline(c_id, sock, buf, len);
*
* Arguments:
* c_id - Structure associated with the desired connection
* sock - Socket to read
* buf - Buffer for the line
* len - Length of the buffer
*
* Returns:
* 0 - Success. 'buf' contains the line.
* -1 - Error occurred. Error information is set in the
* error stack.
*/
static int
{
int n, r;
for (n = 0; n < len; n++) {
if (r < 0) {
return (-1);
} else if (r == 0) {
return (0);
}
if (*ptr == '\n') {
*ptr = '\0';
/* Strip off the CR if it's there */
n--;
}
return (n);
}
ptr++;
}
return (0);
}
/*
* proxy_connect - Set up a proxied TCP connection to the target host.
*
* sock = proxy_connect(c_id);
*
* Arguments:
* c_id - Structure associated with the desired connection
*
* Returns:
* >= 0 - Socket number.
* -1 - Error occurred. Error information is set in the
* error stack. Any cleanup is done.
*
* This function established a connection to the proxy and then sends
* the request to connect to the target host. It reads the response
* (the status line and any headers). When it returns, the connection
* is ready for a HEAD or GET request.
*/
static int
{
struct sockaddr_in addr;
int sock;
char buf[1024];
char *ptr;
int i;
CONN_PROXY_PORT)) < 0) {
return (-1);
}
if (!CONN_HTTPS) {
return (sock);
}
/* Now that we're connected, do the proxy request */
/* socket_write sets the errors */
(void) socket_close(sock);
return (-1);
}
/* And read the response */
if (i <= 0) {
if (i == 0)
"proxy_connect: Empty response from proxy");
(void) socket_close(sock);
return (-1);
}
"proxy_connect: Unrecognized protocol");
(void) socket_close(sock);
return (-1);
}
/* skip to the code */
ptr += 4;
ptr++;
ptr++;
/* make sure it's three digits */
"proxy_connect: Received error from proxy server");
(void) socket_close(sock);
return (-1);
}
ptr += 3;
(void) socket_close(sock);
return (-1);
}
/* Look for the blank line that signals end of proxy header */
;
if (i < 0) {
(void) socket_close(sock);
return (-1);
}
return (sock);
}
/*
* check_cert_chain - Check if we have a valid certificate chain.
*
* ret = check_cert_chain(c_id, host);
*
* Arguments:
* c_id - Connection info.
* host - Name to compare with the common name in the certificate.
*
* Returns:
* 0 - Certificate chain and common name are both OK.
*/
static int
{
char peer_CN[256];
long verify_err;
"check_cert_chain: Certificate doesn't verify");
return (-1);
}
/*
* Check the cert chain. The chain length
* is automatically checked by OpenSSL when we
* set the verify depth in the ctx
*
* All we need to do here is check that the CN
* matches
*/
/* Check the common name */
"check_cert_chain: Peer did not present a certificate");
return (-1);
}
if (verbosemode)
"check_cert_chain: Common name doesn't match host name");
return (-1);
}
return (0);
}
/*
* print_ciphers - Print the list of ciphers for debugging.
*
* print_ciphers(ssl);
*
* Arguments:
* ssl - SSL connection.
*
* Returns:
* none
*/
static void
{
SSL_CIPHER *c;
int i;
const char *name;
return;
return;
for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
/* LINTED */
c = sk_SSL_CIPHER_value(sk, i);
}
name = "";
}
/*
* read_headerlines - Get the header lines from the server. This reads
* lines until it gets a empty line indicating end of headers.
*
* ret = read_headerlines(c_id);
*
* Arguments:
* c_id - Info about the connection being read.
* bread - TRUE if the headerlines are part of the message body.
*
* Returns:
* 0 - Header lines were read.
* -1 - Error occurred. The errors information is already in
* the error stack.
*
* Read the lines. If the current line begins with a space or tab, it is
* a continuation. Take the new line and append it to the end of the
* previous line rather than making an entry for another line in
* c_id->resphdr.
*
* Note that I/O errors are put into the error stack by http_srv_recv(),
* which is called by getline().
*/
static int
{
char line[MAXHOSTNAMELEN];
char **new_buf;
char *ptr;
int next;
int cur;
int n;
/* process headers, stop when we get to an empty line */
cur = 0;
next = 0;
if (verbosemode)
"read_headerlines: %s", line);
/*
* See if this is a continuation line (first col is a
* space or a tab)
*/
next ++;
new_buf =
return (-1);
}
return (-1);
}
} else {
ptr ++;
return (-1);
}
}
ptr --;
}
/* Cause of any I/O error was already put into error stack. */
return (n >= 0 ? 0 : -1);
}
static void
{
int i;
/* free memory from previous calls */
}
}
}
}
}
static int
{
int err_ret = 0;
while ((err = ERR_get_error()) != 0)
err_ret = -1;
}
}
err_ret = -1;
}
}
return (err_ret);
}
/*
* get_chunk_header - Get a chunk header line
*
* Arguments:
* c_id - Structure describing the connection in question.
*
* Returns:
* >=0 - Length of next chunk
* -1 - Error occurred. The error information is in the error stack.
*/
static int
{
char line[MAXHOSTNAMELEN];
char *ptr;
int value;
int ok;
int i;
/*
* Determine whether an extra crlf pair will precede the
* chunk header. For the first one, there is no preceding
* crlf. For later chunks, there is one crlf.
*/
if (c_id->is_firstchunk) {
ok = 1;
} else {
}
if (ok)
if (!ok || i < 0) {
/*
* If I/O error, the Cause was already put into
* error stack. This is an additional error.
*/
return (-1);
}
if (verbosemode)
/*
* The first (and probably only) field in the line is the hex
* length of the chunk.
*/
value = 0;
ptr ++;
}
return (value);
}
/*
* init_bread - Initialize the counters used to read message bodies.
*
* Arguments:
* c_id - Structure describing the connection in question.
*
* Returns:
* 0 - Success
* -1 - Error occurred. The error information is in the error stack.
*
* This routine will determine whether the message body being received is
* chunked or non-chunked. Once determined, the counters used to read
* message bodies will be initialized.
*/
static int
{
char *hdr;
char *ptr;
/*
* Assume non-chunked reads until proven otherwise.
*/
return (-1);
}
}
/*
* If size was not determined above, then see if this is a
* chunked message. Keep in mind that the first chunk size is
* "special".
*/
if (!sized) {
}
if (c_id->is_chunked) {
/*
* Error stack was already set at a
* lower level.
*/
return (-1);
}
}
}
}
/*
* Well, isn't this a fine predicament? It wasn't chunked or
* non-chunked as far as we can tell.
*/
if (!sized) {
return (-1);
}
c_id->body_read_tot = 0;
return (0);
}
/*
* get_msgcnt - Get the number of bytes left in the message body or chunk.
*
* Arguments:
* c_id - Structure describing the connection in question.
* msgcnt - Where to store the message count.
*
* Returns:
* 0 - Success
* -1 - Error occurred. The error information is in the error stack.
*
* Note that if the message being read is not chunked, then the byte count
* is simply the message size minus the bytes read thus far. In the case of
* chunked messages, the byte count returned will be the number of bytes
* left in the chunk. If the current chunk has been exhausted, then this
* routine will determine the size of the next chunk. When the next chunk
* size is zero, the message has been read in its entirety.
*/
static int
{
/*
* If there are more bytes in the message, then return.
*/
if (*msgcnt != 0) {
return (0);
}
/*
* If this is not a chunked message and the body has been
* read, then we're done.
*/
if (!c_id->is_chunked) {
return (0);
}
/*
* We're looking at a chunked message whose immediate
* chunk has been totally processed. See if there is
* another chunk.
*/
/*
* Error stack was already set at a
* lower level.
*/
return (-1);
}
/*
* No bytes of this chunk have been processed yet.
*/
/*
* A zero length chunk signals the end of the
* message body and chunking.
*/
return (0);
}
/*
* There is another chunk.
*/
return (0);
}
/*
* getline - Get lines of data from the HTTP response, up to 'len' bytes.
* NOTE: the line will not end with a NULL if all 'len' bytes
* were read.
*
* Arguments:
* c_id - Structure describing the connection in question.
* line - Where to store the data.
* len - Maximum number of bytes in the line.
* bread - TRUE if the lines are part of the message body.
*
* Returns:
* >=0 - The number of bytes successfully read.
* <0 - An error occurred. This is (the number of bytes gotten + 1),
* negated. In other words, if 'n' bytes were read and then an
* error occurred, this will return (-(n+1)). So zero bytes read
* and then an error occurs, this will return -1. If 1 bytes
* was read, it will return -2, etc.
*
* Specifics of the error can be gotten using http_get_lasterr();
*
* Note that I/O errors are put into the error stack by http_srv_recv().1
*/
static int
{
int i = 0;
while (i < len) {
/*
* Special processing required for message body reads.
*/
if (bread) {
/*
* See if there is another chunk. Obviously, in the
* case of non-chunked messages, there won't be.
* But in either case, chunked or not, if msgcnt
* is still zero after the call to get_msgcnt(),
* then we're done.
*/
if (msgcnt == 0) {
return (-(i+1));
}
if (msgcnt == 0) {
break;
}
}
} else {
}
/* read more data if buffer empty */
cnt);
return (i);
}
return (-(i+1));
}
}
/* skip CR */
continue;
}
line[i] = '\0';
return (i);
}
/* copy buf from internal buffer */
}
return (i);
}
/*
* getbytes - Get a block from the HTTP response. Used for the HTTP body.
*
* Arguments:
* c_id - Structure describing the connection in question.
* line - Where to store the data.
* len - Maximum number of bytes in the block.
*
* Returns:
* >=0 - The number of bytes successfully read.
* <0 - An error occurred. This is (the number of bytes gotten + 1),
* negated. In other words, if 'n' bytes were read and then an
* error occurred, this will return (-(n+1)). So zero bytes read
* and then an error occurs, this will return -1. If 1 bytes
* was read, it will return -2, etc.
*
* Specifics of the error can be gotten using http_get_lasterr();
*
* Note that all reads performed here assume that a message body is being
* read. If this changes in the future, then the logic should more closely
* resemble getline().
*
* Note that I/O errors are put into the error stack by http_srv_recv().
*/
static int
{
int i = 0;
int nbytes;
while (i < len) {
/*
* See if there is another chunk. Obviously, in the
* case of non-chunked messages, there won't be.
* But in either case, chunked or not, if msgcnt
* is still zero after the call to get_msgcnt(), then
* we're done.
*/
if (msgcnt == 0) {
return (-(i+1));
}
if (msgcnt == 0) {
break;
}
}
nbytes);
} else {
if (nbytes == 0) {
return (i);
}
if (nbytes < 0) {
return (-(i+1));
}
}
i += nbytes;
}
return (i);
}
static int
{
int retval;
}
return (retval);
} else {
if (retval < 0) {
return (-1);
}
return (retval);
}
}
static int
{
int retval;
}
return (retval);
} else {
if (retval < 0) {
return (-1);
}
return (retval);
}
}
static boolean_t
{
early_err = 0;
return (B_FALSE);
}
return (B_TRUE);
}
static void
{
switch (err) {
case SSL_ERROR_NONE:
return;
case SSL_ERROR_ZERO_RETURN:
return;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_CONNECT:
return;
case SSL_ERROR_SYSCALL:
err = ERR_get_error();
if (err == 0)
else {
while ((err = ERR_get_error()) != 0)
}
return;
case SSL_ERROR_SSL:
while ((err = ERR_get_error()) != 0) {
}
return;
}
}
static int
count_digits(int value)
{
int count = 1;
if (value < 0) {
count++;
}
while (value > 9) {
value /= 10;
count++;
}
return (count);
}
static int
{
return (ch - '0');
return (-1);
}
static char *
{
ptr++;
return (ptr);
}
static boolean_t
{
return (B_TRUE);
}
return (B_FALSE);
}