/* Provenance: openssl/openssl tls_parse_stoc_alpn (fixed). * repo: openssl/openssl * commit: 4b375b998798dd516d367036773073e1b88e6433 * parent: 189a7ed3e380e34ea38fe4190a7c9396bace0fb7 * commit_url: https://github.com/openssl/openssl/commit/4b375b998798dd516d367036773073e1b88e6433 * cve: CVE-2024-5535 * license: Apache-2.0 * function: tls_parse_stoc_alpn * relpath: ssl/statem/extensions_clnt.c */ int tls_parse_stoc_alpn(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx) { size_t len; PACKET confpkt, protpkt; int valid = 0; /* We must have requested it. */ if (!s->s3.alpn_sent) { SSLfatal(s, SSL_AD_UNSUPPORTED_EXTENSION, SSL_R_BAD_EXTENSION); return 0; } /*- * The extension data consists of: * uint16 list_length * uint8 proto_length; * uint8 proto[proto_length]; */ if (!PACKET_get_net_2_len(pkt, &len) || PACKET_remaining(pkt) != len || !PACKET_get_1_len(pkt, &len) || PACKET_remaining(pkt) != len) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); return 0; } /* It must be a protocol that we sent */ if (!PACKET_buf_init(&confpkt, s->ext.alpn, s->ext.alpn_len)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } while (PACKET_get_length_prefixed_1(&confpkt, &protpkt)) { if (PACKET_remaining(&protpkt) != len) continue; if (memcmp(PACKET_data(pkt), PACKET_data(&protpkt), len) == 0) { /* Valid protocol found */ valid = 1; break; } } if (!valid) { /* The protocol sent from the server does not match one we advertised */ SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); return 0; } OPENSSL_free(s->s3.alpn_selected); s->s3.alpn_selected = OPENSSL_malloc(len); if (s->s3.alpn_selected == NULL) { s->s3.alpn_selected_len = 0; SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } if (!PACKET_copy_bytes(pkt, s->s3.alpn_selected, len)) { SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_R_BAD_EXTENSION); return 0; } s->s3.alpn_selected_len = len; if (s->session->ext.alpn_selected == NULL || s->session->ext.alpn_selected_len != len || memcmp(s->session->ext.alpn_selected, s->s3.alpn_selected, len) != 0) { /* ALPN not consistent with the old session so cannot use early_data */ s->ext.early_data_ok = 0; } if (!s->hit) { /* * This is a new session and so alpn_selected should have been * initialised to NULL. We should update it with the selected ALPN. */ if (!ossl_assert(s->session->ext.alpn_selected == NULL)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } s->session->ext.alpn_selected = OPENSSL_memdup(s->s3.alpn_selected, s->s3.alpn_selected_len); if (s->session->ext.alpn_selected == NULL) { s->session->ext.alpn_selected_len = 0; SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } s->session->ext.alpn_selected_len = s->s3.alpn_selected_len; } return 1; }