diff --git a/mongoose.c b/mongoose.c index 616e3f1e..1cd0b4bb 100644 --- a/mongoose.c +++ b/mongoose.c @@ -11112,6 +11112,8 @@ enum mg_tls_hs_state { // Server state machine: MG_TLS_STATE_SERVER_START, // Wait for ClientHello + MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate + MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish MG_TLS_STATE_SERVER_CONNECTED // Done }; @@ -11147,14 +11149,15 @@ struct tls_data { uint8_t x25519_cli[32]; // client X25519 key between the handshake states uint8_t x25519_sec[32]; // x25519 secret between the handshake states - int skip_verification; // perform checks on server certificate? - int cert_requested; // client received a CertificateRequest? + bool skip_verification; // do not perform checks on server certificate + bool cert_requested; // client received a CertificateRequest + bool is_twoway; // server is configured to authenticate clients struct mg_str cert_der; // certificate in DER format struct mg_str ca_der; // CA certificate uint8_t ec_key[32]; // EC private key - char hostname[254]; // server hostname (client extension) + char hostname[254]; // matching hostname - int is_ec_pubkey; // EC or RSA? + bool is_ec_pubkey; // EC or RSA uint8_t pubkey[512 + 16]; // server EC (64) or RSA (512+exp) public key to // verify cert size_t pubkeysz; // size of the server public key @@ -11600,18 +11603,18 @@ static int mg_tls_recv_record(struct mg_connection *c) { } static void mg_tls_calc_cert_verify_hash(struct mg_connection *c, - uint8_t hash[32], int is_client) { + uint8_t hash[32], bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; - uint8_t server_context[34] = "TLS 1.3, server CertificateVerify"; - uint8_t client_context[34] = "TLS 1.3, client CertificateVerify"; uint8_t sig_content[130]; mg_sha256_ctx sha256; memset(sig_content, 0x20, 64); if (is_client) { - memmove(sig_content + 64, client_context, sizeof(client_context)); + uint8_t client_context[34] = "TLS 1.3, client CertificateVerify"; + memcpy(sig_content + 64, client_context, sizeof(client_context)); } else { - memmove(sig_content + 64, server_context, sizeof(server_context)); + uint8_t server_context[34] = "TLS 1.3, server CertificateVerify"; + memcpy(sig_content + 64, server_context, sizeof(server_context)); } memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); @@ -11753,17 +11756,46 @@ static void mg_tls_server_send_ext(struct mg_connection *c) { mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE); } -static void mg_tls_server_send_cert(struct mg_connection *c) { +// signature algorithms we actually support: +// rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256 +static const uint8_t secp256r1_sig_algs[12] = { + 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, +}; + +static void mg_tls_server_send_cert_request(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; - int send_ca = !c->is_client && tls->ca_der.len > 0; - // server DER certificate + CA (optional) + size_t n = sizeof(secp256r1_sig_algs) + 6; + uint8_t *req = (uint8_t *) mg_calloc(1, 13 + n); + if (req == NULL) { + mg_error(c, "tls cert req oom"); + return; + } + req[0] = MG_TLS_CERTIFICATE_REQUEST; // handshake header + MG_STORE_BE24(req + 1, n + 9); + req[4] = 0; // context length + MG_STORE_BE16(req + 5, n); // extensions length + MG_STORE_BE16(req + 7, 13); // "signature algorithms" + MG_STORE_BE16(req + 9, sizeof(secp256r1_sig_algs) + 2); // length + MG_STORE_BE16( + req + 11, + sizeof(secp256r1_sig_algs)); // signature hash algorithms length + memcpy(req + 13, (uint8_t *) secp256r1_sig_algs, sizeof(secp256r1_sig_algs)); + mg_sha256_update(&tls->sha256, req, 13 + n); + mg_tls_encrypt(c, req, 13 + n, MG_TLS_HANDSHAKE); + mg_free(req); +} + +static void mg_tls_send_cert(struct mg_connection *c, bool is_client) { + struct tls_data *tls = (struct tls_data *) c->tls; + int send_ca = !is_client && tls->ca_der.len > 0; + // DER certificate + CA (server optional) size_t n = tls->cert_der.len + (send_ca ? tls->ca_der.len + 5 : 0); uint8_t *cert = (uint8_t *) mg_calloc(1, 13 + n); if (cert == NULL) { mg_error(c, "tls cert oom"); return; } - cert[0] = 0x0b; // handshake header + cert[0] = MG_TLS_CERTIFICATE; // handshake header MG_STORE_BE24(cert + 1, n + 9); cert[4] = 0; // request context MG_STORE_BE24(cert + 5, n + 5); // 3 bytes: cert (s) length @@ -11806,7 +11838,7 @@ static void finish_SHA256(const MG_UECC_HashContext *base, mg_sha256_final(hash_result, &c->ctx); } -static void mg_tls_send_cert_verify(struct mg_connection *c, int is_client) { +static void mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; // server certificate verify packet uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00}; @@ -11883,12 +11915,9 @@ static void mg_tls_client_send_hello(struct mg_connection *c) { uint8_t x25519_pub[X25519_BYTES]; - // signature algorithms we actually support: - // rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256 - uint8_t secp256r1_sig_algs[12] = { - 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, - }; - // all popular signature algorithms (if we don't care about verification) + // - "signature algorithms we actually support", see above + // uint8_t secp256r1_sig_algs[] + // - all popular signature algorithms (if we don't care about verification) uint8_t all_sig_algs[34] = { 0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, @@ -11932,7 +11961,8 @@ static void mg_tls_client_send_hello(struct mg_connection *c) { const char *hostname = tls->hostname; size_t hostnamesz = strlen(tls->hostname); size_t hostname_extsz = hostnamesz ? hostnamesz + 9 : 0; - uint8_t *sig_alg = tls->skip_verification ? all_sig_algs : secp256r1_sig_algs; + uint8_t *sig_alg = + tls->skip_verification ? all_sig_algs : (uint8_t *) secp256r1_sig_algs; size_t sig_alg_sz = tls->skip_verification ? sizeof(all_sig_algs) : sizeof(secp256r1_sig_algs); @@ -12307,7 +12337,7 @@ static int mg_tls_verify_cert_cn(struct mg_der_tlv *subj, const char *host) { return matched; } -static int mg_tls_client_recv_cert(struct mg_connection *c) { +static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; unsigned char *recv_buf; @@ -12325,7 +12355,8 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { } if (recv_buf[0] != MG_TLS_CERTIFICATE) { - mg_error(c, "expected server certificate but got msg 0x%02x", recv_buf[0]); + mg_error(c, "expected %s certificate but got msg 0x%02x", + is_client ? "server" : "client", recv_buf[0]); return -1; } @@ -12335,7 +12366,7 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { } { - // Normally, there are 2-3 certs in a chain + // Normally, there are 2-3 certs in a chain (when is_client) struct mg_tls_cert certs[8]; int certnum = 0; uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1); @@ -12380,9 +12411,10 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { } if (ci == certs) { - // First certificate in the chain is peer cert, check SAN and store - // public key for further CertVerify step - if (mg_tls_verify_cert_san(cert, certsz, tls->hostname) <= 0 && + // First certificate in the chain is peer cert, check SAN if requested, + // and store public key for further CertVerify step + if (tls->hostname != NULL && *tls->hostname != '\0' && + mg_tls_verify_cert_san(cert, certsz, tls->hostname) <= 0 && mg_tls_verify_cert_cn(&ci->subj, tls->hostname) <= 0) { mg_error(c, "failed to verify hostname"); return -1; @@ -12413,7 +12445,7 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { !mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) { mg_error(c, "failed to verify CA"); return -1; - } else { + } else if (is_client) { MG_VERBOSE( ("CA was not in the chain, but verification with builtin CA " "passed")); @@ -12421,11 +12453,11 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { } } mg_tls_drop_message(c); - mg_tls_calc_cert_verify_hash(c, tls->sighash, 0); + mg_tls_calc_cert_verify_hash(c, tls->sighash, !is_client); return 0; } -static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { +static int mg_tls_recv_cert_verify(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; unsigned char *recv_buf; if (mg_tls_recv_record(c) < 0) { @@ -12433,8 +12465,8 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { } recv_buf = &c->rtls.buf[tls->recv_offset]; if (recv_buf[0] != MG_TLS_CERTIFICATE_VERIFY) { - mg_error(c, "expected server certificate verify but got msg 0x%02x", - recv_buf[0]); + mg_error(c, "expected %s certificate verify but got msg 0x%02x", + c->is_client ? "server" : "client", recv_buf[0]); return -1; } if (tls->recv_len < 8) { @@ -12443,7 +12475,7 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { return -1; } - // Ignore CertificateVerify is strict checks are not required + // Ignore CertificateVerify if strict checks are not required if (tls->skip_verification) { mg_tls_drop_message(c); return 0; @@ -12574,13 +12606,13 @@ static void mg_tls_client_handshake(struct mg_connection *c) { tls->state = MG_TLS_STATE_CLIENT_WAIT_CERT; // Fallthrough case MG_TLS_STATE_CLIENT_WAIT_CERT: - if (mg_tls_client_recv_cert(c) < 0) { + if (mg_tls_recv_cert(c, true) < 0) { break; } tls->state = MG_TLS_STATE_CLIENT_WAIT_CV; // Fallthrough case MG_TLS_STATE_CLIENT_WAIT_CV: - if (mg_tls_client_recv_cert_verify(c) < 0) { + if (mg_tls_recv_cert_verify(c) < 0) { break; } tls->state = MG_TLS_STATE_CLIENT_WAIT_FINISH; @@ -12589,28 +12621,19 @@ static void mg_tls_client_handshake(struct mg_connection *c) { if (mg_tls_client_recv_finish(c) < 0) { break; } - if (tls->cert_requested) { - /* for mTLS we should generate application keys at this point - * but then restore handshake keys and continue with - * the rest of the handshake */ - struct tls_enc app_keys; - struct tls_enc hs_keys = tls->enc; - mg_tls_generate_application_keys(c); - app_keys = tls->enc; - tls->enc = hs_keys; - mg_tls_server_send_cert(c); - mg_tls_send_cert_verify(c, 1); - mg_tls_client_send_finish(c); - tls->enc = app_keys; - } else { - mg_tls_client_send_finish(c); - mg_tls_generate_application_keys(c); + if (tls->cert_requested && tls->cert_der.len > 0) { // two-way auth + mg_tls_send_cert(c, true); + mg_tls_send_cert_verify(c, true); } + mg_tls_client_send_finish(c); + mg_tls_generate_application_keys(c); tls->state = MG_TLS_STATE_CLIENT_CONNECTED; c->is_tls_hs = 0; mg_call(c, MG_EV_TLS_HS, NULL); break; - default: mg_error(c, "unexpected client state: %d", tls->state); break; + default: + mg_error(c, "unexpected client state: %d", tls->state); + break; } } @@ -12624,9 +12647,14 @@ static void mg_tls_server_handshake(struct mg_connection *c) { mg_tls_server_send_hello(c); mg_tls_generate_handshake_keys(c); mg_tls_server_send_ext(c); - mg_tls_server_send_cert(c); - mg_tls_send_cert_verify(c, 0); + if (tls->is_twoway) mg_tls_server_send_cert_request(c); + mg_tls_send_cert(c, false); + mg_tls_send_cert_verify(c, false); mg_tls_server_send_finish(c); + if (tls->is_twoway) { + tls->state = MG_TLS_STATE_SERVER_WAIT_CERT; + break; + } tls->state = MG_TLS_STATE_SERVER_NEGOTIATED; // fallthrough case MG_TLS_STATE_SERVER_NEGOTIATED: @@ -12637,7 +12665,17 @@ static void mg_tls_server_handshake(struct mg_connection *c) { tls->state = MG_TLS_STATE_SERVER_CONNECTED; c->is_tls_hs = 0; return; - default: mg_error(c, "unexpected server state: %d", tls->state); break; + case MG_TLS_STATE_SERVER_WAIT_CERT: + if (mg_tls_recv_cert(c, false) < 0) break; + tls->state = MG_TLS_STATE_SERVER_WAIT_CV; + // Fallthrough + case MG_TLS_STATE_SERVER_WAIT_CV: + if (mg_tls_recv_cert_verify(c) < 0) break; + tls->state = MG_TLS_STATE_SERVER_NEGOTIATED; + break; + default: + mg_error(c, "unexpected server state: %d", tls->state); + break; } } @@ -12724,6 +12762,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { MG_ERROR(("Failed to load certificate")); return; } + if (!c->is_client) tls->is_twoway = true; // server + CA: two-way auth } if (opts->cert.buf == NULL) { diff --git a/src/tls_builtin.c b/src/tls_builtin.c index 25a7ccff..864e10a0 100644 --- a/src/tls_builtin.c +++ b/src/tls_builtin.c @@ -42,6 +42,8 @@ enum mg_tls_hs_state { // Server state machine: MG_TLS_STATE_SERVER_START, // Wait for ClientHello + MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate + MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish MG_TLS_STATE_SERVER_CONNECTED // Done }; @@ -77,14 +79,15 @@ struct tls_data { uint8_t x25519_cli[32]; // client X25519 key between the handshake states uint8_t x25519_sec[32]; // x25519 secret between the handshake states - int skip_verification; // perform checks on server certificate? - int cert_requested; // client received a CertificateRequest? + bool skip_verification; // do not perform checks on server certificate + bool cert_requested; // client received a CertificateRequest + bool is_twoway; // server is configured to authenticate clients struct mg_str cert_der; // certificate in DER format struct mg_str ca_der; // CA certificate uint8_t ec_key[32]; // EC private key - char hostname[254]; // server hostname (client extension) + char hostname[254]; // matching hostname - int is_ec_pubkey; // EC or RSA? + bool is_ec_pubkey; // EC or RSA uint8_t pubkey[512 + 16]; // server EC (64) or RSA (512+exp) public key to // verify cert size_t pubkeysz; // size of the server public key @@ -530,18 +533,18 @@ static int mg_tls_recv_record(struct mg_connection *c) { } static void mg_tls_calc_cert_verify_hash(struct mg_connection *c, - uint8_t hash[32], int is_client) { + uint8_t hash[32], bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; - uint8_t server_context[34] = "TLS 1.3, server CertificateVerify"; - uint8_t client_context[34] = "TLS 1.3, client CertificateVerify"; uint8_t sig_content[130]; mg_sha256_ctx sha256; memset(sig_content, 0x20, 64); if (is_client) { - memmove(sig_content + 64, client_context, sizeof(client_context)); + uint8_t client_context[34] = "TLS 1.3, client CertificateVerify"; + memcpy(sig_content + 64, client_context, sizeof(client_context)); } else { - memmove(sig_content + 64, server_context, sizeof(server_context)); + uint8_t server_context[34] = "TLS 1.3, server CertificateVerify"; + memcpy(sig_content + 64, server_context, sizeof(server_context)); } memmove(&sha256, &tls->sha256, sizeof(mg_sha256_ctx)); @@ -683,17 +686,46 @@ static void mg_tls_server_send_ext(struct mg_connection *c) { mg_tls_encrypt(c, ext, sizeof(ext), MG_TLS_HANDSHAKE); } -static void mg_tls_server_send_cert(struct mg_connection *c) { +// signature algorithms we actually support: +// rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256 +static const uint8_t secp256r1_sig_algs[12] = { + 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, +}; + +static void mg_tls_server_send_cert_request(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; - int send_ca = !c->is_client && tls->ca_der.len > 0; - // server DER certificate + CA (optional) + size_t n = sizeof(secp256r1_sig_algs) + 6; + uint8_t *req = (uint8_t *) mg_calloc(1, 13 + n); + if (req == NULL) { + mg_error(c, "tls cert req oom"); + return; + } + req[0] = MG_TLS_CERTIFICATE_REQUEST; // handshake header + MG_STORE_BE24(req + 1, n + 9); + req[4] = 0; // context length + MG_STORE_BE16(req + 5, n); // extensions length + MG_STORE_BE16(req + 7, 13); // "signature algorithms" + MG_STORE_BE16(req + 9, sizeof(secp256r1_sig_algs) + 2); // length + MG_STORE_BE16( + req + 11, + sizeof(secp256r1_sig_algs)); // signature hash algorithms length + memcpy(req + 13, (uint8_t *) secp256r1_sig_algs, sizeof(secp256r1_sig_algs)); + mg_sha256_update(&tls->sha256, req, 13 + n); + mg_tls_encrypt(c, req, 13 + n, MG_TLS_HANDSHAKE); + mg_free(req); +} + +static void mg_tls_send_cert(struct mg_connection *c, bool is_client) { + struct tls_data *tls = (struct tls_data *) c->tls; + int send_ca = !is_client && tls->ca_der.len > 0; + // DER certificate + CA (server optional) size_t n = tls->cert_der.len + (send_ca ? tls->ca_der.len + 5 : 0); uint8_t *cert = (uint8_t *) mg_calloc(1, 13 + n); if (cert == NULL) { mg_error(c, "tls cert oom"); return; } - cert[0] = 0x0b; // handshake header + cert[0] = MG_TLS_CERTIFICATE; // handshake header MG_STORE_BE24(cert + 1, n + 9); cert[4] = 0; // request context MG_STORE_BE24(cert + 5, n + 5); // 3 bytes: cert (s) length @@ -736,7 +768,7 @@ static void finish_SHA256(const MG_UECC_HashContext *base, mg_sha256_final(hash_result, &c->ctx); } -static void mg_tls_send_cert_verify(struct mg_connection *c, int is_client) { +static void mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; // server certificate verify packet uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00}; @@ -813,12 +845,9 @@ static void mg_tls_client_send_hello(struct mg_connection *c) { uint8_t x25519_pub[X25519_BYTES]; - // signature algorithms we actually support: - // rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256 - uint8_t secp256r1_sig_algs[12] = { - 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, - }; - // all popular signature algorithms (if we don't care about verification) + // - "signature algorithms we actually support", see above + // uint8_t secp256r1_sig_algs[] + // - all popular signature algorithms (if we don't care about verification) uint8_t all_sig_algs[34] = { 0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, @@ -862,7 +891,8 @@ static void mg_tls_client_send_hello(struct mg_connection *c) { const char *hostname = tls->hostname; size_t hostnamesz = strlen(tls->hostname); size_t hostname_extsz = hostnamesz ? hostnamesz + 9 : 0; - uint8_t *sig_alg = tls->skip_verification ? all_sig_algs : secp256r1_sig_algs; + uint8_t *sig_alg = + tls->skip_verification ? all_sig_algs : (uint8_t *) secp256r1_sig_algs; size_t sig_alg_sz = tls->skip_verification ? sizeof(all_sig_algs) : sizeof(secp256r1_sig_algs); @@ -1237,7 +1267,7 @@ static int mg_tls_verify_cert_cn(struct mg_der_tlv *subj, const char *host) { return matched; } -static int mg_tls_client_recv_cert(struct mg_connection *c) { +static int mg_tls_recv_cert(struct mg_connection *c, bool is_client) { struct tls_data *tls = (struct tls_data *) c->tls; unsigned char *recv_buf; @@ -1255,7 +1285,8 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { } if (recv_buf[0] != MG_TLS_CERTIFICATE) { - mg_error(c, "expected server certificate but got msg 0x%02x", recv_buf[0]); + mg_error(c, "expected %s certificate but got msg 0x%02x", + is_client ? "server" : "client", recv_buf[0]); return -1; } @@ -1265,7 +1296,7 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { } { - // Normally, there are 2-3 certs in a chain + // Normally, there are 2-3 certs in a chain (when is_client) struct mg_tls_cert certs[8]; int certnum = 0; uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1); @@ -1310,9 +1341,10 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { } if (ci == certs) { - // First certificate in the chain is peer cert, check SAN and store - // public key for further CertVerify step - if (mg_tls_verify_cert_san(cert, certsz, tls->hostname) <= 0 && + // First certificate in the chain is peer cert, check SAN if requested, + // and store public key for further CertVerify step + if (tls->hostname != NULL && *tls->hostname != '\0' && + mg_tls_verify_cert_san(cert, certsz, tls->hostname) <= 0 && mg_tls_verify_cert_cn(&ci->subj, tls->hostname) <= 0) { mg_error(c, "failed to verify hostname"); return -1; @@ -1343,7 +1375,7 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { !mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) { mg_error(c, "failed to verify CA"); return -1; - } else { + } else if (is_client) { MG_VERBOSE( ("CA was not in the chain, but verification with builtin CA " "passed")); @@ -1351,11 +1383,11 @@ static int mg_tls_client_recv_cert(struct mg_connection *c) { } } mg_tls_drop_message(c); - mg_tls_calc_cert_verify_hash(c, tls->sighash, 0); + mg_tls_calc_cert_verify_hash(c, tls->sighash, !is_client); return 0; } -static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { +static int mg_tls_recv_cert_verify(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; unsigned char *recv_buf; if (mg_tls_recv_record(c) < 0) { @@ -1363,8 +1395,8 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { } recv_buf = &c->rtls.buf[tls->recv_offset]; if (recv_buf[0] != MG_TLS_CERTIFICATE_VERIFY) { - mg_error(c, "expected server certificate verify but got msg 0x%02x", - recv_buf[0]); + mg_error(c, "expected %s certificate verify but got msg 0x%02x", + c->is_client ? "server" : "client", recv_buf[0]); return -1; } if (tls->recv_len < 8) { @@ -1373,7 +1405,7 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) { return -1; } - // Ignore CertificateVerify is strict checks are not required + // Ignore CertificateVerify if strict checks are not required if (tls->skip_verification) { mg_tls_drop_message(c); return 0; @@ -1504,13 +1536,13 @@ static void mg_tls_client_handshake(struct mg_connection *c) { tls->state = MG_TLS_STATE_CLIENT_WAIT_CERT; // Fallthrough case MG_TLS_STATE_CLIENT_WAIT_CERT: - if (mg_tls_client_recv_cert(c) < 0) { + if (mg_tls_recv_cert(c, true) < 0) { break; } tls->state = MG_TLS_STATE_CLIENT_WAIT_CV; // Fallthrough case MG_TLS_STATE_CLIENT_WAIT_CV: - if (mg_tls_client_recv_cert_verify(c) < 0) { + if (mg_tls_recv_cert_verify(c) < 0) { break; } tls->state = MG_TLS_STATE_CLIENT_WAIT_FINISH; @@ -1519,28 +1551,19 @@ static void mg_tls_client_handshake(struct mg_connection *c) { if (mg_tls_client_recv_finish(c) < 0) { break; } - if (tls->cert_requested) { - /* for mTLS we should generate application keys at this point - * but then restore handshake keys and continue with - * the rest of the handshake */ - struct tls_enc app_keys; - struct tls_enc hs_keys = tls->enc; - mg_tls_generate_application_keys(c); - app_keys = tls->enc; - tls->enc = hs_keys; - mg_tls_server_send_cert(c); - mg_tls_send_cert_verify(c, 1); - mg_tls_client_send_finish(c); - tls->enc = app_keys; - } else { - mg_tls_client_send_finish(c); - mg_tls_generate_application_keys(c); + if (tls->cert_requested && tls->cert_der.len > 0) { // two-way auth + mg_tls_send_cert(c, true); + mg_tls_send_cert_verify(c, true); } + mg_tls_client_send_finish(c); + mg_tls_generate_application_keys(c); tls->state = MG_TLS_STATE_CLIENT_CONNECTED; c->is_tls_hs = 0; mg_call(c, MG_EV_TLS_HS, NULL); break; - default: mg_error(c, "unexpected client state: %d", tls->state); break; + default: + mg_error(c, "unexpected client state: %d", tls->state); + break; } } @@ -1554,9 +1577,14 @@ static void mg_tls_server_handshake(struct mg_connection *c) { mg_tls_server_send_hello(c); mg_tls_generate_handshake_keys(c); mg_tls_server_send_ext(c); - mg_tls_server_send_cert(c); - mg_tls_send_cert_verify(c, 0); + if (tls->is_twoway) mg_tls_server_send_cert_request(c); + mg_tls_send_cert(c, false); + mg_tls_send_cert_verify(c, false); mg_tls_server_send_finish(c); + if (tls->is_twoway) { + tls->state = MG_TLS_STATE_SERVER_WAIT_CERT; + break; + } tls->state = MG_TLS_STATE_SERVER_NEGOTIATED; // fallthrough case MG_TLS_STATE_SERVER_NEGOTIATED: @@ -1567,7 +1595,17 @@ static void mg_tls_server_handshake(struct mg_connection *c) { tls->state = MG_TLS_STATE_SERVER_CONNECTED; c->is_tls_hs = 0; return; - default: mg_error(c, "unexpected server state: %d", tls->state); break; + case MG_TLS_STATE_SERVER_WAIT_CERT: + if (mg_tls_recv_cert(c, false) < 0) break; + tls->state = MG_TLS_STATE_SERVER_WAIT_CV; + // Fallthrough + case MG_TLS_STATE_SERVER_WAIT_CV: + if (mg_tls_recv_cert_verify(c) < 0) break; + tls->state = MG_TLS_STATE_SERVER_NEGOTIATED; + break; + default: + mg_error(c, "unexpected server state: %d", tls->state); + break; } } @@ -1654,6 +1692,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { MG_ERROR(("Failed to load certificate")); return; } + if (!c->is_client) tls->is_twoway = true; // server + CA: two-way auth } if (opts->cert.buf == NULL) { diff --git a/test/unit_test.c b/test/unit_test.c index 4ec3889f..579bd958 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -875,8 +875,10 @@ static int fetch(struct mg_mgr *mgr, char *buf, const char *url, if (strstr(url, "localhost") != NULL) { // Local connection, use self-signed certificates opts.ca = mg_unpacked("/certs/ca.crt"); - // opts.cert = mg_str(s_tls_cert); - // opts.key = mg_str(s_tls_key); + if (strstr(url, "23456") != NULL) { // hinted from caller + opts.cert = mg_unpacked("/certs/client.crt"); + opts.key = mg_unpacked("/certs/client.key"); + } } mg_tls_init(c, &opts); } @@ -1422,7 +1424,6 @@ static void test_tls(void) { struct mg_str bd; ASSERT(data.buf != NULL && data.len > 0); memset(&opts, 0, sizeof(opts)); - // opts.ca = mg_str(s_tls_ca); opts.cert = mg_unpacked("/certs/server.crt"); opts.key = mg_unpacked("/certs/server.key"); mg_mgr_init(&mgr); @@ -1471,6 +1472,20 @@ static void test_tls(void) { "is 1.3 only; re-enable when other stacks can be easily configured for " "1.3\n"); #endif + + // Repeat the simplest test with two-way authentication + opts.ca = mg_unpacked("/certs/ca.crt"); // configure the server for two-way + // make it fail: the client will not use 2-way + ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") != 200); + // make it work + mg_mgr_free(&mgr); + ASSERT(mgr.conns == NULL); + mg_mgr_init(&mgr); + url = "https://localhost:23456"; // port # hints the client to use two-way + c = mg_http_listen(&mgr, url, eh1, &opts); + ASSERT(c != NULL); + ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\n\n") == 200); + ASSERT(cmpbody(buf, "hello\n") == 0); mg_mgr_free(&mgr); ASSERT(mgr.conns == NULL); #endif @@ -1533,7 +1548,7 @@ static void test_http_client(void) { ASSERT(ok == 200); mg_mgr_poll(&mgr, 1); - // Make host validationfail + // Make host validation fail c = mg_http_connect(&mgr, url, f3, &ok); ASSERT(c != NULL); opts.name = mg_str("dummy"); // Set some invalid hostname value @@ -1543,6 +1558,16 @@ static void test_http_client(void) { MG_INFO(("OK: %d", ok)); ASSERT(ok == 777); mg_mgr_poll(&mgr, 1); + // Skip host validation + c = mg_http_connect(&mgr, url, f3, &ok); + ASSERT(c != NULL); + opts.name = mg_str(""); + mg_tls_init(c, &opts); + ok = 0; + for (i = 0; i < 500 && ok <= 0; i++) mg_mgr_poll(&mgr, 10); + MG_INFO(("OK: %d", ok)); + ASSERT(ok == 200); + mg_mgr_poll(&mgr, 1); opts.name = mg_url_host(url); #if MG_TLS == MG_TLS_BUILTIN mg_free((void *) opts.ca.buf);