#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct peek_client_hello_ctx { unsigned char sni_peek_retries; /* max 64 SNI parse retries */ struct event* ev; struct event_base* evbase; }; void peek_client_hello_ctx_free(void* ctx) { struct peek_client_hello_ctx * _ctx=(struct peek_client_hello_ctx *)ctx; event_free(_ctx->ev); _ctx->ev = NULL; free(_ctx->sni); _ctx->sni=NULL; free(_ctx); return; } struct ssl_client_hello* ssl_get_peek_result(future_result_t* result) { struct ssl_client_hello* p=(struct ssl_client_hello* )result, *copy=NULL; copy=ALLOC(struct ssl_client_hello*,1); if(p!=NULL) { copy->sni=tfe_strdup(p->sni); copy->cipher_suites=tfe_strdup(p->cipher_suites); copy->version=p->version; } return copy; } void ssl_free_peek_result(struct ssl_client_hello* p) { if(p==NULL) { return; } free(p->sni); p->sni=NULL; free(p->cipher_suites); p->cipher_suites=NULL; p->cipher_suites=NULL; free(p); return; } static void peek_client_hello_cb(evutil_socket_t fd, short what, void * arg) { struct promise* promise=(struct promise*)arg; struct peek_client_hello_ctx* ctx= (struct peek_client_hello_ctx*)promise->ctx; struct ssl_client_hello *result=NULL; memset(&result, 0, sizeof(result)); char* sni=NULL; unsigned char buf[1024]; ssize_t n=0; const unsigned char * chello=NULL; int rv=0; n = recv(fd, buf, sizeof(buf), MSG_PEEK); if (n == -1) { TFE_LOG_ERROR("Error peeking on fd, aborting connection\n"); goto failed; } if (n == 0) { goto failed; } result=ssl_get_peek_result(NULL); //todo: parse version and cipher suites. rv = ssl_tls_clienthello_parse(buf, n, 0, &chello, &result->sni); if ((rv == 1) && !chello) { TFE_LOG_ERROR("Peeking did not yield a (truncated) ClientHello message, aborting connection\n"); goto failed; } if ((rv == 1) && chello && (ctx->sni_peek_retries++ < 50)) { /* ssl_tls_clienthello_parse indicates that we * should retry later when we have more data, and we * haven't reached the maximum retry count yet. * Reschedule this event as timeout-only event in * order to prevent busy looping over the read event. * Because we only peeked at the pending bytes and * never actually read them, fd is still ready for * reading now. We use 25 * 0.2 s = 5 s timeout. */ struct timeval retry_delay = {0, 100}; event_free(ctx->ev); ctx->ev = event_new(ctx->evbase, fd, 0, peek_client_hello_cb, promise); assert(ctx->ev!=NULL); event_add(ctx->ev, &retry_delay); return; } promise_dettach_ctx(promise); promise_success(promise, result); ssl_free_peek_result(result); peek_client_hello_ctx_free(ctx); return; failed: promise_failed(promise,FUTURE_ERROR_EXCEPTION,"too many tries"); peek_client_hello_ctx_free(ctx); promise_dettach_ctx(promise); ssl_free_peek_result(result); return; } void ssl_async_peek_client_hello(struct future* future, evutil_socket_t fd, struct event_base *evbase) { struct event * ev=NULL; struct promise* p=future_to_promise(future); struct peek_client_hello_ctx* ctx=ALLOC(struct peek_client_hello_ctx, 1); ctx->ev = event_new(evbase, fd, EV_READ, peek_client_hello_cb, p); event_add(evbase, NULL); promise_set_ctx(p, ctx, peek_client_hello_ctx_free); return; } struct ssl_connect_origin_ctx { struct bufferevent *bev; SSL* ssl; }; void ssl_connect_origin_ctx_free(struct ssl_connect_origin_ctx* ctx) { ctx->ssl=NULL; bufferevent_free(ctx->bev); ctx->bev=NULL; //Do not free bev and ssl, reserved for user. free(ctx); } /* * Callback for meta events on the up- and downstream connection bufferevents. * Called when EOF has been reached, a connection has been made, and on errors. */ static void ssl_connect_origin_eventcb(struct bufferevent * bev, short events, void * arg) { struct promise* promise=(struct promise*)arg; struct ssl_connect_origin_ctx* ctx=(struct ssl_connect_origin_ctx*)promise_dettach_ctx(promise); struct tfe_stream_private* _stream = (struct tfe_stream_private*)arg; if (events & BEV_EVENT_ERROR) { promise->f.cb_failed(FUTURE_ERROR_EXCEPTION,"ssl connect failed",promise->f.user); } else { if (events & BEV_EVENT_CONNECTED) { promise->f.cb_success(ctx->bev,promise->f.user); ctx->bev=NULL; } else { assert(0); } } ssl_connect_origin_ctx_free(ctx); return } void ssl_async_connect_origin(struct future* future, const struct ssl_client_hello* client_hello, evutil_socket_t fd, const char* sni, struct event_base *evbase) { struct promise* p=future_to_promise(future); struct ssl_connect_origin_ctx* ctx=ALLOC(struct ssl_connect_origin_ctx, 1); ctx->ssl=upstream_ssl_create(opts,sni); ctx->bev = bufferevent_openssl_socket_new(evbase, fd, ctx->ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS); promise_set_ctx(p, ctx, ssl_connect_origin_ctx_free); bufferevent_openssl_set_allow_dirty_shutdown(ctx->bev, 1); bufferevent_setcb(ctx->bev, NULL, NULL, ssl_connect_origin_eventcb, p); bufferevent_enable(ctx->bev, EV_READ | EV_WRITE); } /* * Dump information on a certificate to the debug log. */ static void pxy_debug_crt(X509 * crt) { char * sj = ssl_x509_subject(crt); if (sj) { log_dbg_printf("Subject DN: %s\n", sj); free(sj); } char * names = ssl_x509_names_to_str(crt); if (names) { log_dbg_printf("Common Names: %s\n", names); free(names); } char * fpr; if (!(fpr = ssl_x509_fingerprint(crt, 1))) { log_err_printf("Warning: Error generating X509 fingerprint\n"); } else { log_dbg_printf("Fingerprint: %s\n", fpr); free(fpr); } #ifdef DEBUG_CERTIFICATE /* dump certificate */ log_dbg_print_free(ssl_x509_to_str(crt)); log_dbg_print_free(ssl_x509_to_pem(crt)); #endif /* DEBUG_CERTIFICATE */ } static void pxy_log_connect_nonhttp(pxy_conn_ctx_t * ctx) { char * msg; int rv; /* * The following ifdef's within asprintf arguments list generates * warnings with -Wembedded-directive on some compilers. * Not fixing the code in order to avoid more code duplication. */ if (!ctx->src.ssl) { rv = asprintf(&msg, "%s %s %s %s %s" #ifdef HAVE_LOCAL_PROCINFO " %s" #endif /* HAVE_LOCAL_PROCINFO */ "\n", ctx->passthrough ? "passthrough" : "tcp", STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str) ); } else { rv = asprintf(&msg, "%s %s %s %s %s " "sni:%s names:%s " "sproto:%s:%s dproto:%s:%s " "origcrt:%s usedcrt:%s\n", ctx->clienthello_found ? "upgrade" : "ssl", STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str), STRORDASH(ctx->sni), STRORDASH(ctx->ssl_names), SSL_get_version(ctx->src.ssl), SSL_get_cipher(ctx->src.ssl), SSL_get_version(ctx->dst.ssl), SSL_get_cipher(ctx->dst.ssl), STRORDASH(ctx->origcrtfpr), STRORDASH(ctx->usedcrtfpr) ); } if ((rv < 0) || !msg) { ctx->enomem = 1; goto out; } if (!ctx->opts->detach) { log_err_printf("%s", msg); } if (ctx->opts->connectlog) { } else { free(msg); } out: return; } static void pxy_log_connect_http(pxy_conn_ctx_t * ctx) { char * msg; int rv; #ifdef DEBUG_PROXY if (ctx->passthrough) { log_err_printf("Warning: pxy_log_connect_http called while in " "passthrough mode\n"); return; } #endif /* * The following ifdef's within asprintf arguments list generates * warnings with -Wembedded-directive on some compilers. * Not fixing the code in order to avoid more code duplication. */ if (!ctx->spec->ssl) { rv = asprintf(&msg, "http %s %s %s %s %s %s %s %s %s %s\n", STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str), STRORDASH(ctx->http_host), STRORDASH(ctx->http_method), STRORDASH(ctx->http_uri), STRORDASH(ctx->http_status_code), STRORDASH(ctx->http_content_length), ctx->ocsp_denied ? " ocsp:denied" : ""); } else { rv = asprintf(&msg, "https %s %s %s %s %s %s %s %s %s " "sni:%s names:%s " "sproto:%s:%s dproto:%s:%s " "origcrt:%s usedcrt:%s" #ifdef HAVE_LOCAL_PROCINFO " %s" #endif /* HAVE_LOCAL_PROCINFO */ "%s\n", STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str), STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str), STRORDASH(ctx->http_host), STRORDASH(ctx->http_method), STRORDASH(ctx->http_uri), STRORDASH(ctx->http_status_code), STRORDASH(ctx->http_content_length), STRORDASH(ctx->sni), STRORDASH(ctx->ssl_names), SSL_get_version(ctx->src.ssl), SSL_get_cipher(ctx->src.ssl), SSL_get_version(ctx->dst.ssl), SSL_get_cipher(ctx->dst.ssl), STRORDASH(ctx->origcrtfpr), STRORDASH(ctx->usedcrtfpr), ctx->ocsp_denied ? " ocsp:denied" : ""); } if ((rv < 0) || !msg) { ctx->enomem = 1; goto out; } if (!ctx->opts->detach) { log_err_printf("%s", msg); } if (ctx->opts->connectlog) { } else { free(msg); } out: return; } /* * Called by OpenSSL when a new src SSL session is created. * OpenSSL increments the refcount before calling the callback and will * decrement it again if we return 0. Returning 1 will make OpenSSL skip * the refcount decrementing. In other words, return 0 if we did not * keep a pointer to the object (which we never do here). */ #ifdef HAVE_SSLV2 #define MAYBE_UNUSED #else /* !HAVE_SSLV2 */ #define MAYBE_UNUSED UNUSED #endif /* !HAVE_SSLV2 */ static int pxy_ossl_sessnew_cb(MAYBE_UNUSED SSL * ssl, SSL_SESSION * sess) #undef MAYBE_UNUSED { #ifdef DEBUG_SESSION_CACHE log_dbg_printf("===> OpenSSL new session callback:\n"); if (sess) { log_dbg_print_free(ssl_session_to_str(sess)); } else { log_dbg_printf("(null)\n"); } #endif /* DEBUG_SESSION_CACHE */ #ifdef HAVE_SSLV2 /* Session resumption seems to fail for SSLv2 with protocol * parsing errors, so we disable caching for SSLv2. */ if (SSL_version(ssl) == SSL2_VERSION) { log_err_printf("Warning: Session resumption denied to SSLv2" "client.\n"); return 0; } #endif /* HAVE_SSLV2 */ if (sess) { cachemgr_ssess_set(sess); } return 0; } /* * Called by OpenSSL when a src SSL session should be removed. * OpenSSL calls SSL_SESSION_free() after calling the callback; * we do not need to free the reference here. */ static void pxy_ossl_sessremove_cb(UNUSED SSL_CTX * sslctx, SSL_SESSION * sess) { #ifdef DEBUG_SESSION_CACHE log_dbg_printf("===> OpenSSL remove session callback:\n"); if (sess) { log_dbg_print_free(ssl_session_to_str(sess)); } else { log_dbg_printf("(null)\n"); } #endif /* DEBUG_SESSION_CACHE */ if (sess) { cachemgr_ssess_del(sess); } } /* * Called by OpenSSL when a src SSL session is requested by the client. */ static SSL_SESSION * #if OPENSSL_VERSION_NUMBER < 0x10100000L pxy_ossl_sessget_cb(UNUSED SSL * ssl, unsigned char * id, int idlen, int * copy) #else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ pxy_ossl_sessget_cb(UNUSED SSL *ssl, const unsigned char *id, int idlen, int *copy) #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ { SSL_SESSION * sess; #ifdef DEBUG_SESSION_CACHE log_dbg_printf("===> OpenSSL get session callback:\n"); #endif /* DEBUG_SESSION_CACHE */ *copy = 0; /* SSL should not increment reference count of session */ sess = (SSL_SESSION *) cachemgr_ssess_get(id, idlen); #ifdef DEBUG_SESSION_CACHE if (sess) { log_dbg_print_free(ssl_session_to_str(sess)); } #endif /* DEBUG_SESSION_CACHE */ log_dbg_printf("SSL session cache: %s\n", sess ? "HIT" : "MISS"); return sess; } /* * Set SSL_CTX options that are the same for incoming and outgoing SSL_CTX. */ static void pxy_sslctx_setoptions(SSL_CTX * sslctx, tfe_config opts) { SSL_CTX_set_options(sslctx, SSL_OP_ALL); #ifdef SSL_OP_TLS_ROLLBACK_BUG SSL_CTX_set_options(sslctx, SSL_OP_TLS_ROLLBACK_BUG); #endif /* SSL_OP_TLS_ROLLBACK_BUG */ #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION SSL_CTX_set_options(sslctx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); #endif /* SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */ #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS SSL_CTX_set_options(sslctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); #endif /* SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ #ifdef SSL_OP_NO_TICKET SSL_CTX_set_options(sslctx, SSL_OP_NO_TICKET); #endif /* SSL_OP_NO_TICKET */ #ifdef SSL_OP_NO_SSLv2 #ifdef HAVE_SSLV2 if (opts->no_ssl2) { #endif /* HAVE_SSLV2 */ SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv2); #ifdef HAVE_SSLV2 } #endif /* HAVE_SSLV2 */ #endif /* !SSL_OP_NO_SSLv2 */ #ifdef HAVE_SSLV3 if (opts->no_ssl3) { SSL_CTX_set_options(sslctx, SSL_OP_NO_SSLv3); } #endif /* HAVE_SSLV3 */ #ifdef HAVE_TLSV10 if (opts->no_tls10) { SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1); } #endif /* HAVE_TLSV10 */ #ifdef HAVE_TLSV11 if (opts->no_tls11) { SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_1); } #endif /* HAVE_TLSV11 */ #ifdef HAVE_TLSV12 if (opts->no_tls12) { SSL_CTX_set_options(sslctx, SSL_OP_NO_TLSv1_2); } #endif /* HAVE_TLSV12 */ #ifdef SSL_OP_NO_COMPRESSION if (!opts->sslcomp) { SSL_CTX_set_options(sslctx, SSL_OP_NO_COMPRESSION); } #endif /* SSL_OP_NO_COMPRESSION */ SSL_CTX_set_cipher_list(sslctx, opts->ciphers); } /* * Create and set up a new SSL_CTX instance for terminating SSL. * Set up all the necessary callbacks, the certificate, the cert chain and key. */ static SSL_CTX * downstream_sslctx_create( pxy_conn_ctx_t * ctx, X509 * crt, STACK_OF(X509) * chain, EVP_PKEY * key, struct tfe_config* opts, void* cb_arg) { SSL_CTX * sslctx = SSL_CTX_new(opts->sslmethod()); if (!sslctx) return NULL; pxy_sslctx_setoptions(sslctx, opts); #if OPENSSL_VERSION_NUMBER >= 0x10100000L if (opts->sslversion) { if (SSL_CTX_set_min_proto_version(sslctx, opts->sslversion) == 0 || SSL_CTX_set_max_proto_version(sslctx, opts->sslversion) == 0) { SSL_CTX_free(sslctx); return NULL; } } #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ SSL_CTX_sess_set_new_cb(sslctx, pxy_ossl_sessnew_cb); SSL_CTX_sess_set_remove_cb(sslctx, pxy_ossl_sessremove_cb); SSL_CTX_sess_set_get_cb(sslctx, pxy_ossl_sessget_cb); SSL_CTX_set_session_cache_mode(sslctx, SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL); #ifdef USE_SSL_SESSION_ID_CONTEXT SSL_CTX_set_session_id_context(sslctx, (void *)(&ssl_session_context), sizeof(ssl_session_context)); #endif /* USE_SSL_SESSION_ID_CONTEXT */ #ifndef OPENSSL_NO_TLSEXT SSL_CTX_set_tlsext_servername_callback(sslctx, pxy_ossl_servername_cb); SSL_CTX_set_tlsext_servername_arg(sslctx, ctx); #endif /* !OPENSSL_NO_TLSEXT */ #ifndef OPENSSL_NO_DH if (ctx->opts->dh) { SSL_CTX_set_tmp_dh(sslctx, ctx->opts->dh); } else { SSL_CTX_set_tmp_dh_callback(sslctx, ssl_tmp_dh_callback); } #endif /* !OPENSSL_NO_DH */ #ifndef OPENSSL_NO_ECDH if (ctx->opts->ecdhcurve) { EC_KEY * ecdh = ssl_ec_by_name(ctx->opts->ecdhcurve); SSL_CTX_set_tmp_ecdh(sslctx, ecdh); EC_KEY_free(ecdh); } else { EC_KEY * ecdh = ssl_ec_by_name(NULL); SSL_CTX_set_tmp_ecdh(sslctx, ecdh); EC_KEY_free(ecdh); } #endif /* !OPENSSL_NO_ECDH */ SSL_CTX_use_certificate(sslctx, crt); SSL_CTX_use_PrivateKey(sslctx, key); for (int i = 0; i < sk_X509_num(chain); i++) { X509 * c = sk_X509_value(chain, i); ssl_x509_refcount_inc(c); /* next call consumes a reference */ SSL_CTX_add_extra_chain_cert(sslctx, c); } #ifdef DEBUG_SESSION_CACHE if (OPTS_DEBUG(ctx->opts)) { int mode = SSL_CTX_get_session_cache_mode(sslctx); log_dbg_printf("SSL session cache mode: %08x\n", mode); if (mode == SSL_SESS_CACHE_OFF) log_dbg_printf("SSL_SESS_CACHE_OFF\n"); if (mode & SSL_SESS_CACHE_CLIENT) log_dbg_printf("SSL_SESS_CACHE_CLIENT\n"); if (mode & SSL_SESS_CACHE_SERVER) log_dbg_printf("SSL_SESS_CACHE_SERVER\n"); if (mode & SSL_SESS_CACHE_NO_AUTO_CLEAR) log_dbg_printf("SSL_SESS_CACHE_NO_AUTO_CLEAR\n"); if (mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP) log_dbg_printf("SSL_SESS_CACHE_NO_INTERNAL_LOOKUP\n"); if (mode & SSL_SESS_CACHE_NO_INTERNAL_STORE) log_dbg_printf("SSL_SESS_CACHE_NO_INTERNAL_STORE\n"); } #endif /* DEBUG_SESSION_CACHE */ return sslctx; } static int pxy_srccert_write_to_gendir(pxy_conn_ctx_t * ctx, X509 * crt, int is_orig) { char * fn; int rv; if (!ctx->origcrtfpr) return -1; if (is_orig) { rv = asprintf(&fn, "%s/%s.crt", ctx->opts->certgendir, ctx->origcrtfpr); } else { if (!ctx->usedcrtfpr) return -1; rv = asprintf(&fn, "%s/%s-%s.crt", ctx->opts->certgendir, ctx->origcrtfpr, ctx->usedcrtfpr); } if (rv == -1) { ctx->enomem = 1; return -1; } free(fn); return rv; } static void pxy_srccert_write(pxy_conn_ctx_t * ctx) { if (ctx->opts->certgen_writeall || ctx->generated_cert) { if (pxy_srccert_write_to_gendir(ctx, SSL_get_certificate(ctx->src.ssl), 0) == -1) { log_err_printf("Failed to write used certificate\n"); } } if (ctx->opts->certgen_writeall) { if (pxy_srccert_write_to_gendir(ctx, ctx->origcrt, 1) == -1) { log_err_printf("Failed to write orig certificate\n"); } } } /* * Create new SSL context for the incoming connection, based on the original * destination SSL certificate. * Returns NULL if no suitable certificate could be found. */ static SSL * downstream_ssl_create(cert_t * cert, struct tfe_config* opts) { assert(cert!=NULL); SSL_CTX * sslctx = downstream_sslctx_create(ctx, cert->crt, cert->chain, cert->key); if (!sslctx) { return NULL; } SSL * ssl = SSL_new(sslctx); SSL_CTX_free(sslctx); /* SSL_new() increments refcount */ if (!ssl) { return NULL; } #ifdef SSL_MODE_RELEASE_BUFFERS /* lower memory footprint for idle connections */ SSL_set_mode(ssl, SSL_get_mode(ssl) | SSL_MODE_RELEASE_BUFFERS); #endif /* SSL_MODE_RELEASE_BUFFERS */ return ssl; } /* * OpenSSL servername callback, called when OpenSSL receives a servername * TLS extension in the clientHello. Must switch to a new SSL_CTX with * a different certificate if we want to replace the server cert here. * We generate a new certificate if the current one does not match the * supplied servername. This should only happen if the original destination * server supplies a certificate which does not match the server name we * indicate to it. */ static int pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg) { pxy_conn_ctx_t * ctx = (pxy_conn_ctx_t *) arg; const char * sn; X509 * sslcrt; if (!(sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) return SSL_TLSEXT_ERR_NOACK; if (!ctx->sni) { if (OPTS_DEBUG(ctx->opts)) { log_dbg_printf("Warning: SNI parser yielded no " "hostname, copying OpenSSL one: " "[NULL] != [%s]\n", sn); } ctx->sni = strdup(sn); if (!ctx->sni) { ctx->enomem = 1; return SSL_TLSEXT_ERR_NOACK; } } if (OPTS_DEBUG(ctx->opts)) { if (strcmp(sn, ctx->sni) != 0) { /* * This may happen if the client resumes a session, but * uses a different SNI hostname when resuming than it * used when the session was created. OpenSSL * correctly ignores the SNI in the ClientHello in this * case, but since we have already sent the SNI onwards * to the original destination, there is no way back. * We log an error and hope this never happens. */ log_dbg_printf("Warning: SNI parser yielded different " "hostname than OpenSSL callback for " "the same ClientHello message: " "[%s] != [%s]\n", ctx->sni, sn); } } /* generate a new certificate with sn as additional altSubjectName * and replace it both in the current SSL ctx and in the cert cache */ if (!ctx->immutable_cert && !ssl_x509_names_match((sslcrt = SSL_get_certificate(ssl)), sn)) { X509 * newcrt; SSL_CTX * newsslctx; if (OPTS_DEBUG(ctx->opts)) { log_dbg_printf("Certificate cache: UPDATE " "(SNI mismatch)\n"); } newcrt = ssl_x509_forge(ctx->opts->cacrt, ctx->opts->cakey, sslcrt, ctx->opts->key, sn, ctx->opts->crlurl); if (!newcrt) { ctx->enomem = 1; return SSL_TLSEXT_ERR_NOACK; } cachemgr_fkcrt_set(ctx->origcrt, newcrt); ctx->generated_cert = 1; if (OPTS_DEBUG(ctx->opts)) { log_dbg_printf("===> Updated forged server " "certificate:\n"); pxy_debug_crt(newcrt); } if (WANT_CONNECT_LOG(ctx)) { if (ctx->ssl_names) { free(ctx->ssl_names); } ctx->ssl_names = ssl_x509_names_to_str(newcrt); if (!ctx->ssl_names) { ctx->enomem = 1; } } if (WANT_CONNECT_LOG(ctx) || ctx->opts->certgendir) { if (ctx->usedcrtfpr) { free(ctx->usedcrtfpr); } ctx->usedcrtfpr = ssl_x509_fingerprint(newcrt, 0); if (!ctx->usedcrtfpr) { ctx->enomem = 1; } } newsslctx = downstream_sslctx_create(ctx, newcrt, ctx->opts->chain, ctx->opts->key); if (!newsslctx) { X509_free(newcrt); ctx->enomem = 1; return SSL_TLSEXT_ERR_NOACK; } SSL_set_SSL_CTX(ssl, newsslctx); /* decr's old incr new refc */ SSL_CTX_free(newsslctx); X509_free(newcrt); } else if (OPTS_DEBUG(ctx->opts)) { log_dbg_printf("Certificate cache: KEEP (SNI match or " "target mode)\n"); } return SSL_TLSEXT_ERR_OK; } /* * Create new SSL context for outgoing connections to the original destination. * If hostname sni is provided, use it for Server Name Indication. */ static SSL* upstream_ssl_create(const struct ssl_client_hello* client_hello, int sslversion, const char* sni, MESA_htable_handle* dsess_cache) { SSL_CTX* sslctx=NULL; SSL* ssl=NULL; SSL_SESSION* sess=NULL; sslctx = SSL_CTX_new(opts->sslmethod()); pxy_sslctx_setoptions(sslctx, opts); #if OPENSSL_VERSION_NUMBER >= 0x10100000L if (opts->sslversion) { if (SSL_CTX_set_min_proto_version(sslctx, opts->sslversion) == 0 || SSL_CTX_set_max_proto_version(sslctx, opts->sslversion) == 0) { SSL_CTX_free(sslctx); return NULL; } } #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ SSL_CTX_set_verify(sslctx, SSL_VERIFY_NONE, NULL); ssl = SSL_new(sslctx); SSL_CTX_free(sslctx); /* SSL_new() increments refcount */ if (!ssl) { return NULL; } #ifndef OPENSSL_NO_TLSEXT if (sni) { SSL_set_tlsext_host_name(ssl, sni); } #endif /* !OPENSSL_NO_TLSEXT */ #ifdef SSL_MODE_RELEASE_BUFFERS /* lower memory footprint for idle connections */ SSL_set_mode(ssl, SSL_get_mode(ssl) | SSL_MODE_RELEASE_BUFFERS); #endif /* SSL_MODE_RELEASE_BUFFERS */ //Todo: Refactor below session resumption. /* session resuming based on remote endpoint address and port */ sess = (SSL_SESSION *) cachemgr_dsess_get((struct sockaddr *) &ctx->addr, ctx->addrlen, ctx->sni); /* new sess inst */ if (sess) { if (OPTS_DEBUG(opts)) { log_dbg_printf("Attempt reuse dst SSL session\n"); } SSL_set_session(ssl, sess); /* increments sess refcount */ SSL_SESSION_free(sess); } return ssl; } /* struct ssl_upstream* ssl_upstream_create(tfe_config*opts, const char* sni) { struct ssl_upstream* upstream=NULL; SSL* ssl=NULL; ssl=upstream_ssl_create(opts,sni); if(ssl==NULL) { return NULL; } upstream=ALLOC(struct ssl_upstream,1); upstream->ssl=ssl; return upstream; }*/ void ssl_upstream_free(struct ssl_upstream* p) { X509_free(p->orig_cert); //todo close ssl with a callback. //SSL_free(ctx->ssl); } struct ssl_downstream* ssl_downstream_create(void) { struct ssl_downstream* p=NULL; p=ALLOC(struct ssl_downstream, 1); return p; } void ssl_downstream_free(struct ssl_downstream* p) { free(p->sni); p->sni=NULL; // X509_free(p->fake_cert); }