int _gnutls_epoch_set_cipher_suite (gnutls_session_t session, int epoch_rel, cipher_suite_st * suite) { gnutls_cipher_algorithm_t cipher_algo; gnutls_mac_algorithm_t mac_algo; record_parameters_st *params; int ret; ret = _gnutls_epoch_get (session, epoch_rel, ¶ms); if (ret < 0) return gnutls_assert_val (ret); if (params->initialized || params->cipher_algorithm != GNUTLS_CIPHER_UNKNOWN || params->mac_algorithm != GNUTLS_MAC_UNKNOWN) return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR); cipher_algo = _gnutls_cipher_suite_get_cipher_algo (suite); mac_algo = _gnutls_cipher_suite_get_mac_algo (suite); if (_gnutls_cipher_is_ok (cipher_algo) != 0 || _gnutls_mac_is_ok (mac_algo) != 0) return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM); params->cipher_algorithm = cipher_algo; params->mac_algorithm = mac_algo; return 0; }
int _gnutls_epoch_set_compression(gnutls_session_t session, int epoch_rel, gnutls_compression_method_t comp_algo) { record_parameters_st *params; int ret; ret = _gnutls_epoch_get(session, epoch_rel, ¶ms); if (ret < 0) return gnutls_assert_val(ret); if (params->initialized || params->compression_algorithm != GNUTLS_COMP_UNKNOWN) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); if (_gnutls_compression_is_ok(comp_algo) != 0) return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM); params->compression_algorithm = comp_algo; return 0; }
int _gnutls_epoch_set_keys (gnutls_session_t session, uint16_t epoch) { int hash_size; int IV_size; int key_size, export_flag; gnutls_cipher_algorithm_t cipher_algo; gnutls_mac_algorithm_t mac_algo; gnutls_compression_method_t comp_algo; record_parameters_st *params; int ret; gnutls_protocol_t ver = gnutls_protocol_get_version (session); ret = _gnutls_epoch_get (session, epoch, ¶ms); if (ret < 0) return gnutls_assert_val (ret); if (params->initialized) return 0; _gnutls_record_log ("REC[%p]: Initializing epoch #%u\n", session, params->epoch); cipher_algo = params->cipher_algorithm; mac_algo = params->mac_algorithm; comp_algo = params->compression_algorithm; if (_gnutls_cipher_is_ok (cipher_algo) != 0 || _gnutls_mac_is_ok (mac_algo) != 0) return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR); if (_gnutls_compression_is_ok (comp_algo) != 0) return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM); IV_size = _gnutls_cipher_get_iv_size (cipher_algo); key_size = gnutls_cipher_get_key_size (cipher_algo); export_flag = _gnutls_cipher_get_export_flag (cipher_algo); hash_size = _gnutls_hmac_get_algo_len (mac_algo); ret = _gnutls_set_keys (session, params, hash_size, IV_size, key_size, export_flag); if (ret < 0) return gnutls_assert_val (ret); ret = _gnutls_init_record_state (params, ver, 1, ¶ms->read); if (ret < 0) return gnutls_assert_val (ret); ret = _gnutls_init_record_state (params, ver, 0, ¶ms->write); if (ret < 0) return gnutls_assert_val (ret); params->record_sw_size = 0; _gnutls_record_log ("REC[%p]: Epoch #%u ready\n", session, params->epoch); params->initialized = 1; return 0; }
int _gnutls_epoch_get_compression(gnutls_session_t session, int epoch) { record_parameters_st *params; int ret; ret = _gnutls_epoch_get (session, epoch, ¶ms); if (ret < 0) return GNUTLS_COMP_UNKNOWN; return params->compression_algorithm; }
inline static int is_read_comp_null (gnutls_session_t session) { record_parameters_st *record_params; _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params); if (record_params->compression_algorithm == GNUTLS_COMP_NULL) return 0; return 1; }
/** * gnutls_compression_get: * @session: is a #gnutls_session_t structure. * * Get currently used compression algorithm. * * Returns: the currently used compression method, a * #gnutls_compression_method_t value. **/ gnutls_compression_method_t gnutls_compression_get (gnutls_session_t session) { record_parameters_st *record_params; int ret; ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params); if (ret < 0) return gnutls_assert_val(GNUTLS_COMP_NULL); return record_params->compression_algorithm; }
/** * gnutls_mac_get: * @session: is a #gnutls_session_t structure. * * Get currently used MAC algorithm. * * Returns: the currently used mac algorithm, a * #gnutls_mac_algorithm_t value. **/ gnutls_mac_algorithm_t gnutls_mac_get (gnutls_session_t session) { record_parameters_st *record_params; int ret; ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params); if (ret < 0) return gnutls_assert_val(GNUTLS_MAC_NULL); return record_params->mac_algorithm; }
/** * gnutls_cipher_get: * @session: is a #gnutls_session_t structure. * * Get currently used cipher. * * Returns: the currently used cipher, a #gnutls_cipher_algorithm_t * type. **/ gnutls_cipher_algorithm_t gnutls_cipher_get(gnutls_session_t session) { record_parameters_st *record_params; int ret; ret = _gnutls_epoch_get(session, EPOCH_READ_CURRENT, &record_params); if (ret < 0) return gnutls_assert_val(GNUTLS_CIPHER_NULL); return record_params->cipher->id; }
/* returns overhead imposed by the record layer (encryption/compression) * etc. It does not include the record layer headers, since the caller * needs to cope with rounding to multiples of blocksize, and the header * is outside that. * * blocksize: will contain the block size when padding may be required or 1 * * It may return a negative error code on error. */ static int record_overhead_rt(gnutls_session_t session, unsigned int *blocksize) { record_parameters_st *params; int total = 0, ret, iv_size; if (session->internals.initial_negotiation_completed == 0) return GNUTLS_E_INVALID_REQUEST; ret = _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, ¶ms); if (ret < 0) return gnutls_assert_val(ret); /* requires padding */ iv_size = _gnutls_cipher_get_iv_size(params->cipher_algorithm); if (_gnutls_cipher_is_block (params->cipher_algorithm) == CIPHER_BLOCK) { *blocksize = iv_size; total += iv_size; /* iv_size == block_size in DTLS */ /* We always pad with at least one byte; never 0. */ total++; } else { *blocksize = 1; } if (params->mac_algorithm == GNUTLS_MAC_AEAD) total += _gnutls_cipher_get_tag_size(params->cipher_algorithm); else { ret = _gnutls_hmac_get_algo_len(params->mac_algorithm); if (ret < 0) return gnutls_assert_val(ret); total+=ret; } if (params->compression_algorithm != GNUTLS_COMP_NULL) total += EXTRA_COMP_SIZE; return total; }
/** * gnutls_dtls_prestate_set: * @session: a new session * @prestate: contains the client's prestate * * This function will associate the prestate acquired by * the cookie authentication with the client, with the newly * established session. * * Since: 3.0 **/ void gnutls_dtls_prestate_set(gnutls_session_t session, gnutls_dtls_prestate_st* prestate) { record_parameters_st *params; int ret; if (prestate == NULL) return; /* we do not care about read_params, since we accept anything * the peer sends. */ ret = _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, ¶ms); if (ret < 0) return; params->write.sequence_number.i[7] = prestate->record_seq; session->internals.dtls.hsk_read_seq = prestate->hsk_read_seq; session->internals.dtls.hsk_write_seq = prestate->hsk_write_seq + 1; }
int _gnutls_epoch_set_cipher_suite(gnutls_session_t session, int epoch_rel, const uint8_t suite[2]) { const cipher_entry_st *cipher_algo; const mac_entry_st *mac_algo; record_parameters_st *params; const gnutls_cipher_suite_entry_st *cs; int ret; ret = _gnutls_epoch_get(session, epoch_rel, ¶ms); if (ret < 0) return gnutls_assert_val(ret); if (params->initialized || params->cipher != NULL || params->mac != NULL) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); cs = ciphersuite_to_entry(suite); if (cs == NULL) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); cipher_algo = cipher_to_entry(cs->block_algorithm); mac_algo = mac_to_entry(cs->mac_algorithm); if (_gnutls_cipher_is_ok(cipher_algo) == 0 || _gnutls_mac_is_ok(mac_algo) == 0) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); if (_gnutls_cipher_priority(session, cipher_algo->id) < 0) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); if (_gnutls_mac_priority(session, mac_algo->id) < 0) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); params->cipher = cipher_algo; params->mac = mac_algo; return 0; }
/* Format: * 4 bytes the total security data size * 1 byte the entity type (client/server) * 1 byte the key exchange algorithm used * 1 byte the read cipher algorithm * 1 byte the read mac algorithm * 1 byte the read compression algorithm * * 1 byte the write cipher algorithm * 1 byte the write mac algorithm * 1 byte the write compression algorithm * * 1 byte the certificate type * 1 byte the protocol version * * 2 bytes the cipher suite * * 48 bytes the master secret * * 32 bytes the client random * 32 bytes the server random * * 1 byte the session ID size * x bytes the session ID (32 bytes max) * * 4 bytes a timestamp * ------------------- * MAX: 165 bytes * */ static int pack_security_parameters (gnutls_session_t session, gnutls_buffer_st * ps) { int ret; int size_offset; size_t cur_size; record_parameters_st *params; if (session->security_parameters.epoch_read != session->security_parameters.epoch_write) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, ¶ms); if (ret < 0) { gnutls_assert (); return ret; } /* move after the auth info stuff. */ size_offset = ps->length; BUFFER_APPEND_NUM (ps, 0); cur_size = ps->length; BUFFER_APPEND (ps, &session->security_parameters.entity, 1); BUFFER_APPEND (ps, &session->security_parameters.kx_algorithm, 1); BUFFER_APPEND (ps, &session->security_parameters.current_cipher_suite.suite[0], 1); BUFFER_APPEND (ps, &session->security_parameters.current_cipher_suite.suite[1], 1); BUFFER_APPEND (ps, ¶ms->compression_algorithm, 1); BUFFER_APPEND (ps, &session->security_parameters.cert_type, 1); BUFFER_APPEND (ps, &session->security_parameters.version, 1); BUFFER_APPEND (ps, session->security_parameters.master_secret, GNUTLS_MASTER_SIZE); BUFFER_APPEND (ps, session->security_parameters.client_random, GNUTLS_RANDOM_SIZE); BUFFER_APPEND (ps, session->security_parameters.server_random, GNUTLS_RANDOM_SIZE); BUFFER_APPEND_NUM (ps, session->security_parameters.session_id_size); BUFFER_APPEND (ps, session->security_parameters.session_id, session->security_parameters.session_id_size); BUFFER_APPEND_NUM (ps, session->security_parameters.max_record_send_size); BUFFER_APPEND_NUM (ps, session->security_parameters.max_record_recv_size); BUFFER_APPEND_NUM (ps, session->security_parameters.timestamp); _gnutls_write_uint32 (ps->length - cur_size, ps->data + size_offset); return 0; }
int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch) { int hash_size; int IV_size; int key_size; gnutls_compression_method_t comp_algo; record_parameters_st *params; int ret; const version_entry_st *ver = get_version(session); if (unlikely(ver == NULL)) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); ret = _gnutls_epoch_get(session, epoch, ¶ms); if (ret < 0) return gnutls_assert_val(ret); if (params->initialized) return 0; _gnutls_record_log ("REC[%p]: Initializing epoch #%u\n", session, params->epoch); comp_algo = params->compression_algorithm; if (_gnutls_cipher_is_ok(params->cipher) == 0 || _gnutls_mac_is_ok(params->mac) == 0) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); if (_gnutls_cipher_priority(session, params->cipher->id) < 0) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); if (_gnutls_mac_priority(session, params->mac->id) < 0) return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); if (_gnutls_compression_is_ok(comp_algo) != 0) return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM); if (!_gnutls_version_has_explicit_iv(ver) && _gnutls_cipher_type(params->cipher) == CIPHER_BLOCK) { IV_size = _gnutls_cipher_get_iv_size(params->cipher); } else { IV_size = _gnutls_cipher_get_implicit_iv_size(params->cipher); } key_size = _gnutls_cipher_get_key_size(params->cipher); hash_size = _gnutls_mac_get_key_size(params->mac); params->etm = session->security_parameters.etm; ret = _gnutls_set_keys (session, params, hash_size, IV_size, key_size); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_init_record_state(params, ver, 1, ¶ms->read); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_init_record_state(params, ver, 0, ¶ms->write); if (ret < 0) return gnutls_assert_val(ret); params->record_sw_size = 0; _gnutls_record_log("REC[%p]: Epoch #%u ready\n", session, params->epoch); params->initialized = 1; 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. * * 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; }
/* @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. * * @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; }