You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

512 lines
12 KiB

/*
* TLS support code for CUPS using Google BoringSSL.
*
* Copyright 2007-2016 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
* property of Apple Inc. and are protected by Federal copyright
* law. Distribution and use rights are outlined in the file "LICENSE.txt"
* which should have been included with this file. If this file is
* file is missing or damaged, see the license at "http://www.cups.org/".
*
* This file is subject to the Apple OS-Developed Software exception.
*/
/**** This file is included from tls.c ****/
/*
* Local globals...
*/
#include "cups-private.h"
#include "debug-internal.h"
#include "http.h"
#include "thread-private.h"
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <sys/stat.h>
static char *tls_keypath = NULL;
/* Server cert keychain path */
static int tls_options = -1,/* Options for TLS connections */
tls_min_version = _HTTP_TLS_1_0,
tls_max_version = _HTTP_TLS_MAX;
/*
* Local functions...
*/
static BIO_METHOD * _httpBIOMethods(void);
static int http_bio_write(BIO *h, const char *buf, int num);
static int http_bio_read(BIO *h, char *buf, int size);
static int http_bio_puts(BIO *h, const char *str);
static long http_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2);
static int http_bio_new(BIO *h);
static int http_bio_free(BIO *data);
static BIO_METHOD http_bio_methods =
{
BIO_TYPE_SSL,
"http",
http_bio_write,
http_bio_read,
http_bio_puts,
NULL, /* http_bio_gets, */
http_bio_ctrl,
http_bio_new,
http_bio_free,
NULL,
};
/*
* 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
*
* @since CUPS 2.0/OS 10.10@
*/
int /* O - 1 on success, 0 on failure */
cupsMakeServerCredentials(
const char *path, /* I - Path to keychain/directory */
const char *common_name, /* I - Common name */
int num_alt_names, /* I - Number of subject alternate names */
const char **alt_names, /* I - Subject Alternate Names */
time_t expiration_date) /* I - Expiration date */
{
int pid, /* Process ID of command */
status; /* Status of command */
char command[1024], /* Command */
*argv[12], /* Command-line arguments */
*envp[1000], /* Environment variables */
infofile[1024], /* Type-in information for cert */
seedfile[1024]; /* Random number seed file */
int envc, /* Number of environment variables */
bytes; /* Bytes written */
cups_file_t *fp; /* Seed/info file */
int infofd; /* Info file descriptor */
char temp[1024], /* Temporary directory name */
crtfile[1024], /* Certificate filename */
keyfile[1024]; /* Private key filename */
DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, alt_names, (int)expiration_date));
return 0;
}
/*
* '_httpCreateCredentials()' - Create credentials in the internal format.
*/
http_tls_credentials_t /* O - Internal credentials */
_httpCreateCredentials(
cups_array_t *credentials) /* I - Array of credentials */
{
(void)credentials;
return (NULL);
}
/*
* '_httpFreeCredentials()' - Free internal credentials.
*/
void
_httpFreeCredentials(
http_tls_credentials_t credentials) /* I - Internal credentials */
{
(void)credentials;
}
/*
* '_httpBIOMethods()' - Get the OpenSSL BIO methods for HTTP connections.
*/
static BIO_METHOD * /* O - BIO methods for OpenSSL */
_httpBIOMethods(void)
{
return (&http_bio_methods);
}
/*
* 'http_bio_ctrl()' - Control the HTTP connection.
*/
static long /* O - Result/data */
http_bio_ctrl(BIO *h, /* I - BIO data */
int cmd, /* I - Control command */
long arg1, /* I - First argument */
void *arg2) /* I - Second argument */
{
switch (cmd)
{
default :
return (0);
case BIO_CTRL_RESET :
h->ptr = NULL;
return (0);
case BIO_C_SET_FILE_PTR :
h->ptr = arg2;
h->init = 1;
return (1);
case BIO_C_GET_FILE_PTR :
if (arg2)
{
*((void **)arg2) = h->ptr;
return (1);
}
else
return (0);
case BIO_CTRL_DUP :
case BIO_CTRL_FLUSH :
return (1);
}
}
/*
* 'http_bio_free()' - Free OpenSSL data.
*/
static int /* O - 1 on success, 0 on failure */
http_bio_free(BIO *h) /* I - BIO data */
{
if (!h)
return (0);
if (h->shutdown)
{
h->init = 0;
h->flags = 0;
}
return (1);
}
/*
* 'http_bio_new()' - Initialize an OpenSSL BIO structure.
*/
static int /* O - 1 on success, 0 on failure */
http_bio_new(BIO *h) /* I - BIO data */
{
if (!h)
return (0);
h->init = 0;
h->num = 0;
h->ptr = NULL;
h->flags = 0;
return (1);
}
/*
* 'http_bio_puts()' - Send a string for OpenSSL.
*/
static int /* O - Bytes written */
http_bio_puts(BIO *h, /* I - BIO data */
const char *str) /* I - String to write */
{
return (send(((http_t *)h->ptr)->fd, str, strlen(str), 0));
}
/*
* 'http_bio_read()' - Read data for OpenSSL.
*/
static int /* O - Bytes read */
http_bio_read(BIO *h, /* I - BIO data */
char *buf, /* I - Buffer */
int size) /* I - Number of bytes to read */
{
http_t *http; /* HTTP connection */
http = (http_t *)h->ptr;
if (!http->blocking)
{
/*
* Make sure we have data before we read...
*/
while (!_httpWait(http, http->wait_value, 0))
{
if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
continue;
http->error = ETIMEDOUT;
return (-1);
}
}
return (recv(http->fd, buf, size, 0));
}
/*
* 'http_bio_write()' - Write data for OpenSSL.
*/
static int /* O - Bytes written */
http_bio_write(BIO *h, /* I - BIO data */
const char *buf, /* I - Buffer to write */
int num) /* I - Number of bytes to write */
{
return (send(((http_t *)h->ptr)->fd, buf, num, 0));
}
/*
* '_httpTLSInitialize()' - Initialize the TLS stack.
*/
void
_httpTLSInitialize(void)
{
SSL_library_init();
}
/*
* '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
*/
size_t /* O - Bytes available */
_httpTLSPending(http_t *http) /* I - HTTP connection */
{
return (SSL_pending(http->tls));
}
/*
* '_httpTLSRead()' - Read from a SSL/TLS connection.
*/
int /* O - Bytes read */
_httpTLSRead(http_t *http, /* I - Connection to server */
char *buf, /* I - Buffer to store data */
int len) /* I - Length of buffer */
{
return (SSL_read((SSL *)(http->tls), buf, len));
}
/*
* '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
*/
void
_httpTLSSetOptions(int options, int min_version, int max_version) /* I - Options */
{
tls_options = options;
tls_min_version = min_version;
tls_max_version = max_version;
}
/*
* '_httpTLSStart()' - Set up SSL/TLS support on a connection.
*/
int /* O - 0 on success, -1 on failure */
_httpTLSStart(http_t *http) /* I - Connection to server */
{
char hostname[256], /* Hostname */
*hostptr; /* Pointer into hostname */
SSL_CTX *context; /* Context for encryption */
BIO *bio; /* BIO data */
const char *message = NULL;/* Error message */
DEBUG_printf(("3_httpTLSStart(http=%p)", (void *)http));
if (tls_options < 0)
{
DEBUG_puts("4_httpTLSStart: Setting defaults.");
_cupsSetDefaults();
DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
}
if (http->mode == _HTTP_MODE_SERVER && !tls_keypath)
{
DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
http->error = errno = EINVAL;
http->status = HTTP_STATUS_ERROR;
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
return (-1);
}
context = SSL_CTX_new(TLS_method());
SSL_CTX_set_min_proto_version(context, tls_min_version);
SSL_CTX_set_max_proto_version(context, tls_max_version);
bio = BIO_new(_httpBIOMethods());
BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
http->tls = SSL_new(context);
SSL_set_bio(http->tls, bio, bio);
/* http->tls retains an internal reference to the SSL_CTX. */
SSL_CTX_free(context);
if (http->mode == _HTTP_MODE_CLIENT)
{
SSL_set_connect_state(http->tls);
/*
* Client: get the hostname to use for TLS...
*/
if (httpAddrLocalhost(http->hostaddr))
{
strlcpy(hostname, "localhost", sizeof(hostname));
}
else
{
/*
* Otherwise make sure the hostname we have does not end in a trailing dot.
*/
strlcpy(hostname, http->hostname, sizeof(hostname));
if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
*hostptr == '.')
*hostptr = '\0';
}
SSL_set_tlsext_host_name(http->tls, hostname);
}
else
{
/* @@@ TODO @@@ */
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, "Server not supported", 0);
}
if (SSL_do_handshake(http->tls) != 1)
{
unsigned long error; /* Error code */
char buf[256];
while ((error = ERR_get_error()) != 0)
{
ERR_error_string_n(error, buf, sizeof(buf));
DEBUG_printf(("8http_setup_ssl: %s", buf));
}
SSL_free(http->tls);
http->tls = NULL;
http->error = errno;
http->status = HTTP_STATUS_ERROR;
if (!message)
message = _("Unable to establish a secure connection to host.");
_cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, message, 1);
return (-1);
}
_cups_globals_t *cg = _cupsGlobals();
if (cg->server_cert_cb)
{
int error = 0;
X509 *peer_certificate = SSL_get_peer_certificate(http->tls);
if (peer_certificate)
{
ASN1_BIT_STRING *key = X509_get0_pubkey_bitstr(peer_certificate);
cups_array_t *credentials = cupsArrayNew(NULL, NULL);
if (credentials != NULL)
{
httpAddCredential(credentials, key->data, key->length);
error = cg->server_cert_cb(http, http->tls, credentials, cg->server_cert_data);
httpFreeCredentials(credentials);
}
X509_free(peer_certificate);
}
if (error != 0)
{
SSL_free(http->tls);
http->tls = NULL;
http->error = errno = EINVAL;
http->status = HTTP_STATUS_ERROR;
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Client rejected the server certificate."), 1);
}
return (error);
}
return (0);
}
/*
* '_httpTLSStop()' - Shut down SSL/TLS on a connection.
*/
void
_httpTLSStop(http_t *http) /* I - Connection to server */
{
unsigned long error; /* Error code */
switch (SSL_shutdown(http->tls))
{
case 1 :
break;
case -1 :
_cupsSetError(IPP_STATUS_ERROR_INTERNAL,
"Fatal error during SSL shutdown!", 0);
default :
while ((error = ERR_get_error()) != 0)
{
char buf[256];
ERR_error_string_n(error, buf, sizeof(buf));
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, buf, 0);
}
break;
}
SSL_free(http->tls);
http->tls = NULL;
}
/*
* '_httpTLSWrite()' - Write to a SSL/TLS connection.
*/
int /* O - Bytes written */
_httpTLSWrite(http_t *http, /* I - Connection to server */
const char *buf, /* I - Buffer holding data */
int len) /* I - Length of buffer */
{
int result; /* Return value */
DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
result = SSL_write((SSL *)(http->tls), buf, len);
DEBUG_printf(("3http_write_ssl: Returning %d.", result));
return result;
}