static int
openssl_start_listen (tls_ctx_t * lctx)
{
+ u64 flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
+ openssl_main_t *om = &openssl_main;
const SSL_METHOD *method;
SSL_CTX *ssl_ctx;
int rv;
u32 olc_index;
openssl_listen_ctx_t *olc;
app_cert_key_pair_t *ckpair;
- app_certkey_int_ctx_t *cki;
+ app_certkey_int_ctx_t *cki = 0;
- long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
- openssl_main_t *om = &openssl_main;
+ if (lctx->ckpair_index)
+ {
+ ckpair = app_cert_key_pair_get_if_valid (lctx->ckpair_index);
+ if (!ckpair)
+ return -1;
- ckpair = app_cert_key_pair_get_if_valid (lctx->ckpair_index);
- if (!ckpair)
- return -1;
+ if (!ckpair->cert || !ckpair->key)
+ {
+ TLS_DBG (1, "tls cert and/or key not configured %d",
+ lctx->parent_app_wrk_index);
+ return -1;
+ }
- if (!ckpair->cert || !ckpair->key)
- {
- TLS_DBG (1, "tls cert and/or key not configured %d",
- lctx->parent_app_wrk_index);
- return -1;
+ cki = app_certkey_get_int_ctx (ckpair, lctx->c_thread_index);
+ if (!cki || !cki->cert)
+ {
+ cki = openssl_init_certkey_init_ctx (ckpair, lctx->c_thread_index);
+ if (!cki)
+ {
+ clib_warning ("unable to initialize certificate/key pair");
+ return -1;
+ }
+ }
}
- method = lctx->tls_type == TRANSPORT_PROTO_TLS ? SSLv23_server_method () :
+ method = lctx->tls_type == TRANSPORT_PROTO_TLS ? TLS_server_method () :
DTLS_server_method ();
ssl_ctx = SSL_CTX_new (method);
if (!ssl_ctx)
/*
* Set the key and cert
*/
- cki = app_certkey_get_int_ctx (ckpair, lctx->c_thread_index);
- if (!cki || !cki->cert)
+ if (cki)
{
- cki = openssl_init_certkey_init_ctx (ckpair, lctx->c_thread_index);
- if (!cki)
+ rv = SSL_CTX_use_certificate (ssl_ctx, cki->cert);
+ if (rv != 1)
{
- clib_warning ("unable to initialize certificate/key pair");
- return -1;
+ clib_warning ("unable to use SSL certificate");
+ goto err;
}
}
-
- rv = SSL_CTX_use_certificate (ssl_ctx, cki->cert);
- if (rv != 1)
+ else
{
- clib_warning ("unable to use SSL certificate");
- goto err;
+ lctx->flags |= TLS_CONN_F_ASYNC_CERT;
}
rv = SSL_CTX_use_PrivateKey (ssl_ctx, cki->key);
return 0;
}
+static void
+openssl_server_async_cert_cb (app_crypto_async_reply_t *reply)
+{
+ app_crypto_async_req_handle_t handle = reply->handle;
+ clib_thread_index_t thread_index;
+ app_cert_key_pair_t *ckpair;
+ app_certkey_int_ctx_t *cki;
+ openssl_ctx_t *oc;
+ tls_ctx_t *ctx;
+
+ thread_index = handle.thread_index;
+ ASSERT (thread_index == vlib_get_thread_index ());
+ ctx = openssl_ctx_get_w_thread (handle.opaque & TLS_IDX_MASK,
+ handle.thread_index);
+ oc = (openssl_ctx_t *) ctx;
+
+ ckpair = app_cert_key_pair_get_if_valid (reply->async_cert.ckpair_index);
+ if (!ckpair || !ckpair->cert || !ckpair->key)
+ {
+ TLS_DBG (1, "Invalid certificate/key pair %u", ckpair_index);
+ goto error;
+ }
+
+ /* Get or initialize certificate context */
+ cki = app_certkey_get_int_ctx (ckpair, thread_index);
+ if (!cki || !cki->cert)
+ {
+ cki = openssl_init_certkey_init_ctx (ckpair, thread_index);
+ if (!cki)
+ {
+ TLS_DBG (1, "Failed to initialize certificate context");
+ goto error;
+ }
+ }
+
+ /* Set the certificate and private key */
+ if (SSL_use_certificate (oc->ssl, cki->cert) != 1)
+ {
+ TLS_DBG (1, "Failed to set certificate");
+ goto error;
+ }
+
+ if (SSL_use_PrivateKey (oc->ssl, cki->key) != 1)
+ {
+ TLS_DBG (1, "Failed to set private key");
+ goto error;
+ }
+
+ /* Verify that the private key matches the certificate */
+ if (SSL_check_private_key (oc->ssl) != 1)
+ {
+ TLS_DBG (1, "Private key does not match certificate");
+ goto error;
+ }
+
+ TLS_DBG (2, "Successfully set certificate for server %s",
+ SSL_get_servername (oc->ssl, TLSEXT_NAMETYPE_host_name));
+
+ /* Continue handshake */
+ while (1)
+ {
+ int rv, err;
+ rv = SSL_do_handshake (oc->ssl);
+ err = SSL_get_error (oc->ssl, rv);
+ if (openssl_main.async && err == SSL_ERROR_WANT_ASYNC)
+ break;
+
+ if (err != SSL_ERROR_WANT_WRITE)
+ break;
+ }
+
+ return;
+
+error:
+ /* Do not free ctx yet, in case we have pending rx events */
+ ctx->flags |= TLS_CONN_F_NO_APP_SESSION;
+ tls_disconnect_transport (ctx);
+}
+
+static int
+openssl_server_cert_callback (SSL *ssl, void *arg)
+{
+ u32 ctx_index = (u32) pointer_to_uword (arg);
+ tls_ctx_t *ctx = openssl_ctx_get (ctx_index);
+ openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
+ const char *servername;
+
+ TLS_DBG (2, "Server certificate callback invoked for ctx %u",
+ oc->openssl_ctx_index);
+
+ /* Get the requested server name from SNI extension */
+ servername = SSL_get_servername (ssl, TLSEXT_NAMETYPE_host_name);
+ if (!servername)
+ {
+ /* TODO maybe handle using default cert, if provided */
+ TLS_DBG (1, "No SNI provided");
+ return 0;
+ }
+
+ TLS_DBG (2, "SNI requested server name: %s", servername);
+
+ app_crypto_async_req_t req = { .req_type = APP_CRYPTO_ASYNC_REQ_TYPE_CERT,
+ .handle = {
+ .thread_index = ctx->c_thread_index,
+ .opaque = ctx->tls_ctx_handle,
+ },
+ .cb = openssl_server_async_cert_cb,
+ .app_wrk_index = ctx->parent_app_wrk_index,
+ .async_cert = {
+ .servername = (const u8 *) servername,
+ } };
+
+ oc->req_ticket = app_crypto_async_req (&req);
+ if (oc->req_ticket.as_u64 == APP_CRYPTO_ASYNC_INVALID_TICKET.as_u64)
+ return 0;
+
+ /* Certificate selection in progress */
+ return -1;
+}
+
static int
openssl_ctx_init_server (tls_ctx_t * ctx)
{
openssl_ctx_handshake_rx (ctx, tls_session);
}
+ /* Set up certificate callback for async certificate retrieval */
+ if (ctx->flags & TLS_CONN_F_ASYNC_CERT)
+ {
+ uword oc_index = oc->openssl_ctx_index;
+ TLS_DBG (2, "Set async cert callback for ctx %u", oc_index);
+ SSL_set_cert_cb (oc->ssl, openssl_server_cert_callback,
+ uword_to_pointer (oc_index, void *));
+ return 0;
+ }
+
while (1)
{
rv = SSL_do_handshake (oc->ssl);
tls_async_ctx_t async_ctx;
BIO *rbio;
BIO *wbio;
+ app_crypto_async_req_ticket_t req_ticket;
} openssl_ctx_t;
typedef struct tls_listen_ctx_opensl_
else
application_name_table_add (app);
+ app_crypto_ctx_init (&app->crypto_ctx);
+
a->app_index = app->app_index;
APP_DBG ("New app name: %v api index: %u index %u", app->name,
*/
if (application_is_builtin (app))
application_name_table_del (app);
+
+ app_crypto_ctx_free (&app->crypto_ctx);
+
vec_free (app->name);
pool_put (app_main.app_pool, app);
}
/** collector index, if any */
u32 evt_collector_index;
+
+ /** app crypto state */
+ app_crypto_ctx_t crypto_ctx;
} application_t;
+static inline app_crypto_wrk_t *
+app_crypto_wrk_get (application_t *app, clib_thread_index_t thread_index)
+{
+ return vec_elt_at_index (app->crypto_ctx.wrk, thread_index);
+}
+
typedef struct app_rx_mq_handle_
{
union
return 0;
}
+app_crypto_async_req_ticket_t
+app_crypto_async_req (app_crypto_async_req_t *areq)
+{
+ app_crypto_async_req_t *req;
+ app_crypto_wrk_t *crypto_wrk;
+ app_worker_t *app_wrk;
+ application_t *app;
+ app_crypto_async_req_ticket_t ticket;
+
+ app_wrk = app_worker_get (areq->app_wrk_index);
+ app = application_get (app_wrk->app_index);
+ if (!app->cb_fns.app_crypto_async)
+ return APP_CRYPTO_ASYNC_INVALID_TICKET;
+
+ crypto_wrk = app_crypto_wrk_get (app, areq->handle.thread_index);
+
+ /* TODO(fcoras) caching layer */
+
+ pool_get (crypto_wrk->reqs, req);
+ *req = *areq;
+ req->req_index = req - crypto_wrk->reqs;
+ req->cancelled = 0;
+ ticket.app_index = app->app_index;
+ ticket.req_index = req->req_index;
+
+ /* Hand over request to app */
+ if (app->cb_fns.app_crypto_async (req))
+ return APP_CRYPTO_ASYNC_INVALID_TICKET;
+
+ return ticket;
+}
+
+void
+app_crypto_async_cancel_req (app_crypto_async_req_ticket_t ticket)
+{
+ application_t *app = application_get (ticket.app_index);
+ clib_thread_index_t thread_index = vlib_get_thread_index ();
+ app_crypto_async_req_t *req;
+ app_crypto_wrk_t *crypto_wrk;
+
+ crypto_wrk = app_crypto_wrk_get (app, thread_index);
+
+ if (pool_is_free_index (crypto_wrk->reqs, ticket.req_index))
+ return;
+ req = pool_elt_at_index (crypto_wrk->reqs, ticket.req_index);
+ req->cancelled = 1;
+}
+
+void
+app_crypto_async_reply (app_crypto_async_reply_t *reply)
+{
+ application_t *app = application_get (reply->app_index);
+ clib_thread_index_t thread_index = reply->handle.thread_index;
+ app_crypto_wrk_t *crypto_wrk;
+ app_crypto_async_req_t *req;
+
+ ASSERT (thread_index == vlib_get_thread_index ());
+
+ crypto_wrk = app_crypto_wrk_get (app, thread_index);
+ req = pool_elt_at_index (crypto_wrk->reqs, reply->req_index);
+
+ if (req->cancelled)
+ goto done;
+
+ reply->handle = req->handle;
+ req->cb (reply);
+
+done:
+ pool_put (crypto_wrk->reqs, req);
+}
+
+void
+app_crypto_ctx_init (app_crypto_ctx_t *crypto_ctx)
+{
+ vec_validate (crypto_ctx->wrk, vlib_num_workers ());
+}
+
+void
+app_crypto_ctx_free (app_crypto_ctx_t *crypto_ctx)
+{
+ app_crypto_wrk_t *crypto_wrk;
+
+ vec_foreach (crypto_wrk, crypto_ctx->wrk)
+ pool_free (crypto_wrk->reqs);
+ vec_free (crypto_ctx->wrk);
+}
+
u8 *
format_cert_key_pair (u8 *s, va_list *args)
{
{
app_crypto_main_t *acm = &app_crypto_main;
- /* Index 0 was originally used by legacy apis, maintain as invalid */
+ /* Index 0 is invalid, used to indicate that no cert was provided */
app_cert_key_pair_alloc ();
acm->last_crypto_engine = CRYPTO_ENGINE_LAST;
void *data; /**< protocol specific data */
} crypto_context_t;
+typedef union
+{
+ struct
+ {
+ u32 app_index;
+ u32 req_index;
+ };
+ u64 as_u64;
+} app_crypto_async_req_ticket_t;
+
+#define APP_CRYPTO_ASYNC_INVALID_TICKET \
+ ((app_crypto_async_req_ticket_t){ { .app_index = ~0, .req_index = ~0 } })
+
+typedef union
+{
+ struct
+ {
+ u32 opaque; /**< opaque metadata */
+ clib_thread_index_t thread_index; /**< thread on which req was made */
+ };
+ u64 handle;
+} app_crypto_async_req_handle_t;
+
+struct app_crypto_async_reply_;
+
+typedef void (*app_crypto_async_req_cb) (
+ struct app_crypto_async_reply_ *reply);
+
+#define foreach_app_crypto_async_req_type _ (CERT, "async-cert")
+
+typedef enum app_crypto_req_type_
+{
+#define _(a, b) APP_CRYPTO_ASYNC_REQ_TYPE_##a,
+ foreach_app_crypto_async_req_type
+#undef _
+} app_crypto_async_req_type_t;
+
+typedef struct app_crypto_async_req_
+{
+ app_crypto_async_req_type_t req_type; /**< request type */
+ app_crypto_async_req_handle_t handle; /**< async request handle */
+ app_crypto_async_req_cb cb; /**< callback to invoke on completion */
+ u32 app_wrk_index; /**< application worker index */
+ u32 req_index; /**< index in crypto worker's request pool */
+ u8 cancelled; /**< flag to indicate if cancelled */
+ union
+ {
+ struct
+ {
+ const u8 *servername; /**< server name for SNI */
+ } async_cert; /**< async cert request data */
+ };
+} app_crypto_async_req_t;
+
+typedef struct app_crypto_async_reply_
+{
+ u32 app_index; /**< app that resolved the request */
+ u32 req_index; /**< request index in app crypto pool */
+ app_crypto_async_req_handle_t handle; /**< request handle */
+ app_crypto_async_req_type_t req_type; /**< request type */
+ union
+ {
+ struct
+ {
+ u32 ckpair_index; /**< certificate key-pair index */
+ } async_cert; /**< async cert reply data */
+ };
+} app_crypto_async_reply_t;
+
+typedef struct app_crypto_wrk_
+{
+ app_crypto_async_req_t *reqs;
+} app_crypto_wrk_t;
+
+typedef struct app_crypto_ctx_
+{
+ app_crypto_wrk_t *wrk;
+} app_crypto_ctx_t;
+
+void app_crypto_ctx_init (app_crypto_ctx_t *crypto_ctx);
+void app_crypto_ctx_free (app_crypto_ctx_t *crypto_ctx);
+
/*
* Certificate key-pair management
*/
return vec_elt_at_index (ck->cki, thread_index);
}
+app_crypto_async_req_ticket_t
+app_crypto_async_req (app_crypto_async_req_t *req);
+void app_crypto_async_cancel_req (app_crypto_async_req_ticket_t ticket);
+
/*
* Crypto engine management
*/
/** Collect and export session logs */
int (*app_evt_callback) (session_t *s);
+
+ /** Callback to handle async crypto requests */
+ int (*app_crypto_async) (app_crypto_async_req_t *req);
} session_cb_vft_t;
#define foreach_app_init_args \
typedef struct transport_endpt_crypto_cfg_
{
- u32 ckpair_index;
+ u32 ckpair_index; /**< index of ck pair in application crypto layer */
u8 alpn_protos[4]; /**< ordered by preference for server */
- u8 crypto_engine;
- u8 hostname[256]; /**< full domain len is 255 as per rfc 3986 */
+ u8 crypto_engine; /**< crypto engine requested */
+ u8 hostname[256]; /**< full domain len is 255 as per rfc 3986 */
} transport_endpt_crypto_cfg_t;
typedef struct transport_endpt_ext_cfg_
_ (RESUME, "resume") \
_ (HS_DONE, "handshake-done") \
_ (ASYNC_RD, "async-read") \
- _ (SHUTDOWN_TRANSPORT, "shutdown-transport")
+ _ (SHUTDOWN_TRANSPORT, "shutdown-transport") \
+ _ (ASYNC_CERT, "async-cert")
typedef enum tls_conn_flags_bit_
{