/* Increments the sequence value */ inline static int sequence_increment(gnutls_session_t session, uint64 * value) { if (IS_DTLS(session)) { return _gnutls_uint48pp(value); } else { return _gnutls_uint64pp(value); } }
/* 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 sizeofdata, unsigned int mflags) { mbuffer_st *bufel; ssize_t cipher_size; int retval, ret; int data2send_size; uint8_t headers[5]; 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) { gnutls_assert (); return ret; } /* Safeguard against processing data with an incomplete cipher state. */ if (!record_params->initialized) { gnutls_assert (); return 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 && (sizeofdata == 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]); _gnutls_record_log ("REC[%p]: Sending Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (type), type, (int) sizeofdata); if (sizeofdata > MAX_RECORD_SEND_SIZE) data2send_size = MAX_RECORD_SEND_SIZE; else data2send_size = sizeofdata; /* 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 = data2send_size + MAX_RECORD_OVERHEAD; bufel = _mbuffer_alloc (cipher_size, cipher_size); if (bufel == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } cipher_size = _gnutls_encrypt (session, headers, RECORD_HEADER_SIZE, data, data2send_size, _mbuffer_get_udata_ptr (bufel), cipher_size, type, (session->internals.priorities.no_padding == 0) ? 1 : 0, record_params); if (cipher_size <= 0) { gnutls_assert (); if (cipher_size == 0) cipher_size = GNUTLS_E_ENCRYPTION_FAILED; gnutls_free (bufel); return cipher_size; /* error */ } retval = data2send_size; session->internals.record_send_buffer_user_size = data2send_size; /* increase sequence number */ if (_gnutls_uint64pp (&record_state->sequence_number) != 0) { session_invalidate (session); gnutls_assert (); gnutls_free (bufel); return 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 (ret < 0 && gnutls_error_is_fatal (ret) == 0) { /* If we have sent any data then just return * the error value. Do not invalidate the session. */ gnutls_assert (); return ret; } if (ret > 0) { gnutls_assert (); ret = GNUTLS_E_INTERNAL_ERROR; } session_unresumable (session); session->internals.may_not_write = 1; gnutls_assert (); return ret; } session->internals.record_send_buffer_user_size = 0; _gnutls_record_log ("REC[%p]: Sent Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (type), type, (int) cipher_size); return retval; }
/* This function behaves exactly like read(). The only difference is * that it accepts the gnutls_session_t and the content_type_t of data to * receive (if called by the user the Content is Userdata only) * It is intended to receive data, under the current session. * * The gnutls_handshake_description_t was introduced to support SSL V2.0 client hellos. */ ssize_t _gnutls_recv_int (gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, opaque * data, size_t sizeofdata) { int decrypted_length; opaque version[2]; content_type_t recv_type; uint16_t length; uint8_t *ciphertext; int ret, ret2; uint16_t header_size; int empty_packet = 0; gnutls_datum_t data_enc, tmp; record_parameters_st *record_params; record_state_st *record_state; ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params); if (ret < 0) { gnutls_assert (); return ret; } /* Safeguard against processing data with an incomplete cipher state. */ if (!record_params->initialized) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } record_state = &record_params->read; if (type != GNUTLS_ALERT && (sizeofdata == 0 || data == NULL)) { return GNUTLS_E_INVALID_REQUEST; } begin: if (empty_packet > MAX_EMPTY_PACKETS_SEQUENCE) { 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) { gnutls_assert (); return GNUTLS_E_INVALID_SESSION; } /* If we have enough data in the cache do not bother receiving * a new packet. (in order to flush the cache) */ ret = check_buffers (session, type, data, sizeofdata); if (ret != 0) return ret; /* default headers for TLS 1.0 */ header_size = RECORD_HEADER_SIZE; if ((ret = _gnutls_io_read_buffered (session, header_size, -1)) != header_size) { if (ret < 0 && gnutls_error_is_fatal (ret) == 0) return ret; session_invalidate (session); if (type == GNUTLS_ALERT) { gnutls_assert (); return 0; /* we were expecting close notify */ } session_unresumable (session); gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } ret = _mbuffer_linearize (&session->internals.record_recv_buffer); if (ret != 0) { gnutls_assert (); return ret; } _mbuffer_get_first (&session->internals.record_recv_buffer, &data_enc); if ((ret = record_check_headers (session, data_enc.data, type, htype, &recv_type, version, &length, &header_size)) < 0) { gnutls_assert (); return ret; } /* Here we check if the Type of the received packet is * ok. */ if ((ret = check_recv_type (recv_type)) < 0) { gnutls_assert (); return ret; } /* Here we check if the advertized version is the one we * negotiated in the handshake. */ if ((ret = record_check_version (session, htype, version)) < 0) { gnutls_assert (); session_invalidate (session); return ret; } _gnutls_record_log ("REC[%p]: Expected Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (type), type, (int) sizeofdata); _gnutls_record_log ("REC[%p]: Received Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (recv_type), recv_type, length); if (length > MAX_RECV_SIZE) { _gnutls_record_log ("REC[%p]: FATAL ERROR: Received packet with length: %d\n", session, length); session_unresumable (session); session_invalidate (session); gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } /* check if we have that data into buffer. */ if ((ret = _gnutls_io_read_buffered (session, header_size + length, recv_type)) != header_size + length) { if (ret < 0 && gnutls_error_is_fatal (ret) == 0) return ret; session_unresumable (session); session_invalidate (session); gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } /* ok now we are sure that we can read all the data - so * move on ! */ ret = _mbuffer_linearize (&session->internals.record_recv_buffer); if (ret != 0) { gnutls_assert (); return ret; } _mbuffer_get_first (&session->internals.record_recv_buffer, &data_enc); ciphertext = &data_enc.data[header_size]; ret = get_temp_recv_buffer (session, &tmp); if (ret < 0) { gnutls_assert (); return ret; } /* decrypt the data we got. */ ret = _gnutls_decrypt (session, ciphertext, length, tmp.data, tmp.size, recv_type, record_params); if (ret < 0) { session_unresumable (session); session_invalidate (session); gnutls_assert (); return ret; } _mbuffer_remove_bytes (&session->internals.record_recv_buffer, header_size + length); decrypted_length = ret; /* Check if this is a CHANGE_CIPHER_SPEC */ if (type == GNUTLS_CHANGE_CIPHER_SPEC && recv_type == GNUTLS_CHANGE_CIPHER_SPEC) { _gnutls_record_log ("REC[%p]: ChangeCipherSpec Packet was received\n", session); if ((size_t) ret != sizeofdata) { /* sizeofdata should be 1 */ gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } memcpy (data, tmp.data, sizeofdata); return ret; } _gnutls_record_log ("REC[%p]: Decrypted Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&record_state->sequence_number), _gnutls_packet2str (recv_type), recv_type, decrypted_length); /* increase sequence number */ if (_gnutls_uint64pp (&record_state->sequence_number) != 0) { session_invalidate (session); gnutls_assert (); return GNUTLS_E_RECORD_LIMIT_REACHED; } ret = record_check_type (session, recv_type, type, htype, tmp.data, decrypted_length); if (ret < 0) { if (ret == GNUTLS_E_INT_RET_0) return 0; gnutls_assert (); return ret; } /* Get Application data from buffer */ if ((recv_type == type) && (type == GNUTLS_APPLICATION_DATA || type == GNUTLS_HANDSHAKE || type == GNUTLS_INNER_APPLICATION)) { ret = _gnutls_record_buffer_get (type, session, data, sizeofdata); if (ret < 0) { gnutls_assert (); return ret; } /* if the buffer just got empty */ if (_gnutls_record_buffer_get_size (type, session) == 0) { if ((ret2 = _gnutls_io_clear_peeked_data (session)) < 0) { gnutls_assert (); return ret2; } } } else { gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET; /* we didn't get what we wanted to */ } /* (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 (ret == 0) { empty_packet++; goto begin; } 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, const void *_data, size_t sizeofdata) { uint8_t *cipher; int cipher_size; int retval, ret; int data2send_size; uint8_t headers[5]; const uint8_t *data = _data; int erecord_size = 0; opaque *erecord = NULL; /* 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.length == 0 && (sizeofdata == 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]); _gnutls_record_log ("REC[%x]: Sending Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&session->connection_state. write_sequence_number), _gnutls_packet2str (type), type, sizeofdata); if (sizeofdata > MAX_RECORD_SEND_SIZE) data2send_size = MAX_RECORD_SEND_SIZE; else data2send_size = sizeofdata; /* Only encrypt if we don't have data to send * from the previous run. - probably interrupted. */ if (session->internals.record_send_buffer.length > 0) { ret = _gnutls_io_write_flush (session); if (ret > 0) cipher_size = ret; else cipher_size = 0; cipher = NULL; retval = session->internals.record_send_buffer_user_size; } else { /* now proceed to packet encryption */ cipher_size = data2send_size + MAX_RECORD_OVERHEAD; cipher = gnutls_malloc (cipher_size); if (cipher == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } cipher_size = _gnutls_encrypt (session, headers, RECORD_HEADER_SIZE, data, data2send_size, cipher, cipher_size, type, 1); if (cipher_size <= 0) { gnutls_assert (); if (cipher_size == 0) cipher_size = GNUTLS_E_ENCRYPTION_FAILED; gnutls_afree (erecord); gnutls_free (cipher); return cipher_size; /* error */ } retval = data2send_size; session->internals.record_send_buffer_user_size = data2send_size; /* increase sequence number */ if (_gnutls_uint64pp (&session->connection_state.write_sequence_number) != 0) { session_invalidate (session); gnutls_assert (); gnutls_afree (erecord); gnutls_free (cipher); return GNUTLS_E_RECORD_LIMIT_REACHED; } ret = _gnutls_io_write_buffered2 (session, erecord, erecord_size, cipher, cipher_size); gnutls_afree (erecord); gnutls_free (cipher); } if (ret != cipher_size + erecord_size) { if (ret < 0 && gnutls_error_is_fatal (ret) == 0) { /* If we have sent any data then just return * the error value. Do not invalidate the session. */ gnutls_assert (); return ret; } if (ret > 0) { gnutls_assert (); ret = GNUTLS_E_INTERNAL_ERROR; } session_unresumable (session); session->internals.may_not_write = 1; gnutls_assert (); return ret; } session->internals.record_send_buffer_user_size = 0; _gnutls_record_log ("REC[%x]: Sent Packet[%d] %s(%d) with length: %d\n", session, (int) _gnutls_uint64touint32 (&session-> connection_state. write_sequence_number), _gnutls_packet2str (type), type, cipher_size); return retval; }