/* this function reads a (small) unsigned integer * from asn1 structs. Combines the read and the convertion * steps. */ int _gnutls_x509_read_uint (ASN1_TYPE node, const char *value, unsigned int *ret) { int len, result; opaque *tmpstr; len = 0; result = asn1_read_value (node, value, NULL, &len); if (result != ASN1_MEM_ERROR) { gnutls_assert (); return _gnutls_asn2err (result); } tmpstr = gnutls_malloc (len); if (tmpstr == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } result = asn1_read_value (node, value, tmpstr, &len); if (result != ASN1_SUCCESS) { gnutls_assert (); gnutls_free (tmpstr); return _gnutls_asn2err (result); } if (len == 1) *ret = tmpstr[0]; else if (len == 2) *ret = _gnutls_read_uint16 (tmpstr); else if (len == 3) *ret = _gnutls_read_uint24 (tmpstr); else if (len == 4) *ret = _gnutls_read_uint32 (tmpstr); else { gnutls_assert (); gnutls_free (tmpstr); return GNUTLS_E_INTERNAL_ERROR; } gnutls_free (tmpstr); return 0; }
/* Receive TLS/IA data. Store received TLS/IA message type in *MSG_TYPE, and the data in DATA of max SIZEOFDATA size. Return the number of bytes read, or an error code. */ static ssize_t _gnutls_recv_inner_application (gnutls_session_t session, gnutls_ia_apptype_t * msg_type, opaque * data, size_t sizeofdata) { ssize_t len; uint32_t len24; opaque pkt[4]; len = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, pkt, 4); if (len != 4) { gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } *msg_type = pkt[0]; len24 = _gnutls_read_uint24 (&pkt[1]); if (*msg_type != GNUTLS_IA_APPLICATION_PAYLOAD && len24 != CHECKSUM_SIZE) { gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } if (sizeofdata < len24) { /* XXX push back pkt to IA buffer? */ gnutls_assert (); return GNUTLS_E_SHORT_MEMORY_BUFFER; } if (len24 > 0) { uint32_t tmplen = len24; len24 = _gnutls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, data, tmplen); if (len24 != tmplen) { gnutls_assert (); /* XXX Correct? */ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } } return len24; }
static int parse_handshake_header(gnutls_session_t session, mbuffer_st * bufel, handshake_buffer_st * hsk) { uint8_t *dataptr = NULL; /* for realloc */ size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session), data_size, frag_size; /* Note: SSL2_HEADERS == 1 */ if (_mbuffer_get_udata_size(bufel) < handshake_header_size) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); dataptr = _mbuffer_get_udata_ptr(bufel); /* if reading a client hello of SSLv2 */ #ifdef ENABLE_SSL2 if (unlikely (!IS_DTLS(session) && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)) { handshake_header_size = SSL2_HEADERS; /* we've already read one byte */ frag_size = _mbuffer_get_udata_size(bufel) - handshake_header_size; /* we've read the first byte */ if (dataptr[0] != GNUTLS_HANDSHAKE_CLIENT_HELLO) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); hsk->rtype = hsk->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2; hsk->sequence = 0; hsk->start_offset = 0; hsk->length = frag_size; } else #endif { /* TLS or DTLS handshake headers */ hsk->rtype = hsk->htype = dataptr[0]; /* we do not use DECR_LEN because we know * that the packet has enough data. */ hsk->length = _gnutls_read_uint24(&dataptr[1]); if (IS_DTLS(session)) { hsk->sequence = _gnutls_read_uint16(&dataptr[4]); hsk->start_offset = _gnutls_read_uint24(&dataptr[6]); frag_size = _gnutls_read_uint24(&dataptr[9]); } else { hsk->sequence = 0; hsk->start_offset = 0; frag_size = MIN((_mbuffer_get_udata_size(bufel) - handshake_header_size), hsk->length); } /* TLS1.3: distinguish server hello versus hello retry request. * The epitome of slick protocol design. */ if (hsk->htype == GNUTLS_HANDSHAKE_SERVER_HELLO && hsk->start_offset == 0 && !IS_DTLS(session)) { if (_mbuffer_get_udata_size(bufel) > handshake_header_size+2+GNUTLS_RANDOM_SIZE && memcmp(dataptr+handshake_header_size+2, HRR_RANDOM, GNUTLS_RANDOM_SIZE) == 0) { hsk->htype = GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST; } } } data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size; if (frag_size > 0) hsk->end_offset = hsk->start_offset + frag_size - 1; else hsk->end_offset = 0; _gnutls_handshake_log ("HSK[%p]: %s (%u) was received. Length %d[%d], frag offset %d, frag length: %d, sequence: %d\n", session, _gnutls_handshake2str(hsk->htype), (unsigned) hsk->htype, (int) hsk->length, (int) data_size, hsk->start_offset, (int) frag_size, (int) hsk->sequence); hsk->header_size = handshake_header_size; memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel), handshake_header_size); if (hsk->length > 0 && (frag_size > data_size || (frag_size > 0 && hsk->end_offset >= hsk->length))) { return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); } else if (hsk->length == 0 && hsk->end_offset != 0 && hsk->start_offset != 0) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); return handshake_header_size; }
static int parse_handshake_header(gnutls_session_t session, mbuffer_st * bufel, handshake_buffer_st * hsk) { uint8_t *dataptr = NULL; /* for realloc */ size_t handshake_header_size = HANDSHAKE_HEADER_SIZE(session), data_size; /* Note: SSL2_HEADERS == 1 */ if (_mbuffer_get_udata_size(bufel) < handshake_header_size) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); dataptr = _mbuffer_get_udata_ptr(bufel); /* if reading a client hello of SSLv2 */ #ifdef ENABLE_SSL2 if (unlikely (!IS_DTLS(session) && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO_V2)) { handshake_header_size = SSL2_HEADERS; /* we've already read one byte */ hsk->length = _mbuffer_get_udata_size(bufel) - handshake_header_size; /* we've read the first byte */ if (dataptr[0] != GNUTLS_HANDSHAKE_CLIENT_HELLO) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); hsk->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2; hsk->sequence = 0; hsk->start_offset = 0; hsk->end_offset = hsk->length; } else #endif { /* TLS or DTLS handshake headers */ hsk->htype = dataptr[0]; /* we do not use DECR_LEN because we know * that the packet has enough data. */ hsk->length = _gnutls_read_uint24(&dataptr[1]); handshake_header_size = HANDSHAKE_HEADER_SIZE(session); if (IS_DTLS(session)) { hsk->sequence = _gnutls_read_uint16(&dataptr[4]); hsk->start_offset = _gnutls_read_uint24(&dataptr[6]); hsk->end_offset = hsk->start_offset + _gnutls_read_uint24(&dataptr[9]); } else { hsk->sequence = 0; hsk->start_offset = 0; hsk->end_offset = MIN((_mbuffer_get_udata_size(bufel) - handshake_header_size), hsk->length); } } data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size; /* make the length offset */ if (hsk->end_offset > 0) hsk->end_offset--; _gnutls_handshake_log ("HSK[%p]: %s (%u) was received. Length %d[%d], frag offset %d, frag length: %d, sequence: %d\n", session, _gnutls_handshake2str(hsk->htype), (unsigned) hsk->htype, (int) hsk->length, (int) data_size, hsk->start_offset, hsk->end_offset - hsk->start_offset + 1, (int) hsk->sequence); hsk->header_size = handshake_header_size; memcpy(hsk->header, _mbuffer_get_udata_ptr(bufel), handshake_header_size); if (hsk->length > 0 && (hsk->end_offset - hsk->start_offset >= data_size)) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); if (hsk->length > 0 && (hsk->start_offset >= hsk->end_offset || hsk->end_offset - hsk->start_offset >= data_size || hsk->end_offset >= hsk->length)) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); else if (hsk->length == 0 && hsk->end_offset != 0 && hsk->start_offset != 0) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); return handshake_header_size; }
static int parse_cert_list(gnutls_session_t session, uint8_t * data, size_t data_size) { int len, ret; uint8_t *p = data; cert_auth_info_t info; gnutls_certificate_credentials_t cred; ssize_t dsize = data_size, size; int i; unsigned npeer_certs, npeer_ocsp, j; crt_cert_ctx_st ctx; gnutls_datum_t *peer_certs = NULL; gnutls_datum_t *peer_ocsp = NULL; unsigned nentries = 0; cred = (gnutls_certificate_credentials_t) _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE); if (cred == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if ((ret = _gnutls_auth_info_init(session, GNUTLS_CRD_CERTIFICATE, sizeof(cert_auth_info_st), 1)) < 0) { gnutls_assert(); return ret; } if (data == NULL || data_size == 0) { /* no certificate was sent */ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); } info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE); if (info == NULL) return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); DECR_LEN(dsize, 3); size = _gnutls_read_uint24(p); p += 3; if (size != dsize) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); if (size == 0) return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND); i = dsize; while (i > 0) { DECR_LEN(dsize, 3); len = _gnutls_read_uint24(p); if (len == 0) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); DECR_LEN(dsize, len); p += len + 3; i -= len + 3; DECR_LEN(dsize, 2); len = _gnutls_read_uint16(p); DECR_LEN(dsize, len); i -= len + 2; p += len + 2; nentries++; } if (dsize != 0) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); /* this is unnecessary - keeping to avoid a regression due to a re-org * of the loop above */ if (nentries == 0) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); npeer_ocsp = 0; npeer_certs = 0; /* Ok we now allocate the memory to hold the * certificate list */ peer_certs = gnutls_calloc(nentries, sizeof(gnutls_datum_t)); if (peer_certs == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); peer_ocsp = gnutls_calloc(nentries, sizeof(gnutls_datum_t)); if (peer_ocsp == NULL) { ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); goto cleanup; } p = data+3; /* Now we start parsing the list (again). * We don't use DECR_LEN since the list has * been parsed before. */ ctx.session = session; for (j = 0; j < nentries; j++) { len = _gnutls_read_uint24(p); p += 3; ret = _gnutls_set_datum(&peer_certs[j], p, len); if (ret < 0) { gnutls_assert(); ret = GNUTLS_E_CERTIFICATE_ERROR; goto cleanup; } npeer_certs++; p += len; len = _gnutls_read_uint16(p); ctx.ocsp = &peer_ocsp[j]; ctx.idx = j; ret = _gnutls_extv_parse(&ctx, parse_cert_extension, p, len+2); if (ret < 0) { gnutls_assert(); goto cleanup; } p += len+2; npeer_ocsp++; } /* The OCSP entries match the certificate entries, although * the contents of each OCSP entry may be NULL. */ for(j=0;j<info->ncerts;j++) gnutls_free(info->raw_certificate_list[j].data); gnutls_free(info->raw_certificate_list); for(j=0;j<info->nocsp;j++) gnutls_free(info->raw_ocsp_list[j].data); gnutls_free(info->raw_ocsp_list); info->raw_certificate_list = peer_certs; info->ncerts = npeer_certs; info->raw_ocsp_list = peer_ocsp; info->nocsp = npeer_ocsp; return 0; cleanup: for(j=0;j<npeer_certs;j++) gnutls_free(peer_certs[j].data); for(j=0;j<npeer_ocsp;j++) gnutls_free(peer_ocsp[j].data); gnutls_free(peer_certs); gnutls_free(peer_ocsp); return ret; }
int _gnutls_recv_server_certificate_status(gnutls_session_t session) { uint8_t *data; int data_size; size_t r_size; gnutls_buffer_st buf; int ret; status_request_ext_st *priv = NULL; extension_priv_data_t epriv; ret = _gnutls_ext_get_session_data(session, GNUTLS_EXTENSION_STATUS_REQUEST, &epriv); if (ret < 0) return 0; priv = epriv.ptr; if (!priv->expect_cstatus) return 0; ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_STATUS, 0, &buf); if (ret < 0) return gnutls_assert_val_fatal(ret); priv->expect_cstatus = 0; data = buf.data; data_size = buf.length; /* minimum message is type (1) + response (3) + data */ if (data_size == 0) return 0; else if (data_size < 4) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); if (data[0] != 0x01) { gnutls_assert(); _gnutls_handshake_log("EXT[%p]: unknown status_type %d\n", session, data[0]); return 0; } DECR_LENGTH_COM(data_size, 1, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error); data++; DECR_LENGTH_COM(data_size, 3, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error); r_size = _gnutls_read_uint24(data); data += 3; DECR_LENGTH_COM(data_size, r_size, ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; goto error); ret = _gnutls_set_datum(&priv->response, data, r_size); if (ret < 0) goto error; ret = 0; error: _gnutls_buffer_clear(&buf); return ret; }