/* returns ciphertext which contains the headers too. This also * calculates the size in the header field. * */ int _gnutls_encrypt(gnutls_session_t session, const uint8_t *data, size_t data_size, size_t min_pad, mbuffer_st *bufel, content_type_t type, record_parameters_st *params) { gnutls_datum_t plaintext; const version_entry_st *vers = get_version(session); int ret; plaintext.data = (uint8_t *) data; plaintext.size = data_size; if (vers && vers->tls13_sem) { /* it fills the header, as it is included in the authenticated * data of the AEAD cipher. */ ret = encrypt_packet_tls13(session, _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size(bufel), &plaintext, min_pad, type, params); if (ret < 0) return gnutls_assert_val(ret); } else { ret = encrypt_packet(session, _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size (bufel), &plaintext, min_pad, type, params); if (ret < 0) return gnutls_assert_val(ret); } if (IS_DTLS(session)) _gnutls_write_uint16(ret, ((uint8_t *) _mbuffer_get_uhead_ptr(bufel)) + 11); else _gnutls_write_uint16(ret, ((uint8_t *) _mbuffer_get_uhead_ptr(bufel)) + 3); _mbuffer_set_udata_size(bufel, ret); _mbuffer_set_uhead_size(bufel, 0); return _mbuffer_get_udata_size(bufel); }
static unsigned is_aligned16(mbuffer_st * bufel, unsigned align_pos) { uint8_t * ptr = _mbuffer_get_udata_ptr(bufel); if (((size_t)(ptr+align_pos)) % ALIGN_SIZE == 0) return 1; else return 0; }
int _gnutls_send_server_certificate_status(gnutls_session_t session, int again) { mbuffer_st *bufel = NULL; uint8_t *data; int data_size = 0; int ret; status_request_ext_st *priv = NULL; extension_priv_data_t epriv; if (again == 0) { ret = _gnutls_ext_get_session_data(session, GNUTLS_EXTENSION_STATUS_REQUEST, &epriv); if (ret < 0) return 0; priv = epriv.ptr; if (!priv->response.size) return 0; data_size = priv->response.size + 4; bufel = _gnutls_handshake_alloc(session, data_size, data_size); if (!bufel) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); data = _mbuffer_get_udata_ptr(bufel); data[0] = 0x01; _gnutls_write_uint24(priv->response.size, &data[1]); memcpy(&data[4], priv->response.data, priv->response.size); _gnutls_free_datum(&priv->response); } return _gnutls_send_handshake(session, data_size ? bufel : NULL, GNUTLS_HANDSHAKE_CERTIFICATE_STATUS); }
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; }
/* This is a receive function for the gnutls handshake * protocol. Makes sure that we have received all data. * * htype is the next handshake packet expected. */ int _gnutls_parse_record_buffered_msgs(gnutls_session_t session) { gnutls_datum_t msg; mbuffer_st *bufel = NULL, *prev = NULL; int ret; size_t data_size; handshake_buffer_st *recv_buf = session->internals.handshake_recv_buffer; bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); if (bufel == NULL) return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; if (!IS_DTLS(session)) { ssize_t append, header_size; do { if (bufel->type != GNUTLS_HANDSHAKE) return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET); if (unlikely (session->internals.handshake_recv_buffer_size == 0 && msg.size < HANDSHAKE_HEADER_SIZE(session) && session->internals.handshake_header_recv_buffer.byte_length < HANDSHAKE_HEADER_SIZE(session) - msg.size)) { bufel = _mbuffer_head_pop_first(&session->internals.record_buffer); _mbuffer_enqueue(&session->internals.handshake_header_recv_buffer, bufel); break; } else if (session->internals.handshake_recv_buffer_size > 0 && recv_buf[0].length > recv_buf[0].data.length) { /* this is the rest of a previous message */ append = MIN(msg.size, recv_buf[0].length - recv_buf[0].data.length); ret = _gnutls_buffer_append_data(&recv_buf [0].data, msg.data, append); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_head_remove_bytes(&session-> internals. record_buffer, append); } else { /* received new message */ if (unlikely (session->internals. handshake_header_recv_buffer.length > 0)) { bufel = _mbuffer_head_pop_first(&session->internals. record_buffer); _mbuffer_enqueue(&session->internals. handshake_header_recv_buffer, bufel); ret = _mbuffer_linearize_align16(&session->internals. handshake_header_recv_buffer, get_total_headers(session)); if (ret < 0) return gnutls_assert_val(ret); bufel = _mbuffer_head_pop_first(&session->internals. handshake_header_recv_buffer); _mbuffer_head_push_first(&session->internals. record_buffer, bufel); } ret = parse_handshake_header(session, bufel, &recv_buf[0]); if (ret < 0) return gnutls_assert_val(ret); header_size = ret; session->internals. handshake_recv_buffer_size = 1; _mbuffer_set_uhead_size(bufel, header_size); data_size = MIN(recv_buf[0].length, _mbuffer_get_udata_size(bufel)); ret = _gnutls_buffer_append_data(&recv_buf [0].data, _mbuffer_get_udata_ptr (bufel), data_size); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_set_uhead_size(bufel, 0); _mbuffer_head_remove_bytes(&session-> internals. record_buffer, data_size + header_size); } /* if packet is complete then return it */ if (recv_buf[0].length == recv_buf[0].data.length) { return 0; } bufel = _mbuffer_head_get_first(&session->internals. record_buffer, &msg); } while (bufel != NULL); /* if we are here it means that the received packets were not * enough to complete the handshake packet. */ return gnutls_assert_val(GNUTLS_E_AGAIN); } else { /* DTLS */ handshake_buffer_st tmp; do { /* we now * 0. parse headers * 1. insert to handshake_recv_buffer * 2. sort handshake_recv_buffer on sequence numbers * 3. return first packet if completed or GNUTLS_E_AGAIN. */ do { if (bufel->type != GNUTLS_HANDSHAKE) { gnutls_assert(); goto next; /* ignore packet */ } _gnutls_handshake_buffer_init(&tmp); ret = parse_handshake_header(session, bufel, &tmp); if (ret < 0) { gnutls_assert(); _gnutls_audit_log(session, "Invalid handshake packet headers. Discarding.\n"); break; } _mbuffer_consume(&session->internals. record_buffer, bufel, ret); data_size = MIN(tmp.length, tmp.end_offset - tmp.start_offset + 1); ret = _gnutls_buffer_append_data(&tmp.data, _mbuffer_get_udata_ptr (bufel), data_size); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_consume(&session->internals. record_buffer, bufel, data_size); ret = merge_handshake_packet(session, &tmp); if (ret < 0) return gnutls_assert_val(ret); } while (_mbuffer_get_udata_size(bufel) > 0); prev = bufel; bufel = _mbuffer_dequeue(&session->internals. record_buffer, bufel); _mbuffer_xfree(&prev); continue; next: bufel = _mbuffer_head_get_next(bufel, NULL); } while (bufel != NULL); /* sort in descending order */ if (session->internals.handshake_recv_buffer_size > 1) qsort(recv_buf, session->internals. handshake_recv_buffer_size, sizeof(recv_buf[0]), handshake_compare); while (session->internals.handshake_recv_buffer_size > 0 && recv_buf[LAST_ELEMENT].sequence < session->internals.dtls.hsk_read_seq) { _gnutls_audit_log(session, "Discarded replayed handshake packet with sequence %d\n", recv_buf[LAST_ELEMENT].sequence); _gnutls_handshake_buffer_clear(&recv_buf [LAST_ELEMENT]); session->internals.handshake_recv_buffer_size--; } return 0; } }
/* This function fragments and transmits a previously buffered * outgoing message. It accepts mtu_data which is a buffer to * be reused (should be set to NULL initially). */ static inline int transmit_message (gnutls_session_t session, mbuffer_st *bufel, uint8_t **buf) { uint8_t *data, *mtu_data; int ret = 0; unsigned int offset, frag_len, data_size; const unsigned int mtu = gnutls_dtls_get_data_mtu(session) - DTLS_HANDSHAKE_HEADER_SIZE; if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC) { _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d)\n", session, bufel->handshake_sequence, _gnutls_handshake2str (bufel->htype), bufel->htype); return _gnutls_send_int (session, bufel->type, -1, bufel->epoch, _mbuffer_get_uhead_ptr(bufel), _mbuffer_get_uhead_size(bufel), 0); } if (*buf == NULL) *buf = gnutls_malloc(mtu + DTLS_HANDSHAKE_HEADER_SIZE); if (*buf == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); mtu_data = *buf; data = _mbuffer_get_udata_ptr( bufel); data_size = _mbuffer_get_udata_size(bufel); /* Write fixed headers */ /* Handshake type */ mtu_data[0] = (uint8_t) bufel->htype; /* Total length */ _gnutls_write_uint24 (data_size, &mtu_data[1]); /* Handshake sequence */ _gnutls_write_uint16 (bufel->handshake_sequence, &mtu_data[4]); /* Chop up and send handshake message into mtu-size pieces. */ for (offset=0; offset <= data_size; offset += mtu) { /* Calculate fragment length */ if(offset + mtu > data_size) frag_len = data_size - offset; else frag_len = mtu; /* Fragment offset */ _gnutls_write_uint24 (offset, &mtu_data[6]); /* Fragment length */ _gnutls_write_uint24 (frag_len, &mtu_data[9]); memcpy (&mtu_data[DTLS_HANDSHAKE_HEADER_SIZE], data+offset, frag_len); _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with " "length: %u, offset: %u, fragment length: %u\n", session, bufel->handshake_sequence, _gnutls_handshake2str (bufel->htype), bufel->htype, data_size, offset, frag_len); ret = _gnutls_send_int (session, bufel->type, bufel->htype, bufel->epoch, mtu_data, DTLS_HANDSHAKE_HEADER_SIZE + frag_len, 0); if (ret < 0) { gnutls_assert(); break; } } return ret; }
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; }
/* @ms: is the number of milliseconds to wait for data. Use zero for indefinite. * * This will receive record layer packets and add them to * application_data_buffer and handshake_data_buffer. * * If the htype is not -1 then handshake timeouts * will be enforced. */ ssize_t _gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, unsigned int ms) { uint64 *packet_sequence; gnutls_datum_t ciphertext; mbuffer_st* bufel = NULL, *decrypted = NULL; gnutls_datum_t t; int ret; unsigned int empty_fragments = 0; record_parameters_st *record_params; record_state_st *record_state; struct tls_record_st record; begin: if (empty_fragments > session->internals.priorities.max_empty_records) { gnutls_assert (); return GNUTLS_E_TOO_MANY_EMPTY_PACKETS; } if (session->internals.read_eof != 0) { /* if we have already read an EOF */ return 0; } else if (session_is_valid (session) != 0 || session->internals.may_not_read != 0) return gnutls_assert_val(GNUTLS_E_INVALID_SESSION); /* get the record state parameters */ ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params); if (ret < 0) return gnutls_assert_val (ret); /* Safeguard against processing data with an incomplete cipher state. */ if (!record_params->initialized) return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR); record_state = &record_params->read; /* receive headers */ ret = recv_headers(session, type, htype, &record, &ms); if (ret < 0) { ret = gnutls_assert_val_fatal(ret); goto recv_error; } if (IS_DTLS(session)) packet_sequence = &record.sequence; else packet_sequence = &record_state->sequence_number; /* Read the packet data and insert it to record_recv_buffer. */ ret = _gnutls_io_read_buffered (session, record.packet_size, record.type, &ms); if (ret != record.packet_size) { gnutls_assert(); goto recv_error; } /* ok now we are sure that we have read all the data - so * move on ! */ ret = _mbuffer_linearize (&session->internals.record_recv_buffer); if (ret < 0) return gnutls_assert_val(ret); bufel = _mbuffer_head_get_first (&session->internals.record_recv_buffer, NULL); if (bufel == NULL) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); /* We allocate the maximum possible to allow few compressed bytes to expand to a * full record. */ decrypted = _mbuffer_alloc(record.length, record.length); if (decrypted == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ciphertext.data = (uint8_t*)_mbuffer_get_udata_ptr(bufel) + record.header_size; ciphertext.size = record.length; /* decrypt the data we got. */ t.data = _mbuffer_get_udata_ptr(decrypted); t.size = _mbuffer_get_udata_size(decrypted); ret = _gnutls_decrypt (session, &ciphertext, &t, record.type, record_params, packet_sequence); if (ret >= 0) _mbuffer_set_udata_size(decrypted, ret); _mbuffer_head_remove_bytes (&session->internals.record_recv_buffer, record.header_size + record.length); if (ret < 0) { gnutls_assert(); _gnutls_audit_log(session, "Discarded message[%u] due to invalid decryption\n", (unsigned int)_gnutls_uint64touint32 (packet_sequence)); goto sanity_check_error; } /* check for duplicates. We check after the message * is processed and authenticated to avoid someone * messing with our windows. */ if (IS_DTLS(session) && session->internals.no_replay_protection == 0) { ret = _dtls_record_check(record_params, packet_sequence); if (ret < 0) { _gnutls_audit_log(session, "Discarded duplicate message[%u.%u]: %s\n", (unsigned int)record.sequence.i[0]*256 +(unsigned int)record.sequence.i[1], (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type)); goto sanity_check_error; } _gnutls_record_log ("REC[%p]: Decrypted Packet[%u.%u] %s(%d) with length: %d\n", session, (unsigned int)record.sequence.i[0]*256 +(unsigned int)record.sequence.i[1], (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted)); } else { _gnutls_record_log ("REC[%p]: Decrypted Packet[%u] %s(%d) with length: %d\n", session, (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted)); } /* increase sequence number */ if (!IS_DTLS(session) && sequence_increment (session, &record_state->sequence_number) != 0) { session_invalidate (session); gnutls_assert (); ret = GNUTLS_E_RECORD_LIMIT_REACHED; goto sanity_check_error; } /* (originally for) TLS 1.0 CBC protection. * Actually this code is called if we just received * an empty packet. An empty TLS packet is usually * sent to protect some vulnerabilities in the CBC mode. * In that case we go to the beginning and start reading * the next packet. */ if (_mbuffer_get_udata_size(decrypted) == 0) { _mbuffer_xfree(&decrypted); empty_fragments++; goto begin; } if (record.v2) decrypted->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2; else { uint8_t * p = _mbuffer_get_udata_ptr(decrypted); decrypted->htype = p[0]; } ret = record_add_to_buffers (session, &record, type, htype, packet_sequence, decrypted); /* bufel is now either deinitialized or buffered somewhere else */ if (ret < 0) return gnutls_assert_val(ret); return ret; discard: session->internals.dtls.packets_dropped++; /* discard the whole received fragment. */ bufel = _mbuffer_head_pop_first(&session->internals.record_recv_buffer); _mbuffer_xfree(&bufel); return gnutls_assert_val(GNUTLS_E_AGAIN); sanity_check_error: if (IS_DTLS(session)) { session->internals.dtls.packets_dropped++; ret = gnutls_assert_val(GNUTLS_E_AGAIN); goto cleanup; } session_unresumable (session); session_invalidate (session); cleanup: _mbuffer_xfree(&decrypted); return ret; recv_error: if (ret < 0 && (gnutls_error_is_fatal (ret) == 0 || ret == GNUTLS_E_TIMEDOUT)) return ret; if (type == GNUTLS_ALERT) /* we were expecting close notify */ { session_invalidate (session); gnutls_assert (); return 0; } if (IS_DTLS(session)) { goto discard; } session_invalidate (session); session_unresumable (session); if (ret == 0) return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; else return ret; }
/* This function behaves exactly like write(). The only difference is * that it accepts, the gnutls_session_t and the content_type_t of data to * send (if called by the user the Content is specific) * It is intended to transfer data, under the current session. * * Oct 30 2001: Removed capability to send data more than MAX_RECORD_SIZE. * This makes the function much easier to read, and more error resistant * (there were cases were the old function could mess everything up). * --nmav * * This function may accept a NULL pointer for data, and 0 for size, if * and only if the previous send was interrupted for some reason. * */ ssize_t _gnutls_send_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, unsigned int epoch_rel, const void *_data, size_t data_size, unsigned int mflags) { mbuffer_st *bufel; ssize_t cipher_size; int retval, ret; int send_data_size; uint8_t headers[MAX_RECORD_HEADER_SIZE]; int header_size; const uint8_t *data = _data; record_parameters_st *record_params; record_state_st *record_state; ret = _gnutls_epoch_get (session, epoch_rel, &record_params); if (ret < 0) return gnutls_assert_val(ret); /* Safeguard against processing data with an incomplete cipher state. */ if (!record_params->initialized) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); record_state = &record_params->write; /* Do not allow null pointer if the send buffer is empty. * If the previous send was interrupted then a null pointer is * ok, and means to resume. */ if (session->internals.record_send_buffer.byte_length == 0 && (data_size == 0 && _data == NULL)) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } if (type != GNUTLS_ALERT) /* alert messages are sent anyway */ if (session_is_valid (session) || session->internals.may_not_write != 0) { gnutls_assert (); return GNUTLS_E_INVALID_SESSION; } headers[0] = type; /* Use the default record version, if it is * set. */ copy_record_version (session, htype, &headers[1]); header_size = RECORD_HEADER_SIZE(session); /* Adjust header length and add sequence for DTLS */ if (IS_DTLS(session)) memcpy(&headers[3], &record_state->sequence_number.i, 8); _gnutls_record_log ("REC[%p]: Preparing Packet %s(%d) with length: %d\n", session, _gnutls_packet2str (type), type, (int) data_size); if (data_size > MAX_RECORD_SEND_SIZE(session)) { if (IS_DTLS(session)) { gnutls_assert (); return GNUTLS_E_LARGE_PACKET; } send_data_size = MAX_RECORD_SEND_SIZE(session); } else send_data_size = data_size; /* Only encrypt if we don't have data to send * from the previous run. - probably interrupted. */ if (mflags != 0 && session->internals.record_send_buffer.byte_length > 0) { ret = _gnutls_io_write_flush (session); if (ret > 0) cipher_size = ret; else cipher_size = 0; retval = session->internals.record_send_buffer_user_size; } else { /* now proceed to packet encryption */ cipher_size = send_data_size + MAX_RECORD_OVERHEAD + CIPHER_SLACK_SIZE; bufel = _mbuffer_alloc (cipher_size, cipher_size); if (bufel == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ret = _gnutls_encrypt (session, headers, header_size, data, send_data_size, _mbuffer_get_udata_ptr (bufel), cipher_size, type, record_params); if (ret <= 0) { gnutls_assert (); if (ret == 0) ret = GNUTLS_E_ENCRYPTION_FAILED; gnutls_free (bufel); return ret; /* error */ } cipher_size = ret; retval = send_data_size; session->internals.record_send_buffer_user_size = send_data_size; /* increase sequence number */ if (sequence_increment (session, &record_state->sequence_number) != 0) { session_invalidate (session); gnutls_free (bufel); return gnutls_assert_val(GNUTLS_E_RECORD_LIMIT_REACHED); } _mbuffer_set_udata_size (bufel, cipher_size); ret = _gnutls_io_write_buffered (session, bufel, mflags); } if (ret != cipher_size) { /* If we have sent any data then just return * the error value. Do not invalidate the session. */ if (ret < 0 && gnutls_error_is_fatal (ret) == 0) return gnutls_assert_val(ret); if (ret > 0) ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); session_unresumable (session); session->internals.may_not_write = 1; return gnutls_assert_val(ret); } session->internals.record_send_buffer_user_size = 0; _gnutls_record_log ("REC[%p]: Sent Packet[%d] %s(%d) in epoch %d and length: %d\n", session, (unsigned int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (type), type, (int) record_params->epoch, (int) cipher_size); return retval; }
/* * Return zero if session tickets haven't been enabled. */ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again) { mbuffer_st *bufel = NULL; uint8_t *data = NULL, *p; int data_size = 0; int ret; gnutls_datum_t state = { NULL, 0 }; uint16_t epoch_saved = session->security_parameters.epoch_write; gnutls_datum_t ticket_data; if (again == 0) { if (session->internals.flags & GNUTLS_NO_TICKETS) return 0; if (!session->internals.session_ticket_renew) return 0; _gnutls_handshake_log ("HSK[%p]: sending session ticket\n", session); /* XXX: Temporarily set write algorithms to be used. _gnutls_write_connection_state_init() does this job, but it also triggers encryption, while NewSessionTicket should not be encrypted in the record layer. */ ret = _gnutls_epoch_set_keys(session, session->security_parameters. epoch_next, 0); if (ret < 0) { gnutls_assert(); return ret; } /* Under TLS1.2 with session tickets, the session ID is used for different * purposes than the TLS1.0 session ID. Ensure that there is an internally * set value which the server will see on the original and resumed sessions */ if (session->internals.resumed != RESUME_TRUE) { ret = _gnutls_generate_session_id(session->security_parameters. session_id, &session->security_parameters. session_id_size); if (ret < 0) { gnutls_assert(); return ret; } } session->security_parameters.epoch_write = session->security_parameters.epoch_next; /* Pack security parameters. */ ret = _gnutls_session_pack(session, &state); if (ret < 0) { gnutls_assert(); return ret; } /* Generate an encrypted ticket */ ret = _gnutls_encrypt_session_ticket(session, &state, &ticket_data); session->security_parameters.epoch_write = epoch_saved; _gnutls_free_datum(&state); if (ret < 0) { gnutls_assert(); return ret; } bufel = _gnutls_handshake_alloc(session, 4 + 2 + ticket_data.size); if (!bufel) { gnutls_assert(); _gnutls_free_datum(&ticket_data); return GNUTLS_E_MEMORY_ERROR; } data = _mbuffer_get_udata_ptr(bufel); p = data; _gnutls_write_uint32(session->internals.expire_time, p); p += 4; _gnutls_write_uint16(ticket_data.size, p); p += 2; memcpy(p, ticket_data.data, ticket_data.size); p += ticket_data.size; _gnutls_free_datum(&ticket_data); data_size = p - data; session->internals.hsk_flags |= HSK_TLS12_TICKET_SENT; } return _gnutls_send_handshake(session, data_size ? bufel : NULL, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET); }
/* * Processes a heartbeat message. */ int _gnutls_heartbeat_handle(gnutls_session_t session, mbuffer_st * bufel) { int ret; unsigned type; unsigned pos; uint8_t *msg = _mbuffer_get_udata_ptr(bufel); size_t hb_len, len = _mbuffer_get_udata_size(bufel); if (gnutls_heartbeat_allowed (session, GNUTLS_HB_PEER_ALLOWED_TO_SEND) == 0) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); if (len < 4) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); pos = 0; type = msg[pos++]; hb_len = _gnutls_read_uint16(&msg[pos]); if (hb_len > len - 3) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); pos += 2; switch (type) { case HEARTBEAT_REQUEST: _gnutls_buffer_reset(&session->internals.hb_remote_data); ret = _gnutls_buffer_resize(&session->internals. hb_remote_data, hb_len); if (ret < 0) return gnutls_assert_val(ret); if (hb_len > 0) memcpy(session->internals.hb_remote_data.data, &msg[pos], hb_len); session->internals.hb_remote_data.length = hb_len; return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PING_RECEIVED); case HEARTBEAT_RESPONSE: if (hb_len != session->internals.hb_local_data.length) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); if (hb_len > 0 && memcmp(&msg[pos], session->internals.hb_local_data.data, hb_len) != 0) { if (IS_DTLS(session)) return gnutls_assert_val(GNUTLS_E_AGAIN); /* ignore it */ else return gnutls_assert_val (GNUTLS_E_UNEXPECTED_PACKET); } _gnutls_buffer_reset(&session->internals.hb_local_data); return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PONG_RECEIVED); default: _gnutls_record_log ("REC[%p]: HB: received unknown type %u\n", session, type); return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); } }
/* This is a receive function for the gnutls handshake * protocol. Makes sure that we have received all data. */ static int parse_record_buffered_msgs (gnutls_session_t session, gnutls_handshake_description_t htype, handshake_buffer_st * hsk) { gnutls_datum_t msg; mbuffer_st* bufel = NULL, *prev = NULL; int ret; size_t data_size; handshake_buffer_st* recv_buf = session->internals.handshake_recv_buffer; bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); if (bufel == NULL) return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; if (!IS_DTLS(session)) { ssize_t remain, append, header_size; do { if (bufel->type != GNUTLS_HANDSHAKE) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); /* if we have a half received message the complete it. */ remain = recv_buf[0].length - recv_buf[0].data.length; /* this is the rest of a previous message */ if (session->internals.handshake_recv_buffer_size > 0 && recv_buf[0].length > 0 && remain > 0) { if (msg.size <= remain) append = msg.size; else append = remain; ret = _gnutls_buffer_append_data(&recv_buf[0].data, msg.data, append); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_head_remove_bytes(&session->internals.record_buffer, append); } else /* received new message */ { ret = parse_handshake_header(session, bufel, htype, &recv_buf[0]); if (ret < 0) return gnutls_assert_val(ret); header_size = ret; session->internals.handshake_recv_buffer_size = 1; _mbuffer_set_uhead_size(bufel, header_size); data_size = MIN(recv_buf[0].length, _mbuffer_get_udata_size(bufel)); ret = _gnutls_buffer_append_data(&recv_buf[0].data, _mbuffer_get_udata_ptr(bufel), data_size); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_set_uhead_size(bufel, 0); _mbuffer_head_remove_bytes(&session->internals.record_buffer, data_size+header_size); if (cmp_hsk_types(htype, recv_buf[0].htype) == 0) { /* an unexpected packet */ hsk->htype = recv_buf[0].htype; return gnutls_assert_val(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET); } } /* if packet is complete then return it */ if (recv_buf[0].length == recv_buf[0].data.length) { return get_last_packet(session, htype, hsk); } bufel = _mbuffer_head_get_first(&session->internals.record_buffer, &msg); } while(bufel != NULL); /* if we are here it means that the received packets were not * enough to complete the handshake packet. */ return gnutls_assert_val(GNUTLS_E_AGAIN); } else /* DTLS */ { handshake_buffer_st tmp; do { /* we now * 0. parse headers * 1. insert to handshake_recv_buffer * 2. sort handshake_recv_buffer on sequence numbers * 3. return first packet if completed or GNUTLS_E_AGAIN. */ do { if (bufel->type != GNUTLS_HANDSHAKE) { gnutls_assert(); goto next; /* ignore packet */ } _gnutls_handshake_buffer_init(&tmp); ret = parse_handshake_header(session, bufel, htype, &tmp); if (ret < 0) { gnutls_assert(); _gnutls_audit_log("Invalid handshake packet headers. Discarding.\n"); break; } _mbuffer_consume(&session->internals.record_buffer, bufel, ret); data_size = MIN(tmp.length, tmp.end_offset-tmp.start_offset+1); ret = _gnutls_buffer_append_data(&tmp.data, _mbuffer_get_udata_ptr(bufel), data_size); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_consume(&session->internals.record_buffer, bufel, data_size); ret = merge_handshake_packet(session, &tmp); if (ret < 0) return gnutls_assert_val(ret); } while(_mbuffer_get_udata_size(bufel) > 0); prev = bufel; bufel = _mbuffer_dequeue(&session->internals.record_buffer, bufel); _mbuffer_xfree(&prev); continue; next: bufel = _mbuffer_head_get_next(bufel, NULL); } while(bufel != NULL); /* sort in descending order */ if (session->internals.handshake_recv_buffer_size > 1) qsort(recv_buf, session->internals.handshake_recv_buffer_size, sizeof(recv_buf[0]), handshake_compare); while(session->internals.handshake_recv_buffer_size > 0 && recv_buf[LAST_ELEMENT].sequence < session->internals.dtls.hsk_read_seq) { _gnutls_audit_log("Discarded replayed handshake packet with sequence %d\n", recv_buf[LAST_ELEMENT].sequence); _gnutls_handshake_buffer_clear(&recv_buf[LAST_ELEMENT]); session->internals.handshake_recv_buffer_size--; } return get_last_packet(session, htype, hsk); } }
/* returns ciphertext which contains the headers too. This also * calculates the size in the header field. * */ int _gnutls_encrypt(gnutls_session_t session, const uint8_t * data, size_t data_size, size_t min_pad, mbuffer_st * bufel, content_type_t type, record_parameters_st * params) { gnutls_datum_t comp; int free_comp = 0; int ret; if (data_size == 0 || is_write_comp_null(params) == 0) { comp.data = (uint8_t *) data; comp.size = data_size; } else { /* Here comp is allocated and must be * freed. */ free_comp = 1; comp.size = _mbuffer_get_udata_size(bufel); comp.data = gnutls_malloc(comp.size); if (comp.data == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); ret = _gnutls_compress(¶ms->write.compression_state, data, data_size, comp.data, comp.size, session->internals.priorities. stateless_compression); if (ret < 0) { gnutls_free(comp.data); return gnutls_assert_val(ret); } comp.size = ret; } ret = compressed_to_ciphertext(session, _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size (bufel), &comp, min_pad, type, params); if (free_comp) gnutls_free(comp.data); if (ret < 0) return gnutls_assert_val(ret); if (IS_DTLS(session)) _gnutls_write_uint16(ret, ((uint8_t *) _mbuffer_get_uhead_ptr(bufel)) + 11); else _gnutls_write_uint16(ret, ((uint8_t *) _mbuffer_get_uhead_ptr(bufel)) + 3); _mbuffer_set_udata_size(bufel, ret); _mbuffer_set_uhead_size(bufel, 0); return _mbuffer_get_udata_size(bufel); }
int _gnutls_send_new_session_ticket(gnutls_session_t session, int again) { mbuffer_st *bufel = NULL; uint8_t *data = NULL, *p; int data_size = 0; int ret; struct ticket_st ticket; uint16_t ticket_len; session_ticket_ext_st *priv = NULL; extension_priv_data_t epriv; uint16_t epoch_saved = session->security_parameters.epoch_write; if (again == 0) { ret = _gnutls_ext_get_session_data(session, GNUTLS_EXTENSION_SESSION_TICKET, &epriv); if (ret < 0) return 0; priv = epriv; if (!priv->session_ticket_renew) return 0; /* XXX: Temporarily set write algorithms to be used. _gnutls_write_connection_state_init() does this job, but it also triggers encryption, while NewSessionTicket should not be encrypted in the record layer. */ ret = _gnutls_epoch_set_keys(session, session->security_parameters. epoch_next); if (ret < 0) { gnutls_assert(); return ret; } session->security_parameters.epoch_write = session->security_parameters.epoch_next; ret = encrypt_ticket(session, priv, &ticket); session->security_parameters.epoch_write = epoch_saved; if (ret < 0) { gnutls_assert(); return ret; } ticket_len = KEY_NAME_SIZE + IV_SIZE + 2 + ticket.encrypted_state_len + MAC_SIZE; bufel = _gnutls_handshake_alloc(session, 4 + 2 + ticket_len); if (!bufel) { gnutls_assert(); gnutls_free(ticket.encrypted_state); return GNUTLS_E_MEMORY_ERROR; } data = _mbuffer_get_udata_ptr(bufel); p = data; _gnutls_write_uint32(session->internals.expire_time, p); p += 4; _gnutls_write_uint16(ticket_len, p); p += 2; memcpy(p, ticket.key_name, KEY_NAME_SIZE); p += KEY_NAME_SIZE; memcpy(p, ticket.IV, IV_SIZE); p += IV_SIZE; _gnutls_write_uint16(ticket.encrypted_state_len, p); p += 2; memcpy(p, ticket.encrypted_state, ticket.encrypted_state_len); gnutls_free(ticket.encrypted_state); p += ticket.encrypted_state_len; memcpy(p, ticket.mac, MAC_SIZE); p += MAC_SIZE; data_size = p - data; session->internals.ticket_sent = 1; } return _gnutls_send_handshake(session, data_size ? bufel : NULL, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET); }