diff --git a/pkg/vere/io/http.c b/pkg/vere/io/http.c index 9ff3fa4e81..ccded20392 100644 --- a/pkg/vere/io/http.c +++ b/pkg/vere/io/http.c @@ -82,14 +82,21 @@ typedef struct _u3_h2o_serv { struct _u3_httd* htd_u; // device backpointer } u3_http; + typedef struct _u3_cert { + u3_noun tuf; // domain (as turf, %$ == *) + uv_buf_t key_u; // PEM RSA private key + uv_buf_t cer_u; // PEM certificate chain + SSL_CTX* ctx_u; // domain-specific SSL context + struct _u3_cert* nex_u; + } u3_cert; + /* u3_form: http config from %eyre */ typedef struct _u3_form { c3_o pro; // proxy c3_o log; // keep access log c3_o red; // redirect to HTTPS - uv_buf_t key_u; // PEM RSA private key - uv_buf_t cer_u; // PEM certificate chain + struct _u3_cert* cer_u; // available certs } u3_form; /* u3_hfig: general http configuration @@ -114,7 +121,7 @@ typedef struct _u3_httd { SSL_CTX* tls_u; // server SSL_CTX* u3p(u3h_root) sax_p; // url->scry cache u3p(u3h_root) nax_p; // scry->noun cache - u3t_spin* stk_u; // spin stack + u3t_spin* stk_u; // spin stack } u3_httd; static u3_weak _http_rec_to_httq(h2o_req_t* rec_u); @@ -502,7 +509,7 @@ _http_spin_unlink(u3_hreq* req_u) if ( 0 != req_u->nex_u ) { req_u->nex_u->pre_u = 0; - } + } else if (req_u->tim_u != NULL) { uv_timer_stop(req_u->tim_u); } @@ -977,7 +984,7 @@ _http_peek_dispatch(u3_hreq* req_u, beam* bem_u, u3_noun gang, u3_noun spur) u3_noun key = u3nc(auth_o, u3k(bam)); u3_weak nac = u3h_get(htd_u->nax_p, key); u3z(key); - + if ( (u3_none == nac) || ((u3_nul == gang) && (c3y == u3r_at(14, nac))) ) { @@ -1062,7 +1069,7 @@ _http_req_dispatch(u3_hreq* req_u, u3_noun req) u3_noun gang = ( _(_http_req_is_auth(fig_u, rec_u)) ) ? u3nc(u3_nul, u3_nul) : u3_nul; - + h2o_headers_t req_headers = req_u->rec_u->headers; byte_range rng_u; c3_o rng_o = _get_range(req_headers, &rng_u); @@ -1228,7 +1235,7 @@ _http_req_cache(u3_hreq* req_u) u3_noun url = u3dc("scot", 't', _http_vec_to_atom(req_u->rec_u->path)); u3_weak sac = u3h_get(htd_u->sax_p, url); u3z(url); - + if ( u3_none == sac ) { return c3n; } @@ -1450,14 +1457,14 @@ _http_continue_respond(u3_hreq* req_u, u3z(data); u3z(complete); return; } - + u3_hgen* gen_u = req_u->gen_u; - + if ( u3_hgen_done == gen_u->sat_e ) { u3z(data); u3z(complete); return; } - + uv_timer_stop(req_u->tim_u); gen_u->sat_e = ( c3y == complete ) ? u3_hgen_done : u3_hgen_wait; @@ -1584,7 +1591,7 @@ static c3_i _http_spin_accept(h2o_handler_t* han_u, h2o_req_t* rec_u) { u3_hcon* hon_u = _http_rec_sock(rec_u); - + if ( NULL == hon_u->htp_u->htd_u->stk_u ) { u3_hreq* req_u = _http_req_prepare(rec_u, _http_req_new); req_u->sat_e = u3_rsat_plan; @@ -2356,13 +2363,67 @@ _http_wain_to_buf(u3_noun wan) return uv_buf_init(buf_c, len_w); } +static c3_o +_http_match_turf(const c3_c* sen_c, u3_noun tuf) +{ + u3_noun fut = u3kb_flop(u3k(tuf)); // (list @t) + + while (u3_nul != fut) { + c3_s len_s = 0; + while ( '.' != *(sen_c + len_s) && + 0 != *(sen_c + len_s) ) + { + len_s++; + } + + u3_noun seg = u3i_bytes(len_s, (c3_y*)sen_c); + sen_c = sen_c + len_s + 1; + + if (u3_nul == u3h(fut) || 0 == u3r_comp(u3h(fut), seg)) { + fut = u3t(fut); + continue; + } + else { + return c3n; + } + } + if ( 0 == *(sen_c-1) ) { + return c3y; + } + return c3n; +} + +static int +_http_sni_cb(SSL* ssl_u, int* ad, void* arg) +{ + u3_httd* htd_u = (u3_httd*)arg; + u3_cert* cer_u = htd_u->fig_u.for_u->cer_u; + + const c3_c* sen_c = SSL_get_servername(ssl_u, TLSEXT_NAMETYPE_host_name); + if ( 0 != sen_c ) { + while ( 0 != cer_u ) { + if ( c3y == _http_match_turf(sen_c, cer_u->tuf) ) { + // swap out context to use matching cert + // + SSL_set_SSL_CTX(ssl_u, cer_u->ctx_u); + return SSL_TLSEXT_ERR_OK; + } + cer_u = cer_u->nex_u; + } + } + + // retained default context + // + return SSL_TLSEXT_ERR_OK; +} + /* _http_init_tls: initialize OpenSSL context */ static SSL_CTX* _http_init_tls(uv_buf_t key_u, uv_buf_t cer_u) { - // XX require 1.1.0 and use TLS_server_method() - SSL_CTX* tls_u = SSL_CTX_new(SSLv23_server_method()); + // XX require 1.1.0 and use TLS_server_method() //TODO that compiles. just do it? + SSL_CTX* tls_u = SSL_CTX_new(TLS_server_method()); // XX use SSL_CTX_set_max_proto_version() and SSL_CTX_set_min_proto_version() SSL_CTX_set_options(tls_u, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | @@ -2435,6 +2496,21 @@ _http_init_tls(uv_buf_t key_u, uv_buf_t cer_u) return tls_u; } +/* _http_init_bootstrap_tls: initialize default OpenSSL context +*/ +static SSL_CTX* +_http_init_bootstrap_tls(u3_httd* htd_u, uv_buf_t key_u, uv_buf_t cer_u) +{ + SSL_CTX* ssl_ctx = _http_init_tls(key_u, cer_u); + + // install the SNI callback on the default SSL_CTX + // + SSL_CTX_set_tlsext_servername_callback(ssl_ctx, _http_sni_cb); + SSL_CTX_set_tlsext_servername_arg(ssl_ctx, (void*)htd_u); + + return ssl_ctx; +} + /* _http_write_ports_file(): update .http.ports */ static void @@ -2531,6 +2607,7 @@ _http_serv_start_all(u3_httd* htd_u) u3_noun non = u3_none; u3_noun dis; u3_form* for_u = htd_u->fig_u.for_u; + u3_cert* cer_u = for_u->cer_u; u3_assert( 0 != for_u ); @@ -2538,8 +2615,13 @@ _http_serv_start_all(u3_httd* htd_u) htd_u->tls_u = 0; // HTTPS server. - if ( (0 != for_u->key_u.base) && (0 != for_u->cer_u.base) ) { - htd_u->tls_u = _http_init_tls(for_u->key_u, for_u->cer_u); + if ( 0 != cer_u ) { + // use lowest priority / "most general" cert for bootstrap tls + // + while ( 0 != cer_u->nex_u ) { + cer_u = cer_u->nex_u; + } + htd_u->tls_u = _http_init_bootstrap_tls(htd_u, cer_u->key_u, cer_u->cer_u); // Note: if tls_u is used for additional servers, // its reference count must be incremented with SSL_CTX_up_ref @@ -2602,7 +2684,6 @@ _http_serv_start_all(u3_httd* htd_u) } _http_write_ports_file(htd_u, u3_Host.dir_c); - _http_form_free(htd_u); } /* _http_serv_restart(): gracefully shutdown, then start servers. @@ -2620,6 +2701,7 @@ _http_serv_restart(u3_httd* htd_u) while ( 0 != htp_u ) { if ( c3y == htp_u->liv ) { + //NOTE last one that closes ends up calling _http_serv_start_all _http_serv_close(htp_u); } htp_u = htp_u->nex_u; @@ -2640,12 +2722,15 @@ _http_form_free(u3_httd* htd_u) return; } - if ( 0 != for_u->key_u.base ) { - c3_free(for_u->key_u.base); - } + while ( 0 != for_u->cer_u ) { + u3_cert* old_u = for_u->cer_u; + for_u->cer_u = old_u->nex_u; - if ( 0 != for_u->cer_u.base ) { - c3_free(for_u->cer_u.base); + u3z(old_u->tuf); + c3_free(old_u->key_u.base); + c3_free(old_u->cer_u.base); + SSL_CTX_free(old_u->ctx_u); + c3_free(old_u); } c3_free(for_u); @@ -2670,10 +2755,18 @@ u3_http_ef_form(u3_httd* htd_u, u3_noun fig) u3_noun sec, pro, log, red; if ( (c3n == u3r_qual(fig, &sec, &pro, &log, &red) ) || - // confirm sec is a valid (unit ^) - !( u3_nul == sec || ( c3y == u3du(sec) && - c3y == u3du(u3t(sec)) && - u3_nul == u3h(sec) ) ) || + // confirm sec is either (unit ^) or (list [* ^]) + !( // empty + u3_nul == sec + // unit value (old style) + || ( c3y == u3du(sec) && + c3y == u3du(u3t(sec)) && + u3_nul == u3h(sec) ) + // non-empty list (new style) + || ( c3y == u3du(sec) && + c3y == u3du(u3h(sec)) && + c3y == u3du(u3t(u3h(sec))) ) + ) || // confirm valid flags ("loobeans") !( c3y == pro || c3n == pro ) || !( c3y == log || c3n == log ) || @@ -2687,17 +2780,57 @@ u3_http_ef_form(u3_httd* htd_u, u3_noun fig) for_u->pro = (c3_o)pro; for_u->log = (c3_o)log; for_u->red = (c3_o)red; + for_u->cer_u = 0; if ( u3_nul != sec ) { - u3_noun key = u3h(u3t(sec)); - u3_noun cer = u3t(u3t(sec)); + // (unit ^) case + // + if (u3_nul == u3h(sec)) { + u3_cert* cer_u = c3_malloc(sizeof(*cer_u)); + + u3_noun key = u3h(u3t(sec)); + u3_noun cer = u3t(u3t(sec)); + + cer_u->key_u = _http_wain_to_buf(u3k(key)); + cer_u->cer_u = _http_wain_to_buf(u3k(cer)); + cer_u->ctx_u = _http_init_tls(cer_u->key_u, cer_u->cer_u); + cer_u->nex_u = 0; + + for_u->cer_u = cer_u; + } + // (list [* ^]) case + // + else { + sec = u3kb_flop(u3k(sec)); + while (u3_nul != sec) { + u3_cert* cer_u = c3_malloc(sizeof(*cer_u)); + + u3_noun nod = u3h(sec); + u3_noun key = u3h(u3t(nod)); + u3_noun cer = u3t(u3t(nod)); + + cer_u->tuf = u3k(u3h(nod)); + cer_u->key_u = _http_wain_to_buf(u3k(key)); + cer_u->cer_u = _http_wain_to_buf(u3k(cer)); + cer_u->ctx_u = _http_init_tls(cer_u->key_u, cer_u->cer_u); + cer_u->nex_u = 0; + + if (0 == for_u->cer_u) { + for_u->cer_u = cer_u; + } + else { + cer_u->nex_u = for_u->cer_u; + for_u->cer_u = cer_u; + } - for_u->key_u = _http_wain_to_buf(u3k(key)); - for_u->cer_u = _http_wain_to_buf(u3k(cer)); + u3z(nod); + sec = u3t(sec); + } + u3z(sec); + } } else { - for_u->key_u = uv_buf_init(0, 0); - for_u->cer_u = uv_buf_init(0, 0); + for_u->cer_u = 0; } u3z(fig); @@ -2737,7 +2870,7 @@ _http_io_talk(u3_auto* car_u) //Setup spin stack htd_u->stk_u = u3t_sstack_open(); - + if ( NULL == htd_u->stk_u ) { u3l_log("http.c: failed to open spin stack"); } @@ -2891,7 +3024,7 @@ _http_stream_slog(void* vop_p, c3_w pri_w, u3_noun tan) */ static void _http_spin_timer_cb(uv_timer_t* tim_u) -{ +{ u3_httd* htd_u = tim_u->data; u3_hreq* siq_u = htd_u->fig_u.siq_u; @@ -2942,7 +3075,7 @@ _http_spin_timer_cb(uv_timer_t* tim_u) _http_continue_respond(siq_u, u3k(dat), c3n); siq_u = siq_u->nex_u; } - u3z(dat); u3z(lin); + u3z(dat); u3z(lin); } uv_timer_start(htd_u->fig_u.sin_u, _http_spin_timer_cb, SPIN_TIMER, 0);