Mercurial > hg > nginx-quic
comparison src/http/ngx_http_request.c @ 7640:b7bbfea7a6c3 quic
QUIC handshake handler, draft 24 bump.
author | Sergey Kandaurov <pluknet@nginx.com> |
---|---|
date | Fri, 28 Feb 2020 13:09:52 +0300 |
parents | 02f331613232 |
children | 72f632f90a17 |
comparison
equal
deleted
inserted
replaced
7639:02f331613232 | 7640:b7bbfea7a6c3 |
---|---|
62 #if (NGX_HTTP_SSL) | 62 #if (NGX_HTTP_SSL) |
63 static void ngx_http_ssl_handshake(ngx_event_t *rev); | 63 static void ngx_http_ssl_handshake(ngx_event_t *rev); |
64 static void ngx_http_ssl_handshake_handler(ngx_connection_t *c); | 64 static void ngx_http_ssl_handshake_handler(ngx_connection_t *c); |
65 | 65 |
66 static void ngx_http_quic_handshake(ngx_event_t *rev); | 66 static void ngx_http_quic_handshake(ngx_event_t *rev); |
67 static void ngx_http_quic_handshake_handler(ngx_event_t *rev); | |
67 #endif | 68 #endif |
68 | 69 |
69 | 70 |
70 static char *ngx_http_client_errors[] = { | 71 static char *ngx_http_client_errors[] = { |
71 | 72 |
704 b->pos += 4; | 705 b->pos += 4; |
705 | 706 |
706 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, | 707 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, |
707 "quic flags:%xi version:%xD", flags, version); | 708 "quic flags:%xi version:%xD", flags, version); |
708 | 709 |
709 if (version != 0xff000017) { | 710 if (version != 0xff000018) { |
710 ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); | 711 ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); |
711 ngx_http_close_connection(c); | 712 ngx_http_close_connection(c); |
712 return; | 713 return; |
713 } | 714 } |
714 | 715 |
740 } | 741 } |
741 | 742 |
742 ngx_memcpy(qc->token.data, b->pos, qc->token.len); | 743 ngx_memcpy(qc->token.data, b->pos, qc->token.len); |
743 b->pos += qc->token.len; | 744 b->pos += qc->token.len; |
744 | 745 |
745 uint64_t plen = ngx_quic_parse_int(&b->pos); | 746 ngx_int_t plen = ngx_quic_parse_int(&b->pos); |
747 | |
748 if (plen > b->last - b->pos) { | |
749 ngx_log_error(NGX_LOG_INFO, rev->log, 0, "truncated initial packet"); | |
750 ngx_http_close_connection(c); | |
751 return; | |
752 } | |
753 | |
746 /* draft-ietf-quic-tls-23#section-5.4.2: | 754 /* draft-ietf-quic-tls-23#section-5.4.2: |
747 * the Packet Number field is assumed to be 4 bytes long | 755 * the Packet Number field is assumed to be 4 bytes long |
748 * draft-ietf-quic-tls-23#section-5.4.[34]: | 756 * draft-ietf-quic-tls-23#section-5.4.[34]: |
749 * AES-Based and ChaCha20-Based header protections sample 16 bytes | 757 * AES-Based and ChaCha20-Based header protections sample 16 bytes |
750 */ | 758 */ |
1394 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | 1402 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, |
1395 "SSL_quic_read_level: %d, SSL_quic_write_level: %d", | 1403 "SSL_quic_read_level: %d, SSL_quic_write_level: %d", |
1396 (int) SSL_quic_read_level(c->ssl->connection), | 1404 (int) SSL_quic_read_level(c->ssl->connection), |
1397 (int) SSL_quic_write_level(c->ssl->connection)); | 1405 (int) SSL_quic_write_level(c->ssl->connection)); |
1398 | 1406 |
1407 if (!rev->timer_set) { | |
1408 ngx_add_timer(rev, c->listening->post_accept_timeout); | |
1409 } | |
1410 | |
1411 rev->handler = ngx_http_quic_handshake_handler; | |
1412 return; | |
1413 } | |
1414 | |
1415 | |
1416 static void | |
1417 ngx_http_quic_handshake_handler(ngx_event_t *rev) | |
1418 { | |
1419 size_t m; | |
1420 ssize_t n; | |
1421 ngx_connection_t *c; | |
1422 ngx_quic_connection_t *qc; | |
1423 u_char buf[4096], b[512], *p; | |
1424 | |
1425 c = rev->data; | |
1426 qc = c->quic; | |
1427 p = b; | |
1428 | |
1429 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic handshake handler"); | |
1430 | |
1431 if (rev->timedout) { | |
1432 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
1433 ngx_http_close_connection(c); | |
1434 return; | |
1435 } | |
1436 | |
1437 if (c->close) { | |
1438 ngx_http_close_connection(c); | |
1439 return; | |
1440 } | |
1441 | |
1442 n = c->recv(c, b, sizeof(b)); | |
1443 | |
1444 if (n == NGX_AGAIN) { | |
1445 return; | |
1446 } | |
1447 | |
1448 if (n == NGX_ERROR) { | |
1449 c->read->eof = 1; | |
1450 ngx_http_close_connection(c); | |
1451 return; | |
1452 } | |
1453 | |
1454 m = ngx_hex_dump(buf, b, n) - buf; | |
1455 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, | |
1456 "quic handshake handler: %*s, len: %uz", m, buf, n); | |
1457 | |
1458 /* XXX bug-for-bug compat - assuming initial ack in handshake pkt */ | |
1459 | |
1460 if ((p[0] & 0xf0) != 0xe0) { | |
1461 ngx_log_error(NGX_LOG_INFO, rev->log, 0, "invalid packet type"); | |
1462 ngx_http_close_connection(c); | |
1463 return; | |
1464 } | |
1465 | |
1466 ngx_int_t flags = *p++; | |
1467 uint32_t version = ngx_http_v2_parse_uint32(p); | |
1468 p += 4; | |
1469 | |
1470 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, | |
1471 "quic flags:%xi version:%xD", flags, version); | |
1472 | |
1473 if (version != 0xff000018) { | |
1474 ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unsupported quic version"); | |
1475 ngx_http_close_connection(c); | |
1476 return; | |
1477 } | |
1478 | |
1479 if (*p++ != qc->dcid.len) { | |
1480 ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic dcidl"); | |
1481 ngx_http_close_connection(c); | |
1482 return; | |
1483 } | |
1484 | |
1485 if (ngx_memcmp(p, qc->dcid.data, qc->dcid.len) != 0) { | |
1486 ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic dcid"); | |
1487 ngx_http_close_connection(c); | |
1488 return; | |
1489 } | |
1490 | |
1491 p += qc->dcid.len; | |
1492 | |
1493 if (*p++ != qc->scid.len) { | |
1494 ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic scidl"); | |
1495 ngx_http_close_connection(c); | |
1496 return; | |
1497 } | |
1498 | |
1499 if (ngx_memcmp(p, qc->scid.data, qc->scid.len) != 0) { | |
1500 ngx_log_error(NGX_LOG_INFO, rev->log, 0, "unexpected quic scid"); | |
1501 ngx_http_close_connection(c); | |
1502 return; | |
1503 } | |
1504 | |
1505 p += qc->scid.len; | |
1506 | |
1507 ngx_int_t plen = ngx_quic_parse_int(&p); | |
1508 | |
1509 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, | |
1510 "quic packet length: %d", plen); | |
1511 | |
1512 if (plen > b + n - p) { | |
1513 ngx_log_error(NGX_LOG_INFO, rev->log, 0, "truncated handshake packet"); | |
1514 ngx_http_close_connection(c); | |
1515 return; | |
1516 } | |
1517 | |
1518 u_char *sample = p + 4; | |
1519 | |
1520 m = ngx_hex_dump(buf, sample, 16) - buf; | |
1521 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, "quic sample: %*s", m, buf); | |
1522 | |
1523 // header protection | |
1524 | |
1525 EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); | |
1526 uint8_t mask[16]; | |
1527 int outlen; | |
1528 | |
1529 if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, | |
1530 qc->client_hs_hp.data, NULL) | |
1531 != 1) | |
1532 { | |
1533 EVP_CIPHER_CTX_free(ctx); | |
1534 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, | |
1535 "EVP_EncryptInit_ex() failed"); | |
1536 ngx_http_close_connection(c); | |
1537 return; | |
1538 } | |
1539 | |
1540 if (!EVP_EncryptUpdate(ctx, mask, &outlen, sample, 16)) { | |
1541 EVP_CIPHER_CTX_free(ctx); | |
1542 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, | |
1543 "EVP_EncryptUpdate() failed"); | |
1544 ngx_http_close_connection(c); | |
1545 return; | |
1546 } | |
1547 | |
1548 EVP_CIPHER_CTX_free(ctx); | |
1549 | |
1550 u_char clearflags = flags ^ (mask[0] & 0x0f); | |
1551 ngx_int_t pnl = (clearflags & 0x03) + 1; | |
1552 uint64_t pn = ngx_quic_parse_pn(&p, pnl, &mask[1]); | |
1553 | |
1554 #if (NGX_DEBUG) | |
1555 if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { | |
1556 m = ngx_hex_dump(buf, mask, 5) - buf; | |
1557 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, | |
1558 "quic mask: %*s", m, buf); | |
1559 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, | |
1560 "quic clear flags: %xi", clearflags); | |
1561 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, rev->log, 0, | |
1562 "quic packet number: %uL, len: %xi", pn, pnl); | |
1563 } | |
1564 #endif | |
1565 | |
1566 // packet protection | |
1567 | |
1568 ngx_str_t ciphertext; | |
1569 ciphertext.data = p; | |
1570 ciphertext.len = plen - pnl; | |
1571 | |
1572 ngx_str_t ad; | |
1573 ad.len = p - b; | |
1574 ad.data = ngx_pnalloc(c->pool, ad.len); | |
1575 if (ad.data == NULL) { | |
1576 ngx_http_close_connection(c); | |
1577 return; | |
1578 } | |
1579 | |
1580 ngx_memcpy(ad.data, b, ad.len); | |
1581 ad.data[0] = clearflags; | |
1582 ad.data[ad.len - pnl] = (u_char)pn; | |
1583 | |
1584 uint8_t *nonce = ngx_pstrdup(c->pool, &qc->client_hs_iv); | |
1585 nonce[11] ^= pn; | |
1586 | |
1587 #if (NGX_DEBUG) | |
1588 if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { | |
1589 m = ngx_hex_dump(buf, nonce, 12) - buf; | |
1590 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, | |
1591 "quic nonce: %*s, len: %uz", m, buf, 12); | |
1592 | |
1593 m = ngx_hex_dump(buf, ad.data, ad.len) - buf; | |
1594 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, rev->log, 0, | |
1595 "quic ad: %*s, len: %uz", m, buf, ad.len); | |
1596 } | |
1597 #endif | |
1598 | |
1599 #ifdef OPENSSL_IS_BORINGSSL | |
1600 const EVP_AEAD *cipher; | |
1601 #else | |
1602 const EVP_CIPHER *cipher; | |
1603 #endif | |
1604 | |
1605 u_char *name = (u_char *) SSL_get_cipher(c->ssl->connection); | |
1606 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, | |
1607 "quic ssl cipher: %s", name); | |
1608 | |
1609 if (ngx_strcasecmp(name, (u_char *) "TLS_AES_128_GCM_SHA256") == 0 | |
1610 || ngx_strcasecmp(name, (u_char *) "(NONE)") == 0) | |
1611 { | |
1612 #ifdef OPENSSL_IS_BORINGSSL | |
1613 cipher = EVP_aead_aes_128_gcm(); | |
1614 #else | |
1615 cipher = EVP_aes_128_gcm(); | |
1616 #endif | |
1617 | |
1618 } else if (ngx_strcasecmp(name, (u_char *) "TLS_AES_256_GCM_SHA384") == 0) { | |
1619 #ifdef OPENSSL_IS_BORINGSSL | |
1620 cipher = EVP_aead_aes_256_gcm(); | |
1621 #else | |
1622 cipher = EVP_aes_256_gcm(); | |
1623 #endif | |
1624 | |
1625 } else { | |
1626 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "unexpected cipher"); | |
1627 ngx_http_close_connection(c); | |
1628 return; | |
1629 } | |
1630 | |
1631 | |
1632 uint8_t cleartext[1600]; | |
1633 size_t cleartext_len; | |
1634 | |
1635 #ifdef OPENSSL_IS_BORINGSSL | |
1636 EVP_AEAD_CTX *aead = EVP_AEAD_CTX_new(cipher, | |
1637 qc->client_hs_key.data, | |
1638 qc->client_hs_key.len, | |
1639 EVP_AEAD_DEFAULT_TAG_LENGTH); | |
1640 if (aead == NULL) { | |
1641 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_AEAD_CTX_new() failed"); | |
1642 ngx_http_close_connection(c); | |
1643 return; | |
1644 } | |
1645 | |
1646 if (EVP_AEAD_CTX_open(aead, cleartext, &cleartext_len, sizeof(cleartext), | |
1647 nonce, qc->client_hs_iv.len, ciphertext.data, | |
1648 ciphertext.len, ad.data, ad.len) | |
1649 != 1) | |
1650 { | |
1651 EVP_AEAD_CTX_free(aead); | |
1652 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, | |
1653 "EVP_AEAD_CTX_open() failed"); | |
1654 ngx_http_close_connection(c); | |
1655 return; | |
1656 } | |
1657 | |
1658 EVP_AEAD_CTX_free(aead); | |
1659 #else | |
1660 int len; | |
1661 u_char *tag; | |
1662 EVP_CIPHER_CTX *aead; | |
1663 | |
1664 aead = EVP_CIPHER_CTX_new(); | |
1665 if (aead == NULL) { | |
1666 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_CIPHER_CTX_new() failed"); | |
1667 ngx_http_close_connection(c); | |
1668 return; | |
1669 } | |
1670 | |
1671 if (EVP_DecryptInit_ex(aead, cipher, NULL, NULL, NULL) != 1) { | |
1672 EVP_CIPHER_CTX_free(aead); | |
1673 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); | |
1674 ngx_http_close_connection(c); | |
1675 return; | |
1676 } | |
1677 | |
1678 if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_IVLEN, qc->client_hs_iv.len, | |
1679 NULL) | |
1680 == 0) | |
1681 { | |
1682 EVP_CIPHER_CTX_free(aead); | |
1683 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, | |
1684 "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed"); | |
1685 ngx_http_close_connection(c); | |
1686 return; | |
1687 } | |
1688 | |
1689 if (EVP_DecryptInit_ex(aead, NULL, NULL, qc->client_hs_key.data, nonce) | |
1690 != 1) | |
1691 { | |
1692 EVP_CIPHER_CTX_free(aead); | |
1693 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptInit_ex() failed"); | |
1694 ngx_http_close_connection(c); | |
1695 return; | |
1696 } | |
1697 | |
1698 if (EVP_DecryptUpdate(aead, NULL, &len, ad.data, ad.len) != 1) { | |
1699 EVP_CIPHER_CTX_free(aead); | |
1700 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); | |
1701 ngx_http_close_connection(c); | |
1702 return; | |
1703 } | |
1704 | |
1705 if (EVP_DecryptUpdate(aead, cleartext, &len, ciphertext.data, | |
1706 ciphertext.len - EVP_GCM_TLS_TAG_LEN) | |
1707 != 1) | |
1708 { | |
1709 EVP_CIPHER_CTX_free(aead); | |
1710 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptUpdate() failed"); | |
1711 ngx_http_close_connection(c); | |
1712 return; | |
1713 } | |
1714 | |
1715 cleartext_len = len; | |
1716 tag = ciphertext.data + ciphertext.len - EVP_GCM_TLS_TAG_LEN; | |
1717 | |
1718 if (EVP_CIPHER_CTX_ctrl(aead, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, | |
1719 tag) | |
1720 == 0) | |
1721 { | |
1722 EVP_CIPHER_CTX_free(aead); | |
1723 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, | |
1724 "EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_TAG) failed"); | |
1725 ngx_http_close_connection(c); | |
1726 return; | |
1727 } | |
1728 | |
1729 if (EVP_DecryptFinal_ex(aead, cleartext + len, &len) <= 0) { | |
1730 EVP_CIPHER_CTX_free(aead); | |
1731 ngx_ssl_error(NGX_LOG_INFO, rev->log, 0, "EVP_DecryptFinal_ex failed"); | |
1732 ngx_http_close_connection(c); | |
1733 return; | |
1734 } | |
1735 | |
1736 cleartext_len += len; | |
1737 | |
1738 EVP_CIPHER_CTX_free(aead); | |
1739 #endif | |
1740 | |
1741 #if (NGX_DEBUG) | |
1742 if (c->log->log_level & NGX_LOG_DEBUG_EVENT) { | |
1743 m = ngx_hex_dump(buf, cleartext, ngx_min(cleartext_len, 256)) - buf; | |
1744 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, rev->log, 0, | |
1745 "quic packet payload: %*s%s, len: %uz", | |
1746 m, buf, m < 512 ? "" : "...", cleartext_len); | |
1747 } | |
1748 #endif | |
1749 | |
1399 ngx_http_close_connection(c); | 1750 ngx_http_close_connection(c); |
1400 return; | |
1401 } | 1751 } |
1402 | 1752 |
1403 | 1753 |
1404 static void | 1754 static void |
1405 ngx_http_ssl_handshake(ngx_event_t *rev) | 1755 ngx_http_ssl_handshake(ngx_event_t *rev) |