"Hey, we should really create a better API."
But I just want to write an application that has TLS support!
Presuming that we've already done the work to resolve DNS and establish a connected socket:
if ((ssl_ctx = SSL_CTX_new(SSLv23_method())) == NULL)
errx(1, "failed to create ssl context");
if ((ssl = SSL_new(ssl_ctx)) == NULL)
errx(1, "failed to create ssl");
if (SSL_set_fd(ssl, s) != 1)
errx(1, "failed to set fd");
if (SSL_connect(ssl) != 1)
errx(1, "failed to connect");
if (SSL_write(ssl, buf, len) != len)
errx(1, "SSL_write failed");
if ((ret = SSL_read(ssl, buf, sizeof(buf))) <= 0)
errx(1, "SSL_read failed");
if (SSL_shutdown(ssl) != 1)
errx(1, "SSL_shutdown failed");
SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL)
SSL_VERIFY_NONE
(insecure by default)X509_check_host()
function (since January 2015).For full details read the man page, but in summary:
tls_init()
tls_config_new()
tls_config_set*()
tls_client() or tls_server()
tls_configure()
tls_connect*() or tls_accept*()
tls_handshake() ]
tls_read()/tls_write()
tls_close()
if ((ctx = tls_client()) == NULL)
err(1, "failed to create client");
if (tls_configure(ctx, NULL) == -1)
err(1, "failed to configure: %s", tls_error(ctx));
if (tls_connect(ctx, "www.linux.conf.au", "https") == -1)
err(1, "failed to connect: %s", tls_error(ctx));
if (tls_write(ctx, buf, len) != len)
errx(1, "failed to write: %s", tls_error(ctx));
if ((ret = tls_read(ctx, buf, sizeof(buf))) <= 0)
errx(1, "failed to read: %s", tls_error(ctx));
if (tls_close(ctx) == -1)
errx(1, "failed to close: %s", tls_error(ctx));
-1
or NULL
indicates failure).tls_read(3)
/tls_write(3)
almost have read(2)
/write(2)
semantics.After you've converted and packed your ALPN protocols into wire format (length prefixed strings)...
static int
tls_server_alpn_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen, void *arg)
{
struct tls *ctx = arg;
if (SSL_select_next_proto((unsigned char**)out, outlen,
ctx->config->alpn, ctx->config->alpn_len, in, inlen) ==
OPENSSL_NPN_NEGOTIATED)
return (SSL_TLSEXT_ERR_OK);
return (SSL_TLSEXT_ERR_NOACK);
}
...
SSL_CTX_set_alpn_select_cb(*ssl_ctx, tls_server_alpn_cb, ctx);
if (tls_config_set_alpn(server_cfg, "h2,http/1.1") == -1)
err(...);
For each additional certificate:
SSL_CTX
and configure it.Register a callback that:
SSL_CTX
) to use.SSL_CTX
for the connection.For each additional certificate:
if (tls_config_add_keypair_file(server_cfg, cert_path, key_path) == -1)
err(...);
If one of the following functions returns <= 0, call SSL_get_error()
.
SSL_connect()
SSL_accept()
SSL_read()
SSL_write()
SSL_shutdown()
ret = SSL_connect(ssl);
if (ret <= 0) {
ssl_err = SSL_get_error(ssl, ret);
switch (ssl_err) {
case SSL_ERROR_NONE:
case SSL_ERROR_ZERO_RETURN:
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_CONNECT:
case SSL_ERROR_WANT_ACCEPT:
continue;
case SSL_ERROR_WANT_X509_LOOKUP:
errx(1, "want x509 lookup");
case SSL_ERROR_SSL:
errstr = "unknown";
if ((ssl_err_code = ERR_peek_error()) != 0)
errstr = ERR_error_string(ssl_err_code, NULL);
errx(1, "SSL error: %s", errstr);
case SSL_ERROR_SYSCALL:
errstr = "unknown";
if ((ssl_err_code = ERR_peek_error()) != 0)
errstr = ERR_error_string(ssl_err_code, NULL);
else if (ret == -1)
errstr = strerror(errno);
errx(1, "SSL error: %s", errstr);
}
}
If a function returns -1
, call tls_config_error()
or
tls_error()
to find out what went wrong.
The following functions, also have two special return values
TLS_WANT_POLLIN
and TLS_WANT_POLLOUT
.
tls_handshake()
tls_read()
tls_write()
tls_close()
Bad APIs lead to:
"A principled solution to the problem must involve a complete redesign of the SSL libraries' API"
The most dangerous code in the world: validating SSL certificates in non-browser software (2012 paper)
"The failure of various applications to note Python's negligence in this matter is a source of regular CVE assignment"
PEP 476 - Enabling certificate verification by default for stdlib http clients (2014)
Cites 11 CVEs in various applications, all due to bad default behaviour.
desc = SSL_CIPHER_description(cipher, NULL, 0);
Must strcmp to determine what to do!
desc = SSL_CIPHER_description(cipher, NULL, 0);
if (strcmp(desc, "OPENSSL_malloc Error") == 0) {
fprintf(stderr, "out of memory\n");
goto err;
}
fprintf(stdout, "%s", desc);
free(desc);
Some lessons to learn from this:
But wait, there's more!
Call X509_STORE_CTX_get_error()
to get the internal error code, which might:
X509_V_OK
X509_V_OK
.X509_V_OK
.Some lessons to learn from this:
/