From e763c3ef1485404585b923365f93314aab4e8dd6 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Sun, 30 May 2021 09:19:26 +0000 Subject: Start work on bearssl server-side sni --- CONTRIBUTING | 5 ++ DCO | 37 +++++++++ src/include/s6-networking/sbearssl.h | 32 ++++++++ src/sbearssl/deps-lib/sbearssl | 3 + src/sbearssl/sbearssl-internal.h | 9 +++ src/sbearssl/sbearssl_sctx_init_full_generic.c | 87 ++++++++++++++++++++++ src/sbearssl/sbearssl_sctx_set_policy_sni.c | 11 +++ src/sbearssl/sbearssl_skey_storagelen.c | 16 ++++ src/sbearssl/sbearssl_skey_wipe.c | 26 +++++++ .../sbearssl_sni_policy_add_keypair_file.c | 42 +++++++++++ src/sbearssl/sbearssl_sni_policy_init.c | 75 +++++++++++++++++++ src/sbearssl/sbearssl_sni_policy_vtable.c | 71 ++++++++++++++++++ 12 files changed, 414 insertions(+) create mode 100644 CONTRIBUTING create mode 100644 DCO create mode 100644 src/sbearssl/sbearssl_sctx_init_full_generic.c create mode 100644 src/sbearssl/sbearssl_sctx_set_policy_sni.c create mode 100644 src/sbearssl/sbearssl_skey_storagelen.c create mode 100644 src/sbearssl/sbearssl_skey_wipe.c create mode 100644 src/sbearssl/sbearssl_sni_policy_add_keypair_file.c create mode 100644 src/sbearssl/sbearssl_sni_policy_init.c create mode 100644 src/sbearssl/sbearssl_sni_policy_vtable.c diff --git a/CONTRIBUTING b/CONTRIBUTING new file mode 100644 index 0000000..6279422 --- /dev/null +++ b/CONTRIBUTING @@ -0,0 +1,5 @@ + Please add a Signed-Off-By: line at the end of your commit, +which certifies that you have the right and authority to pass +it on as an open-source patch, as explicited in the Developer's +Certificate of Origin available in this project's DCO file, +or at https://developercertificate.org/ diff --git a/DCO b/DCO new file mode 100644 index 0000000..8201f99 --- /dev/null +++ b/DCO @@ -0,0 +1,37 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/src/include/s6-networking/sbearssl.h b/src/include/s6-networking/sbearssl.h index 8de12ab..83bc376 100644 --- a/src/include/s6-networking/sbearssl.h +++ b/src/include/s6-networking/sbearssl.h @@ -13,6 +13,7 @@ #include #include #include +#include /* * Support library for bearssl. @@ -153,6 +154,7 @@ extern int sbearssl_skey_from (sbearssl_skey *, br_skey const *, stralloc *) ; extern int sbearssl_skey_to (sbearssl_skey const *, br_skey *, char *) ; extern int sbearssl_skey_readfile (char const *, sbearssl_skey *, stralloc *) ; +extern void sbearssl_skey_wipe (sbearssl_skey, char *) ; /* Public keys */ @@ -262,6 +264,36 @@ extern int sbearssl_send_environment (br_ssl_engine_context *, sbearssl_handshak extern void sbearssl_run (br_ssl_engine_context *, int *, tain_t const *, uint32_t, unsigned int, sbearssl_handshake_cbfunc_ref, sbearssl_handshake_cbarg *) gccattr_noreturn ; + /* Generic server policy class and server-side SNI implementation */ + +typedef struct sbearssl_sni_map_s sbearssl_sni_map, *sbearssl_sni_map_ref ; +struct sbearssl_sni_map_s +{ + char const *servername ; + sbearssl_skey skey ; + size_t chainindex ; + size_t chainlen ; +} ; + +typedef struct sbearssl_sni_policy_context_s sbearssl_sni_policy_context, *sbearssl_sni_policy_context_ref ; +struct sbearssl_sni_policy_context_s +{ + br_ssl_server_policy_class const *vtable ; + br_skey skey ; + avltree map ; + genalloc mapga ; + genalloc certga ; + stralloc storage ; +} + +extern br_ssl_server_policy_class const sbearssl_sni_policy_vtable ; +extern int sbearssl_sni_policy_init (sbearssl_sni_policy_context *) ; +extern int sbearssl_sni_policy_add_keypair_file (sbearssl_sni_policy_context *, char const *, char const *, char const *) ; + +extern void sbearssl_sctx_init_full_generic (br_ssl_server_context *) ; +extern void sbearssl_sctx_set_policy_sni (br_ssl_server_context *, sbearssl_sni_policy_context *) ; + + /* s6-tlsc-io and s6-tlsd-io implementations */ extern void sbearssl_client_init_and_run (int *, tain_t const *, uint32_t, uint32_t, unsigned int, char const *, sbearssl_handshake_cbfunc_ref, sbearssl_handshake_cbarg *) gccattr_noreturn ; diff --git a/src/sbearssl/deps-lib/sbearssl b/src/sbearssl/deps-lib/sbearssl index 68b3ce1..c0d4507 100644 --- a/src/sbearssl/deps-lib/sbearssl +++ b/src/sbearssl/deps-lib/sbearssl @@ -24,8 +24,11 @@ sbearssl_rsa_pkey_to.o sbearssl_rsa_skey_from.o sbearssl_rsa_skey_to.o sbearssl_run.o +sbearssl_sctx_init_full_generic.o +sbearssl_sctx_set_policy_sni.o sbearssl_send_environment.o sbearssl_server_init_and_run.o +sbearssl_sni_policy_vtable.o sbearssl_skey_from.o sbearssl_skey_readfile.o sbearssl_skey_to.o diff --git a/src/sbearssl/sbearssl-internal.h b/src/sbearssl/sbearssl-internal.h index 21a28d7..d8e0c57 100644 --- a/src/sbearssl/sbearssl-internal.h +++ b/src/sbearssl/sbearssl-internal.h @@ -75,4 +75,13 @@ extern int sbearssl_pem_push (br_pem_decoder_context *, char const *, size_t, sb extern sbearssl_suiteinfo const *const sbearssl_suite_list ; extern size_t const sbearssl_suite_list_len ; +typedef struct sbearssl_sni_policy_node_s sbearssl_sni_policy_node, *sbearssl_policy_node_ref ; +struct sbearssl_sni_policy_node_s +{ + size_t servername ; + sbearssl_skey skey ; + size_t chainindex ; + size_t chainlen ; +} ; + #endif diff --git a/src/sbearssl/sbearssl_sctx_init_full_generic.c b/src/sbearssl/sbearssl_sctx_init_full_generic.c new file mode 100644 index 0000000..2c930c8 --- /dev/null +++ b/src/sbearssl/sbearssl_sctx_init_full_generic.c @@ -0,0 +1,87 @@ +/* ISC license. */ + +#include + +#include + +#include + +static uint16_t const suites[] = +{ + BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* ec cipher */ + BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* rsa cipher */ + BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + BR_TLS_RSA_WITH_AES_128_CCM, + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_RSA_WITH_AES_256_CCM, + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_RSA_WITH_AES_128_CCM_8, + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_RSA_WITH_AES_256_CCM_8, + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + BR_TLS_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_RSA_WITH_AES_256_CBC_SHA256, + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + BR_TLS_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA +} ; + +static br_hash_class const *hashes[] = +{ + &br_md5_vtable, + &br_sha1_vtable, + &br_sha224_vtable, + &br_sha256_vtable, + &br_sha384_vtable, + &br_sha512_vtable +} ; + +void sbearssl_sctx_init_full_generic (br_ssl_server_context *sc) +{ + br_ssl_server_zero(sc) ; + br_ssl_engine_set_versions(&sc->eng, BR_TLS10, BR_TLS12) ; + br_ssl_engine_set_suites(&sc->eng, suites, sizeof(suites) / sizeof(suites[0])) ; + br_ssl_engine_set_default_ec(&sc->eng) ; + + for (unsigned int i = br_md5_ID ; i <= br_sha512_ID ; i++) + br_ssl_engine_set_hash(&sc->eng, i, hashes[i-1]) ; + + br_ssl_engine_set_prf10(&sc->eng, &br_tls10_prf) ; + br_ssl_engine_set_prf_sha256(&sc->eng, &br_tls12_sha256_prf) ; + br_ssl_engine_set_prf_sha384(&sc->eng, &br_tls12_sha384_prf) ; + + br_ssl_engine_set_default_aes_cbc(&sc->eng) ; + br_ssl_engine_set_default_aes_ccm(&sc->eng) ; + br_ssl_engine_set_default_aes_gcm(&sc->eng) ; + br_ssl_engine_set_default_des_cbc(&sc->eng) ; + br_ssl_engine_set_default_chapol(&sc->eng) ; +} diff --git a/src/sbearssl/sbearssl_sctx_set_policy_sni.c b/src/sbearssl/sbearssl_sctx_set_policy_sni.c new file mode 100644 index 0000000..166cd97 --- /dev/null +++ b/src/sbearssl/sbearssl_sctx_set_policy_sni.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include + +#include + +void sbearssl_sctx_set_policy_sni (br_ssl_server_context *sc, sbearssl_sni_policy_context *pol) +{ + sc->chain_handler.vtable = pol->vtable ; + sc->policy_vtable = &sc->chain_handler.vtable ; +} diff --git a/src/sbearssl/sbearssl_skey_storagelen.c b/src/sbearssl/sbearssl_skey_storagelen.c new file mode 100644 index 0000000..706b5f6 --- /dev/null +++ b/src/sbearssl/sbearssl_skey_storagelen.c @@ -0,0 +1,16 @@ +/* ISC license. */ + +#include + +size_t sbearssl_skey_storagelen (sbearssl_skey const *l) +{ + switch (l->type) + { + case BR_KEYTYPE_RSA : + return l->data.rsa.plen + l->data.rsa.qlen + l->data.rsa.dplen + l->data.rsa.dqlen + l->data.rsa.iqlen ; + case BR_KEYTYPE_EC : + return l->data.ec.xlen ; + default : + return 0 ; + } +} diff --git a/src/sbearssl/sbearssl_skey_wipe.c b/src/sbearssl/sbearssl_skey_wipe.c new file mode 100644 index 0000000..208f89f --- /dev/null +++ b/src/sbearssl/sbearssl_skey_wipe.c @@ -0,0 +1,26 @@ +/* ISC license. */ + +#include + +#include + +#include + +void sbearssl_skey_wipe (sbearssl_skey *key, char *s) +{ + switch (key->type) + { + case BR_KEYTYPE_RSA : + byte_zzero(s + key->rsa.p, key->rsa.plen) ; + byte_zzero(s + key->rsa.q, key->rsa.qlen) ; + byte_zzero(s + key->rsa.dp, key->rsa.dplen) ; + byte_zzero(s + key->rsa.dq, key->rsa.dqlen) ; + byte_zzero(s + key->rsa.iq, key->rsa.iqlen) ; + break ; + case BR_KEYTYPE_EC : + byte_zzero(s + key->ec.x, key->ec.xlen) ; + break ; + default : break ; + } + byte_zzero(key, sizeof(sbearssl_skey)) ; +} diff --git a/src/sbearssl/sbearssl_sni_policy_add_keypair_file.c b/src/sbearssl/sbearssl_sni_policy_add_keypair_file.c new file mode 100644 index 0000000..e6cc974 --- /dev/null +++ b/src/sbearssl/sbearssl_sni_policy_add_keypair_file.c @@ -0,0 +1,42 @@ +/* ISC license. */ + +#include +#include + +#include + +#include +#include +#include + +#include +#include "sbearssl-internal.h" + +int sbearssl_sni_policy_add_keypair_file (sbearssl_sni_policy_context *pol, char const *servername, char const *certfile, char const *keyfile) +{ + size_t sabase = pol->storage.len ; + size_t gabase = genalloc_len(sbearssl_cert, &pol->certga) ; + size_t mbase = genalloc_len(sbearssl_sni_policy_node, &pol->mapga) ; + sbearssl_sni_policy_node node = { .servername = sabase, .chainindex = gabase } ; + + if (!stralloc_catb(&pol->storage, servername, strlen(servername) + 1)) return 0 ; + if (!sbearssl_cert_readbigpem(certfile, &pol->certga, &pol->storage)) goto err0 ; + node.chainlen = genalloc_len(sbearssl_cert, &pol->certga) - node.chainindex ; + if (!sbearssl_skey_readfile(keyfile, &node.skey, &pol->storage)) goto err1 ; + if (!genalloc_catb(sbearssl_sni_policy_node, &pol->mapga, &node, 1)) goto err2 ; + if (!avltree_insert(&pol->map, mbase)) goto err3 ; + return 1 ; + + err3: + if (mbase) genalloc_setlen(sbearssl_sni_policy_node, &pol->mapga, mbase) ; + else genalloc_free(sbearssl_sni_policy_node, &pol->mapga) ; + err2: + sbearssl_skey_wipe(&pol->skey, pol->storage.s) ; + err1: + if (gabase) genalloc_setlen(sbearssl_cert, &pol->certga, gabase) ; + else genalloc_free(sbearssl_sni_policy_node, &pol->mapga) ; + err0: + if (sabase) pol->storage.len = sabase ; + else stralloc_free(pol->storage) ; + return 0 ; +} diff --git a/src/sbearssl/sbearssl_sni_policy_init.c b/src/sbearssl/sbearssl_sni_policy_init.c new file mode 100644 index 0000000..fd0e946 --- /dev/null +++ b/src/sbearssl/sbearssl_sni_policy_init.c @@ -0,0 +1,75 @@ +/* ISC license. */ + +#include + +#include + +#include +#include +#include + +#include + +#define INSTANCE(c) ((sbearssl_sni_policy_context *)(c)) + +static int choose (br_ssl_server_policy_class const **pctx, br_ssl_server_context const *sc, br_ssl_server_choices *choices) +{ + sbearssl_sni_policy_context *pol = INSTANCE(pctx) ; + uint32_t n ; + char const *servername = br_ssl_engine_get_server_name(&sc->eng) ; + if (!avltree_search(&pol->map, servername, &n) + && (!servername[0] || !avltree_search(&pol->map, "", &n))) + return 0 ; + avltree_free(&pol->map) ; + copy_and_free(pol, n) ; +} + +static uint32_t do_keyx (br_ssl_server_policy_class const **pctx, unsigned char *data, size_t *len) +{ + sbearssl_sni_policy_context *pol = INSTANCE(pctx) ; + switch (pol->skey.type) + { + case BR_KEYTYPE_RSA : return kx_rsa(pol, data, len) ; + case BR_KEYTYPE_EC : return kx_ec(pol, data, len) ; + default : return 0 ; + } +} + +static size_t do_sign (br_ssl_server_policy_class const **pctx, unsigned int algo_id, unsigned char *data, size_t hv_len, size_t len) +{ + sbearssl_sni_policy_context *pol = INSTANCE(pctx) ; + switch (pol->skey.type) + { + case BR_KEYTYPE_RSA : return sign_rsa(pol, algo_id, data, hv_len, len) ; + case BR_KEYTYPE_EC : return sign_ec(pol, algo_id, data, hv_len, len) ; + default : return 0 ; + } +} + +static br_ssl_server_policy_class const vtable = +{ + .context_size = sizeof(sbearssl_sni_policy_context), + .choose = &choose, + .do_keyx = &do_keyx, + .do_sign = &do_sign +} ; + +static void *sbearssl_sni_policy_node_dtok (uint32_t d, void *data) +{ + return ((sbearssl_sni_policy_context *)data)->storage.s + d ; +} + +static int sbearssl_sni_policy_node_cmp (void const *a, void const *b, void *data) +{ + (void)data ; + return strcmp((char const *)a, (char const *)b) ; +} + +void sbearssl_sni_policy_init (sbearssl_sni_policy_context *pol) +{ + pol->vtable = &vtable ; + pol->map = avltree_zero ; + pol->mapga = genalloc_zero ; + pol->certga = genalloc_zero ; + pol->storage = GENALLOC_ZERO ; +} diff --git a/src/sbearssl/sbearssl_sni_policy_vtable.c b/src/sbearssl/sbearssl_sni_policy_vtable.c new file mode 100644 index 0000000..3b39055 --- /dev/null +++ b/src/sbearssl/sbearssl_sni_policy_vtable.c @@ -0,0 +1,71 @@ +/* ISC license. */ + +#include + +#include + +#define INSTANCE(c) ((sbearssl_x509_small_context *)(c)) + +static void start_chain (br_x509_class const **c, char const *server_name) +{ + sbearssl_x509_small_context *ctx = INSTANCE(c) ; + ctx->minimal.vtable->start_chain(&ctx->minimal.vtable, server_name) ; + + ctx->i = 0 ; +} + +static void start_cert (br_x509_class const **c, uint32_t len) +{ + sbearssl_x509_small_context *ctx = INSTANCE(c) ; + ctx->minimal.vtable->start_cert(&ctx->minimal.vtable, len) ; + + if (!ctx->i) br_sha256_init(&ctx->hashctx) ; +} + +static void append (br_x509_class const **c, unsigned char const *s, size_t len) +{ + sbearssl_x509_small_context *ctx = INSTANCE(c) ; + ctx->minimal.vtable->append(&ctx->minimal.vtable, s, len) ; + + if (!ctx->i) br_sha256_update(&ctx->hashctx, s, len) ; +} + +static void end_cert (br_x509_class const **c) +{ + sbearssl_x509_small_context *ctx = INSTANCE(c) ; + ctx->minimal.vtable->end_cert(&ctx->minimal.vtable) ; + + if (!ctx->i) br_sha256_out(&ctx->hashctx, ctx->eehash) ; + ctx->i++ ; +} + +static unsigned int end_chain (br_x509_class const **c) +{ + sbearssl_x509_small_context *ctx = INSTANCE(c) ; + unsigned int r = ctx->minimal.vtable->end_chain(&ctx->minimal.vtable) ; + if (!r) + { + uint8_t mask = 1 ; + for (unsigned int i = 0 ; i < 6 ; i++, mask <<= 1) + if (ctx->elts[i].status) + *ctx->eltstatus |= ctx->elts[i].status < 0 ? 128 : mask ; + } + return r ; +} + +static br_x509_pkey const *get_pkey(br_x509_class const *const *c, unsigned int *usages) +{ + sbearssl_x509_small_context *ctx = INSTANCE(c) ; + return ctx->minimal.vtable->get_pkey(&ctx->minimal.vtable, usages) ; +} + +br_x509_class const sbearssl_x509_small_vtable = +{ + .context_size = sizeof(sbearssl_x509_small_context), + .start_chain = &start_chain, + .start_cert = &start_cert, + .append = &append, + .end_cert = &end_cert, + .end_chain = &end_chain, + .get_pkey = &get_pkey, +} ; -- cgit v1.2.3