/* 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); }
/* 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 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. * * @type: The content type to send * @htype: If this is a handshake message then the handshake type * @epoch_rel: %EPOCH_READ_* or %EPOCH_WRITE_* * @data: the data to be sent * @data_size: the size of the @data * @target_length: @data_size + minimum required padding * @mflags: zero or %MBUFFER_FLUSH * * 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_tlen_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, size_t target_length, unsigned int mflags) { mbuffer_st *bufel; ssize_t cipher_size; int retval, ret; int send_data_size; uint8_t *headers; 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; } if (data_size > MAX_USER_SEND_SIZE(session)) { if (IS_DTLS(session)) return gnutls_assert_val(GNUTLS_E_LARGE_PACKET); send_data_size = MAX_USER_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 { if (unlikely((send_data_size == 0 && target_length == 0))) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); /* now proceed to packet encryption */ cipher_size = MAX_RECORD_SEND_SIZE(session); bufel = _mbuffer_alloc (0, cipher_size+CIPHER_SLACK_SIZE); if (bufel == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); headers = _mbuffer_get_uhead_ptr(bufel); 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 and target length: %d\n", session, _gnutls_packet2str (type), type, (int) data_size, (int) target_length); _mbuffer_set_udata_size(bufel, cipher_size); _mbuffer_set_uhead_size(bufel, header_size); ret = _gnutls_encrypt (session, data, send_data_size, target_length, bufel, type, record_params); if (ret <= 0) { gnutls_assert (); if (ret == 0) ret = GNUTLS_E_ENCRYPTION_FAILED; gnutls_free (bufel); return ret; /* error */ } cipher_size = _mbuffer_get_udata_size(bufel); 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); } 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; }
/* 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); }