diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index f6f57cb1..6121978e 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1077,7 +1077,7 @@ enum lws_callback_reasons { * including OpenSSL support, this callback allows your user code * to load extra certifcates into the server which allow it to * verify the validity of certificates returned by clients. user - * is the server's OpenSSL SSL_CTX* */ + * is the server's OpenSSL SSL_CTX* and in is the lws_vhost * */ LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23, /**< if the libwebsockets vhost was created with the option * LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this @@ -1462,6 +1462,8 @@ lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason, #define LWS_CB_REASON_AUX_BF__CGI_HEADERS 8 ///@} +struct lws_vhost; + /*! \defgroup generic hash * ## Generic Hash related functions * @@ -2233,8 +2235,6 @@ struct lws_protocols { * This is part of the ABI, don't needlessly break compatibility */ }; -struct lws_vhost; - /** * lws_vhost_name_to_protocol() - get vhost's protocol object from its name * @@ -5526,6 +5526,24 @@ lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type, LWS_VISIBLE LWS_EXTERN int lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type, union lws_tls_cert_info_results *buf, size_t len); + +/** + * lws_tls_acme_sni_cert_create() - creates a temp selfsigned cert + * and attaches to a vhost + * + * \param vhost: the vhost to acquire the selfsigned cert + * \param san_a: SAN written into the certificate + * \param san_b: second SAN written into the certificate + * + * + * Returns 0 if created and attached to the vhost. Returns -1 if problems and + * frees all allocations before returning. + * + * On success, any allocations are destroyed at vhost destruction automatically. + */ +LWS_VISIBLE LWS_EXTERN int +lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, + const char *san_b); ///@} /** \defgroup lws_ring LWS Ringbuffer APIs diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index c623eee3..c309abcc 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -969,6 +969,8 @@ struct http2_settings { * SSL SNI -> wsi -> bind after SSL negotiation */ +struct lws_tls_ss_pieces; + struct lws_vhost { #if !defined(LWS_WITH_ESP8266) char http_proxy_address[128]; @@ -1004,6 +1006,7 @@ struct lws_vhost { #ifdef LWS_OPENSSL_SUPPORT lws_tls_ctx *ssl_ctx; lws_tls_ctx *ssl_client_ctx; + struct lws_tls_ss_pieces *ss; /* for acme tls certs */ #endif #if defined(LWS_WITH_MBEDTLS) lws_tls_x509 *x509_client_CA; @@ -2370,6 +2373,7 @@ LWS_EXTERN void lwsl_emit_stderr(int level, const char *line); #define lws_context_init_ssl_library(_a) #define lws_ssl_anybody_has_buffered_read_tsi(_a, _b) (0) #define lws_tls_check_all_cert_lifetimes(_a) +#define lws_tls_acme_sni_cert_destroy(_a) #else #define LWS_SSL_ENABLED(context) (context->use_ssl) LWS_EXTERN int openssl_websocket_private_data_index; @@ -2416,8 +2420,11 @@ lws_tls_check_all_cert_lifetimes(struct lws_context *context); LWS_EXTERN int lws_context_init_server_ssl(struct lws_context_creation_info *info, struct lws_vhost *vhost); +void +lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost); #else #define lws_context_init_server_ssl(_a, _b) (0) +#define lws_tls_acme_sni_cert_destroy(_a) #endif LWS_EXTERN void lws_ssl_destroy(struct lws_vhost *vhost); @@ -2457,6 +2464,7 @@ lws_tls_client_create_vhost_context(struct lws_vhost *vh, const char *ca_filepath, const char *cert_filepath, const char *private_key_filepath); + LWS_EXTERN lws_tls_ctx * lws_tls_ctx_from_wsi(struct lws *wsi); LWS_EXTERN int diff --git a/lib/server/ssl-server.c b/lib/server/ssl-server.c index 66edb066..c480d336 100644 --- a/lib/server/ssl-server.c +++ b/lib/server/ssl-server.c @@ -90,7 +90,7 @@ lws_context_init_server_ssl(struct lws_context_creation_info *info, vhost->protocols[0].callback(&wsi, LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, - vhost->ssl_ctx, NULL, 0); + vhost->ssl_ctx, vhost, 0); } if (vhost->use_ssl) diff --git a/lib/tls/mbedtls/server.c b/lib/tls/mbedtls/server.c index a14bbc18..f12731b7 100644 --- a/lib/tls/mbedtls/server.c +++ b/lib/tls/mbedtls/server.c @@ -337,3 +337,20 @@ lws_tls_server_accept(struct lws *wsi) } +struct lws_tls_ss_pieces { + mbedtls_x509_crt x509; + +}; + +LWS_VISIBLE int +lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, + const char *san_b) +{ + + return 1; +} + +void +lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost) +{ +} diff --git a/lib/tls/mbedtls/ssl.c b/lib/tls/mbedtls/ssl.c index 0abc795c..391b01aa 100644 --- a/lib/tls/mbedtls/ssl.c +++ b/lib/tls/mbedtls/ssl.c @@ -278,6 +278,8 @@ lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx) SSL_CTX_free(vhost->ssl_client_ctx); + + lws_tls_acme_sni_cert_destroy(vhost); } void diff --git a/lib/tls/openssl/lws-genrsa.c b/lib/tls/openssl/lws-genrsa.c index b86068e2..3eabe4c3 100644 --- a/lib/tls/openssl/lws-genrsa.c +++ b/lib/tls/openssl/lws-genrsa.c @@ -34,8 +34,7 @@ lws_jwk_destroy_genrsa_elements(struct lws_genrsa_elements *el) } LWS_VISIBLE int -lws_genrsa_create(struct lws_genrsa_ctx *ctx, - struct lws_genrsa_elements *el) +lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_genrsa_elements *el) { int n; @@ -129,7 +128,8 @@ lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx, { const BIGNUM *mpi[5]; - RSA_get0_key(ctx->rsa, &mpi[JWK_KEY_N], &mpi[JWK_KEY_E], &mpi[JWK_KEY_D]); + RSA_get0_key(ctx->rsa, &mpi[JWK_KEY_N], &mpi[JWK_KEY_E], + &mpi[JWK_KEY_D]); RSA_get0_factors(ctx->rsa, &mpi[JWK_KEY_P], &mpi[JWK_KEY_Q]); #else { diff --git a/lib/tls/openssl/server.c b/lib/tls/openssl/server.c index 08458501..5e0b3e14 100644 --- a/lib/tls/openssl/server.c +++ b/lib/tls/openssl/server.c @@ -100,7 +100,8 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) */ vh = context->vhost_list; while (vh) { - if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) + if (!vh->being_destroyed && + vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) break; vh = vh->vhost_next; } @@ -125,7 +126,7 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) return SSL_TLSEXT_ERR_OK; } - lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port); + lwsl_notice("SNI: Found: %s:%d\n", servername, vh->listen_port); /* select the ssl ctx from the selected vhost for this conn */ SSL_set_SSL_CTX(ssl, vhost->ssl_ctx); @@ -432,3 +433,149 @@ lws_tls_server_accept(struct lws *wsi) return LWS_SSL_CAPABLE_ERROR; } +struct lws_tls_ss_pieces { + X509 *x509; + EVP_PKEY *pkey; + RSA *rsa; +}; + +LWS_VISIBLE LWS_EXTERN int +lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, + const char *san_b) +{ + GENERAL_NAMES *gens = sk_GENERAL_NAME_new_null(); + GENERAL_NAME *gen = NULL; + ASN1_IA5STRING *ia5 = NULL; + X509_NAME *name; + BIGNUM *bn; + int n; + + if (!gens) + return 1; + + vhost->ss = lws_zalloc(sizeof(*vhost->ss), "sni cert"); + if (!vhost->ss) { + GENERAL_NAMES_free(gens); + return 1; + } + + vhost->ss->x509 = X509_new(); + if (!vhost->ss->x509) + goto bail; + + ASN1_INTEGER_set(X509_get_serialNumber(vhost->ss->x509), 1); + X509_gmtime_adj(X509_get_notBefore(vhost->ss->x509), 0); + X509_gmtime_adj(X509_get_notAfter(vhost->ss->x509), 3600); + + vhost->ss->pkey = EVP_PKEY_new(); + if (!vhost->ss->pkey) + goto bail0; + + bn = BN_new(); + if (!bn) + goto bail1; + if (BN_set_word(bn, RSA_F4) != 1) { + BN_free(bn); + goto bail1; + } + + vhost->ss->rsa = RSA_new(); + if (!vhost->ss->rsa) { + BN_free(bn); + goto bail1; + } + + n = RSA_generate_key_ex(vhost->ss->rsa, 4096, bn, NULL); + BN_free(bn); + if (n != 1) + goto bail2; + + if (!EVP_PKEY_assign_RSA(vhost->ss->pkey, vhost->ss->rsa)) + goto bail2; + + X509_set_pubkey(vhost->ss->x509, vhost->ss->pkey); + + name = X509_get_subject_name(vhost->ss->x509); + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, + (unsigned char *)"GB", -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, + (unsigned char *)"somecompany", -1, -1, 0); + if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, + (unsigned char *)"temp.acme.invalid", + -1, -1, 0) != 1) { + lwsl_notice("failed to add CN\n"); + goto bail2; + } + X509_set_issuer_name(vhost->ss->x509, name); + + /* add the SAN payloads */ + + gen = GENERAL_NAME_new(); + ia5 = ASN1_IA5STRING_new(); + if (!ASN1_STRING_set(ia5, san_a, -1)) { + lwsl_notice("failed to set ia5\n"); + GENERAL_NAME_free(gen); + goto bail2; + } + GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); + sk_GENERAL_NAME_push(gens, gen); + + if (X509_add1_ext_i2d(vhost->ss->x509, NID_subject_alt_name, + gens, 0, X509V3_ADD_APPEND) != 1) + goto bail2; + + GENERAL_NAMES_free(gens); + + if (san_b && san_b[0]) { + gens = sk_GENERAL_NAME_new_null(); + gen = GENERAL_NAME_new(); + ia5 = ASN1_IA5STRING_new(); + if (!ASN1_STRING_set(ia5, san_a, -1)) { + lwsl_notice("failed to set ia5\n"); + GENERAL_NAME_free(gen); + goto bail2; + } + GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); + sk_GENERAL_NAME_push(gens, gen); + + if (X509_add1_ext_i2d(vhost->ss->x509, NID_subject_alt_name, + gens, 0, X509V3_ADD_APPEND) != 1) + goto bail2; + + GENERAL_NAMES_free(gens); + } + + /* sign it with our private key */ + if (!X509_sign(vhost->ss->x509, vhost->ss->pkey, EVP_sha256())) + goto bail2; + + /* tell the vhost to use our crafted certificate */ + SSL_CTX_use_certificate(vhost->ssl_ctx, vhost->ss->x509); + /* and to use our generated private key */ + SSL_CTX_use_PrivateKey(vhost->ssl_ctx, vhost->ss->pkey); + + return 0; + +bail2: + RSA_free(vhost->ss->rsa); +bail1: + EVP_PKEY_free(vhost->ss->pkey); +bail0: + X509_free(vhost->ss->x509); +bail: + lws_free(vhost->ss); + GENERAL_NAMES_free(gens); + + return 1; +} + +void +lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost) +{ + if (!vhost->ss) + return; + + EVP_PKEY_free(vhost->ss->pkey); + X509_free(vhost->ss->x509); + lws_free_set_NULL(vhost->ss); +} diff --git a/lib/tls/openssl/ssl.c b/lib/tls/openssl/ssl.c index 46d21ff8..7dbd3efa 100644 --- a/lib/tls/openssl/ssl.c +++ b/lib/tls/openssl/ssl.c @@ -416,6 +416,8 @@ lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost) if (!vhost->user_supplied_ssl_ctx && vhost->ssl_client_ctx) SSL_CTX_free(vhost->ssl_client_ctx); + + lws_tls_acme_sni_cert_destroy(vhost); } void