int lws_tls_server_vhost_backend_init(struct lws_context_creation_info *info, struct lws_vhost *vhost, struct lws *wsi) { const SSL_METHOD *method = TLS_server_method(); uint8_t *p; lws_filepos_t flen; vhost->ssl_ctx = SSL_CTX_new(method); /* create context */ if (!vhost->ssl_ctx) { lwsl_err("problem creating ssl context\n"); return 1; } if (!vhost->use_ssl || !info->ssl_cert_filepath) return 0; if (info->ssl_ca_filepath) { lwsl_notice("%s: vh %s: loading CA filepath %s\n", __func__, vhost->name, info->ssl_ca_filepath); if (lws_tls_alloc_pem_to_der_file(vhost->context, info->ssl_ca_filepath, NULL, 0, &p, &flen)) { lwsl_err("couldn't find client CA file %s\n", info->ssl_ca_filepath); return 1; } if (SSL_CTX_add_client_CA_ASN1(vhost->ssl_ctx, (int)flen, p) != 1) { lwsl_err("%s: SSL_CTX_add_client_CA_ASN1 unhappy\n", __func__); free(p); return 1; } free(p); } return lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath, info->ssl_private_key_filepath, NULL, 0, NULL, 0); }
/* * this may now get called after the vhost creation, when certs become * available. */ int lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, const char *cert, const char *private_key, const char *mem_cert, size_t mem_cert_len, const char *mem_privkey, size_t mem_privkey_len) { #if !defined(OPENSSL_NO_EC) const char *ecdh_curve = "prime256v1"; #if !defined(LWS_WITH_BORINGSSL) && defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) STACK_OF(X509) *extra_certs = NULL; #endif EC_KEY *ecdh, *EC_key = NULL; EVP_PKEY *pkey; X509 *x = NULL; int ecdh_nid; int KeyType; #endif unsigned long error; lws_filepos_t flen; uint8_t *p; int ret; int n = lws_tls_generic_cert_checks(vhost, cert, private_key), m; (void)ret; if (!cert && !private_key) n = LWS_TLS_EXTANT_ALTERNATIVE; if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey)) return 0; if (n == LWS_TLS_EXTANT_NO) n = LWS_TLS_EXTANT_ALTERNATIVE; if (n == LWS_TLS_EXTANT_ALTERNATIVE && (!mem_cert || !mem_privkey)) return 1; /* no alternative */ if (n == LWS_TLS_EXTANT_ALTERNATIVE) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* * Although we have prepared update certs, we no longer have * the rights to read our own cert + key we saved. * * If we were passed copies in memory buffers, use those * in favour of the filepaths we normally want. */ cert = NULL; private_key = NULL; } /* * use the multi-cert interface for backwards compatibility in the * both simple files case */ if (n != LWS_TLS_EXTANT_ALTERNATIVE && cert) { /* set the local certificate from CertFile */ m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); if (m != 1) { error = ERR_get_error(); lwsl_err("problem getting cert '%s' %lu: %s\n", cert, error, ERR_error_string(error, (char *)vhost->context->pt[0].serv_buf)); return 1; } if (private_key) { /* set the private key from KeyFile */ if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, SSL_FILETYPE_PEM) != 1) { error = ERR_get_error(); lwsl_err("ssl problem getting key '%s' %lu: %s\n", private_key, error, ERR_error_string(error, (char *)vhost->context->pt[0].serv_buf)); return 1; } } else { if (vhost->protocols[0].callback(wsi, LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, vhost->tls.ssl_ctx, NULL, 0)) { lwsl_err("ssl private key not set\n"); return 1; } } return 0; } /* otherwise allow for DER or PEM, file or memory image */ if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, mem_cert_len, &p, &flen)) { lwsl_err("%s: couldn't read cert file\n", __func__); return 1; } #if !defined(USE_WOLFSSL) ret = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, (int)flen, p); #else ret = wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, (uint8_t *)p, (int)flen, WOLFSSL_FILETYPE_ASN1); #endif lws_free_set_NULL(p); if (ret != 1) { lwsl_err("%s: Problem loading cert\n", __func__); return 1; } if (lws_tls_alloc_pem_to_der_file(vhost->context, private_key, mem_privkey, mem_privkey_len, &p, &flen)) { lwsl_notice("unable to convert memory privkey\n"); return 1; } #if !defined(USE_WOLFSSL) ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, vhost->tls.ssl_ctx, p, (long)(long long)flen); if (ret != 1) { ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_EC, vhost->tls.ssl_ctx, p, (long)(long long)flen); } #else ret = wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, flen, WOLFSSL_FILETYPE_ASN1); #endif lws_free_set_NULL(p); if (ret != 1) { lwsl_notice("unable to use memory privkey\n"); return 1; } #else /* * Although we have prepared update certs, we no longer have * the rights to read our own cert + key we saved. * * If we were passed copies in memory buffers, use those * instead. * * The passed memory-buffer cert image is in DER, and the * memory-buffer private key image is PEM. */ #ifndef USE_WOLFSSL if (SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, (int)mem_cert_len, (uint8_t *)mem_cert) != 1) { #else if (wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, (uint8_t *)mem_cert, (int)mem_cert_len, WOLFSSL_FILETYPE_ASN1) != 1) { #endif lwsl_err("Problem loading update cert\n"); return 1; } if (lws_tls_alloc_pem_to_der_file(vhost->context, NULL, mem_privkey, mem_privkey_len, &p, &flen)) { lwsl_notice("unable to convert memory privkey\n"); return 1; } #ifndef USE_WOLFSSL if (SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, vhost->tls.ssl_ctx, p, (long)(long long)flen) != 1) { #else if (wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, flen, WOLFSSL_FILETYPE_ASN1) != 1) { #endif lwsl_notice("unable to use memory privkey\n"); return 1; } goto check_key; } /* set the local certificate from CertFile */ m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); if (m != 1) { error = ERR_get_error(); lwsl_err("problem getting cert '%s' %lu: %s\n", cert, error, ERR_error_string(error, (char *)vhost->context->pt[0].serv_buf)); return 1; } if (n != LWS_TLS_EXTANT_ALTERNATIVE && private_key) { /* set the private key from KeyFile */ if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, SSL_FILETYPE_PEM) != 1) { error = ERR_get_error(); lwsl_err("ssl problem getting key '%s' %lu: %s\n", private_key, error, ERR_error_string(error, (char *)vhost->context->pt[0].serv_buf)); return 1; } } else { if (vhost->protocols[0].callback(wsi, LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY, vhost->tls.ssl_ctx, NULL, 0)) { lwsl_err("ssl private key not set\n"); return 1; } } check_key: #endif /* verify private key */ if (!SSL_CTX_check_private_key(vhost->tls.ssl_ctx)) { lwsl_err("Private SSL key doesn't match cert\n"); return 1; } #if !defined(OPENSSL_NO_EC) if (vhost->tls.ecdh_curve[0]) ecdh_curve = vhost->tls.ecdh_curve; ecdh_nid = OBJ_sn2nid(ecdh_curve); if (NID_undef == ecdh_nid) { lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve); return 1; } ecdh = EC_KEY_new_by_curve_name(ecdh_nid); if (NULL == ecdh) { lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve); return 1; } SSL_CTX_set_tmp_ecdh(vhost->tls.ssl_ctx, ecdh); EC_KEY_free(ecdh); SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_SINGLE_ECDH_USE); lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve); if (lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH)) lwsl_notice(" Using ECDH certificate support\n"); /* Get X509 certificate from ssl context */ #if !defined(LWS_WITH_BORINGSSL) #if !defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) x = sk_X509_value(vhost->tls.ssl_ctx->extra_certs, 0); #else SSL_CTX_get_extra_chain_certs_only(vhost->tls.ssl_ctx, &extra_certs); if (extra_certs) x = sk_X509_value(extra_certs, 0); else lwsl_info("%s: no extra certs\n", __func__); #endif if (!x) { //lwsl_err("%s: x is NULL\n", __func__); goto post_ecdh; } #else return 0; #endif /* Get the public key from certificate */ pkey = X509_get_pubkey(x); if (!pkey) { lwsl_err("%s: pkey is NULL\n", __func__); return 1; } /* Get the key type */ KeyType = EVP_PKEY_type(EVP_PKEY_id(pkey)); if (EVP_PKEY_EC != KeyType) { lwsl_notice("Key type is not EC\n"); return 0; } /* Get the key */ EC_key = EVP_PKEY_get1_EC_KEY(pkey); /* Set ECDH parameter */ if (!EC_key) { lwsl_err("%s: ECDH key is NULL \n", __func__); return 1; } SSL_CTX_set_tmp_ecdh(vhost->tls.ssl_ctx, EC_key); EC_KEY_free(EC_key); #else lwsl_notice(" OpenSSL doesn't support ECDH\n"); #endif #if !defined(OPENSSL_NO_EC) && !defined(LWS_WITH_BORINGSSL) post_ecdh: #endif vhost->tls.skipped_certs = 0; return 0; } int lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, struct lws_vhost *vhost, struct lws *wsi) { unsigned long error; SSL_METHOD *method = (SSL_METHOD *)SSLv23_server_method(); if (!method) { error = ERR_get_error(); lwsl_err("problem creating ssl method %lu: %s\n", error, ERR_error_string(error, (char *)vhost->context->pt[0].serv_buf)); return 1; } vhost->tls.ssl_ctx = SSL_CTX_new(method); /* create context */ if (!vhost->tls.ssl_ctx) { error = ERR_get_error(); lwsl_err("problem creating ssl context %lu: %s\n", error, ERR_error_string(error, (char *)vhost->context->pt[0].serv_buf)); return 1; } SSL_CTX_set_ex_data(vhost->tls.ssl_ctx, openssl_SSL_CTX_private_data_index, (char *)vhost->context); /* Disable SSLv2 and SSLv3 */ SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); #ifdef SSL_OP_NO_COMPRESSION SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_NO_COMPRESSION); #endif SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_SINGLE_DH_USE); SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (info->ssl_cipher_list) SSL_CTX_set_cipher_list(vhost->tls.ssl_ctx, info->ssl_cipher_list); #if defined(LWS_HAVE_SSL_CTX_set_ciphersuites) if (info->tls1_3_plus_cipher_list) SSL_CTX_set_ciphersuites(vhost->tls.ssl_ctx, info->tls1_3_plus_cipher_list); #endif #if !defined(OPENSSL_NO_TLSEXT) SSL_CTX_set_tlsext_servername_callback(vhost->tls.ssl_ctx, lws_ssl_server_name_cb); SSL_CTX_set_tlsext_servername_arg(vhost->tls.ssl_ctx, vhost->context); #endif if (info->ssl_ca_filepath && !SSL_CTX_load_verify_locations(vhost->tls.ssl_ctx, info->ssl_ca_filepath, NULL)) { lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", __func__); } if (info->ssl_options_set) SSL_CTX_set_options(vhost->tls.ssl_ctx, info->ssl_options_set); /* SSL_clear_options introduced in 0.9.8m */ #if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) if (info->ssl_options_clear) SSL_CTX_clear_options(vhost->tls.ssl_ctx, info->ssl_options_clear); #endif lwsl_info(" SSL options 0x%lX\n", (unsigned long)SSL_CTX_get_options(vhost->tls.ssl_ctx)); if (!vhost->tls.use_ssl || (!info->ssl_cert_filepath && !info->server_ssl_cert_mem)) return 0; lws_ssl_bind_passphrase(vhost->tls.ssl_ctx, info); return lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath, info->ssl_private_key_filepath, info->server_ssl_cert_mem, info->server_ssl_cert_mem_len, info->server_ssl_private_key_mem, info->server_ssl_private_key_mem_len); }