Mercurial > hg > nginx-quic
comparison src/event/quic/ngx_event_quic.c @ 8412:e19723c40d28 quic
QUIC: separate files for tokens related processing.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Tue, 13 Apr 2021 14:41:52 +0300 |
parents | bc910a5ec737 |
children | 46161c610919 |
comparison
equal
deleted
inserted
replaced
8411:bc910a5ec737 | 8412:e19723c40d28 |
---|---|
5 | 5 |
6 | 6 |
7 #include <ngx_config.h> | 7 #include <ngx_config.h> |
8 #include <ngx_core.h> | 8 #include <ngx_core.h> |
9 #include <ngx_event.h> | 9 #include <ngx_event.h> |
10 #include <ngx_sha1.h> | |
11 #include <ngx_event_quic_connection.h> | 10 #include <ngx_event_quic_connection.h> |
12 | 11 |
13 | 12 |
14 /* | 13 /* |
15 * 7.4. Cryptographic Message Buffering | 14 * 7.4. Cryptographic Message Buffering |
40 ngx_quic_tp_t *ctp); | 39 ngx_quic_tp_t *ctp); |
41 static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c, | 40 static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c, |
42 ngx_quic_conf_t *conf, ngx_quic_header_t *pkt); | 41 ngx_quic_conf_t *conf, ngx_quic_header_t *pkt); |
43 static ngx_int_t ngx_quic_process_stateless_reset(ngx_connection_t *c, | 42 static ngx_int_t ngx_quic_process_stateless_reset(ngx_connection_t *c, |
44 ngx_quic_header_t *pkt); | 43 ngx_quic_header_t *pkt); |
45 static void ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, | |
46 u_char buf[20]); | |
47 static ngx_int_t ngx_quic_validate_token(ngx_connection_t *c, | |
48 u_char *key, ngx_quic_header_t *pkt); | |
49 static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c); | 44 static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c); |
50 static void ngx_quic_input_handler(ngx_event_t *rev); | 45 static void ngx_quic_input_handler(ngx_event_t *rev); |
51 | 46 |
52 static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc); | 47 static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc); |
53 static void ngx_quic_close_timer_handler(ngx_event_t *ev); | 48 static void ngx_quic_close_timer_handler(ngx_event_t *ev); |
618 | 613 |
619 return qc; | 614 return qc; |
620 } | 615 } |
621 | 616 |
622 | 617 |
623 ngx_int_t | |
624 ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret, | |
625 u_char *token) | |
626 { | |
627 ngx_str_t tmp; | |
628 | |
629 tmp.data = secret; | |
630 tmp.len = NGX_QUIC_SR_KEY_LEN; | |
631 | |
632 if (ngx_quic_derive_key(c->log, "sr_token_key", &tmp, cid, token, | |
633 NGX_QUIC_SR_TOKEN_LEN) | |
634 != NGX_OK) | |
635 { | |
636 return NGX_ERROR; | |
637 } | |
638 | |
639 #if (NGX_DEBUG) | |
640 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
641 "quic stateless reset token %*xs", | |
642 (size_t) NGX_QUIC_SR_TOKEN_LEN, token); | |
643 #endif | |
644 | |
645 return NGX_OK; | |
646 } | |
647 | |
648 | |
649 static ngx_int_t | 618 static ngx_int_t |
650 ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt) | 619 ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt) |
651 { | 620 { |
652 u_char *tail, ch; | 621 u_char *tail, ch; |
653 ngx_uint_t i; | 622 ngx_uint_t i; |
683 | 652 |
684 if (ch == 0) { | 653 if (ch == 0) { |
685 return NGX_OK; | 654 return NGX_OK; |
686 } | 655 } |
687 } | 656 } |
688 | |
689 return NGX_DECLINED; | |
690 } | |
691 | |
692 | |
693 ngx_int_t | |
694 ngx_quic_new_token(ngx_connection_t *c, u_char *key, ngx_str_t *token, | |
695 ngx_str_t *odcid, time_t exp, ngx_uint_t is_retry) | |
696 { | |
697 int len, iv_len; | |
698 u_char *p, *iv; | |
699 EVP_CIPHER_CTX *ctx; | |
700 const EVP_CIPHER *cipher; | |
701 | |
702 u_char in[NGX_QUIC_MAX_TOKEN_SIZE]; | |
703 | |
704 ngx_quic_address_hash(c, !is_retry, in); | |
705 | |
706 p = in + 20; | |
707 | |
708 p = ngx_cpymem(p, &exp, sizeof(time_t)); | |
709 | |
710 *p++ = is_retry ? 1 : 0; | |
711 | |
712 if (odcid) { | |
713 *p++ = odcid->len; | |
714 p = ngx_cpymem(p, odcid->data, odcid->len); | |
715 | |
716 } else { | |
717 *p++ = 0; | |
718 } | |
719 | |
720 len = p - in; | |
721 | |
722 cipher = EVP_aes_256_cbc(); | |
723 iv_len = EVP_CIPHER_iv_length(cipher); | |
724 | |
725 token->len = iv_len + len + EVP_CIPHER_block_size(cipher); | |
726 token->data = ngx_pnalloc(c->pool, token->len); | |
727 if (token->data == NULL) { | |
728 return NGX_ERROR; | |
729 } | |
730 | |
731 ctx = EVP_CIPHER_CTX_new(); | |
732 if (ctx == NULL) { | |
733 return NGX_ERROR; | |
734 } | |
735 | |
736 iv = token->data; | |
737 | |
738 if (RAND_bytes(iv, iv_len) <= 0 | |
739 || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv)) | |
740 { | |
741 EVP_CIPHER_CTX_free(ctx); | |
742 return NGX_ERROR; | |
743 } | |
744 | |
745 token->len = iv_len; | |
746 | |
747 if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) { | |
748 EVP_CIPHER_CTX_free(ctx); | |
749 return NGX_ERROR; | |
750 } | |
751 | |
752 token->len += len; | |
753 | |
754 if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) { | |
755 EVP_CIPHER_CTX_free(ctx); | |
756 return NGX_ERROR; | |
757 } | |
758 | |
759 token->len += len; | |
760 | |
761 EVP_CIPHER_CTX_free(ctx); | |
762 | |
763 #ifdef NGX_QUIC_DEBUG_PACKETS | |
764 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
765 "quic new token len:%uz %xV", token->len, token); | |
766 #endif | |
767 | |
768 return NGX_OK; | |
769 } | |
770 | |
771 | |
772 static void | |
773 ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, u_char buf[20]) | |
774 { | |
775 size_t len; | |
776 u_char *data; | |
777 ngx_sha1_t sha1; | |
778 struct sockaddr_in *sin; | |
779 #if (NGX_HAVE_INET6) | |
780 struct sockaddr_in6 *sin6; | |
781 #endif | |
782 | |
783 len = (size_t) c->socklen; | |
784 data = (u_char *) c->sockaddr; | |
785 | |
786 if (no_port) { | |
787 switch (c->sockaddr->sa_family) { | |
788 | |
789 #if (NGX_HAVE_INET6) | |
790 case AF_INET6: | |
791 sin6 = (struct sockaddr_in6 *) c->sockaddr; | |
792 | |
793 len = sizeof(struct in6_addr); | |
794 data = sin6->sin6_addr.s6_addr; | |
795 | |
796 break; | |
797 #endif | |
798 | |
799 case AF_INET: | |
800 sin = (struct sockaddr_in *) c->sockaddr; | |
801 | |
802 len = sizeof(in_addr_t); | |
803 data = (u_char *) &sin->sin_addr; | |
804 | |
805 break; | |
806 } | |
807 } | |
808 | |
809 ngx_sha1_init(&sha1); | |
810 ngx_sha1_update(&sha1, data, len); | |
811 ngx_sha1_final(buf, &sha1); | |
812 } | |
813 | |
814 | |
815 static ngx_int_t | |
816 ngx_quic_validate_token(ngx_connection_t *c, u_char *key, | |
817 ngx_quic_header_t *pkt) | |
818 { | |
819 int len, tlen, iv_len; | |
820 u_char *iv, *p; | |
821 time_t now, exp; | |
822 size_t total; | |
823 ngx_str_t odcid; | |
824 EVP_CIPHER_CTX *ctx; | |
825 const EVP_CIPHER *cipher; | |
826 | |
827 u_char addr_hash[20]; | |
828 u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE]; | |
829 | |
830 /* Retry token or NEW_TOKEN in a previous connection */ | |
831 | |
832 cipher = EVP_aes_256_cbc(); | |
833 iv = pkt->token.data; | |
834 iv_len = EVP_CIPHER_iv_length(cipher); | |
835 | |
836 /* sanity checks */ | |
837 | |
838 if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) { | |
839 goto garbage; | |
840 } | |
841 | |
842 if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) { | |
843 goto garbage; | |
844 } | |
845 | |
846 ctx = EVP_CIPHER_CTX_new(); | |
847 if (ctx == NULL) { | |
848 return NGX_ERROR; | |
849 } | |
850 | |
851 if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) { | |
852 EVP_CIPHER_CTX_free(ctx); | |
853 return NGX_ERROR; | |
854 } | |
855 | |
856 p = pkt->token.data + iv_len; | |
857 len = pkt->token.len - iv_len; | |
858 | |
859 if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) { | |
860 EVP_CIPHER_CTX_free(ctx); | |
861 goto garbage; | |
862 } | |
863 total = len; | |
864 | |
865 if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) { | |
866 EVP_CIPHER_CTX_free(ctx); | |
867 goto garbage; | |
868 } | |
869 total += tlen; | |
870 | |
871 EVP_CIPHER_CTX_free(ctx); | |
872 | |
873 if (total < (20 + sizeof(time_t) + 2)) { | |
874 goto garbage; | |
875 } | |
876 | |
877 p = tdec + 20; | |
878 | |
879 ngx_memcpy(&exp, p, sizeof(time_t)); | |
880 p += sizeof(time_t); | |
881 | |
882 pkt->retried = (*p++ == 1); | |
883 | |
884 ngx_quic_address_hash(c, !pkt->retried, addr_hash); | |
885 | |
886 if (ngx_memcmp(tdec, addr_hash, 20) != 0) { | |
887 goto bad_token; | |
888 } | |
889 | |
890 odcid.len = *p++; | |
891 if (odcid.len) { | |
892 if (odcid.len > NGX_QUIC_MAX_CID_LEN) { | |
893 goto bad_token; | |
894 } | |
895 | |
896 if ((size_t)(tdec + total - p) < odcid.len) { | |
897 goto bad_token; | |
898 } | |
899 | |
900 odcid.data = p; | |
901 p += odcid.len; | |
902 } | |
903 | |
904 now = ngx_time(); | |
905 | |
906 if (now > exp) { | |
907 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token"); | |
908 return NGX_DECLINED; | |
909 } | |
910 | |
911 if (odcid.len) { | |
912 pkt->odcid.len = odcid.len; | |
913 pkt->odcid.data = ngx_pstrdup(c->pool, &odcid); | |
914 if (pkt->odcid.data == NULL) { | |
915 return NGX_ERROR; | |
916 } | |
917 | |
918 } else { | |
919 pkt->odcid = pkt->dcid; | |
920 } | |
921 | |
922 pkt->validated = 1; | |
923 | |
924 return NGX_OK; | |
925 | |
926 garbage: | |
927 | |
928 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic garbage token"); | |
929 | |
930 return NGX_ABORT; | |
931 | |
932 bad_token: | |
933 | |
934 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token"); | |
935 | 657 |
936 return NGX_DECLINED; | 658 return NGX_DECLINED; |
937 } | 659 } |
938 | 660 |
939 | 661 |