void _gnutls_epoch_gc (gnutls_session_t session) { int i, j; unsigned int min_index = 0; _gnutls_record_log ("REC[%p]: Start of epoch cleanup\n", session); /* Free all dead cipher state */ for (i = 0; i < MAX_EPOCH_INDEX; i++) if (session->record_parameters[i] != NULL && !epoch_alive (session, session->record_parameters[i])) { _gnutls_epoch_free (session, session->record_parameters[i]); session->record_parameters[i] = NULL; } /* Look for contiguous NULLs at the start of the array */ for (i = 0; i < MAX_EPOCH_INDEX && session->record_parameters[i] == NULL; i++); min_index = i; /* Pick up the slack in the epoch window. */ for (i = 0, j = min_index; j < MAX_EPOCH_INDEX; i++, j++) session->record_parameters[i] = session->record_parameters[j]; /* Set the new epoch_min */ if (session->record_parameters[0] != NULL) session->security_parameters.epoch_min = session->record_parameters[0]->epoch; _gnutls_record_log ("REC[%p]: End of epoch cleanup\n", session); }
/* Here we check if the advertized version is the one we * negotiated in the handshake. */ inline static int record_check_version (gnutls_session_t session, gnutls_handshake_description_t htype, uint8_t version[2]) { if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO) { /* Reject hello packets with major version higher than 3. */ if (!(IS_DTLS(session)) && version[0] > 3) { gnutls_assert (); _gnutls_record_log ("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n", session, htype, version[0], version[1]); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; } } else if (htype != GNUTLS_HANDSHAKE_SERVER_HELLO && gnutls_protocol_get_version (session) != _gnutls_version_get (version[0], version[1])) { /* Reject record packets that have a different version than the * one negotiated. Note that this version is not protected by any * mac. I don't really think that this check serves any purpose. */ gnutls_assert (); _gnutls_record_log ("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n", session, htype, version[0], version[1]); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; } 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_alloc (gnutls_session_t session, uint16_t epoch, record_parameters_st ** out) { record_parameters_st **slot; _gnutls_record_log ("REC[%p]: Allocating epoch #%u\n", session, epoch); slot = epoch_get_slot (session, epoch); /* If slot out of range or not empty. */ if (slot == NULL) return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST); if (*slot != NULL) return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST); *slot = gnutls_calloc (1, sizeof (record_parameters_st)); if (*slot == NULL) return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR); (*slot)->epoch = epoch; (*slot)->cipher_algorithm = GNUTLS_CIPHER_UNKNOWN; (*slot)->mac_algorithm = GNUTLS_MAC_UNKNOWN; (*slot)->compression_algorithm = GNUTLS_COMP_UNKNOWN; if (IS_DTLS (session)) _gnutls_write_uint16 (epoch, UINT64DATA((*slot)->write.sequence_number)); if (out != NULL) *out = *slot; return 0; }
/** * gnutls_alert_send: * @session: is a #gnutls_session_t type. * @level: is the level of the alert * @desc: is the alert description * * This function will send an alert to the peer in order to inform * him of something important (eg. his Certificate could not be verified). * If the alert level is Fatal then the peer is expected to close the * connection, otherwise he may ignore the alert and continue. * * The error code of the underlying record send function will be * returned, so you may also receive %GNUTLS_E_INTERRUPTED or * %GNUTLS_E_AGAIN as well. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise * an error code is returned. **/ int gnutls_alert_send(gnutls_session_t session, gnutls_alert_level_t level, gnutls_alert_description_t desc) { uint8_t data[2]; int ret; const char *name; data[0] = (uint8_t) level; data[1] = (uint8_t) desc; name = gnutls_alert_get_name((int) data[1]); if (name == NULL) name = "(unknown)"; _gnutls_record_log("REC: Sending Alert[%d|%d] - %s\n", data[0], data[1], name); if ((ret = _gnutls_send_int(session, GNUTLS_ALERT, -1, EPOCH_WRITE_CURRENT, data, 2, MBUFFER_FLUSH)) >= 0) return 0; else return ret; }
void _gnutls_epoch_free (gnutls_session_t session, record_parameters_st * params) { _gnutls_record_log ("REC[%p]: Epoch #%u freed\n", session, params->epoch); free_record_state (¶ms->read, 1); free_record_state (¶ms->write, 0); gnutls_free (params); }
/* Checks the record headers and returns the length, version and * content type. */ static int record_check_headers (gnutls_session_t session, uint8_t headers[RECORD_HEADER_SIZE], content_type_t type, gnutls_handshake_description_t htype, /*output */ content_type_t * recv_type, opaque version[2], uint16_t * length, uint16_t * header_size) { /* Read the first two bytes to determine if this is a * version 2 message */ if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && type == GNUTLS_HANDSHAKE && headers[0] > 127) { /* if msb set and expecting handshake message * it should be SSL 2 hello */ version[0] = 3; /* assume SSL 3.0 */ version[1] = 0; *length = (((headers[0] & 0x7f) << 8)) | headers[1]; /* SSL 2.0 headers */ *header_size = 2; *recv_type = GNUTLS_HANDSHAKE; /* we accept only v2 client hello */ /* in order to assist the handshake protocol. * V2 compatibility is a mess. */ session->internals.v2_hello = *length; _gnutls_record_log ("REC[%p]: V2 packet received. Length: %d\n", session, *length); } else { /* version 3.x */ *recv_type = headers[0]; version[0] = headers[1]; version[1] = headers[2]; /* No DECR_LEN, since headers has enough size. */ *length = _gnutls_read_uint16 (&headers[3]); } return 0; }
/* Checks the record headers and returns the length, version and * content type. */ static void record_read_headers (gnutls_session_t session, uint8_t headers[MAX_RECORD_HEADER_SIZE], content_type_t type, gnutls_handshake_description_t htype, struct tls_record_st* record) { /* Read the first two bytes to determine if this is a * version 2 message */ if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && type == GNUTLS_HANDSHAKE && headers[0] > 127 && !(IS_DTLS(session))) { /* if msb set and expecting handshake message * it should be SSL 2 hello */ record->version[0] = 3; /* assume SSL 3.0 */ record->version[1] = 0; record->length = (((headers[0] & 0x7f) << 8)) | headers[1]; /* SSL 2.0 headers */ record->header_size = record->packet_size = 2; record->type = GNUTLS_HANDSHAKE; /* we accept only v2 client hello */ /* in order to assist the handshake protocol. * V2 compatibility is a mess. */ record->v2 = 1; record->epoch = 0; memset(&record->sequence, 0, sizeof(record->sequence)); _gnutls_record_log ("REC[%p]: SSL 2.0 %s packet received. Length: %d\n", session, _gnutls_packet2str (record->type), record->length); } else { /* dtls version 1.0 and TLS version 1.x */ record->v2 = 0; record->type = headers[0]; record->version[0] = headers[1]; record->version[1] = headers[2]; if(IS_DTLS(session)) { memcpy(record->sequence.i, &headers[3], 8); record->length = _gnutls_read_uint16 (&headers[11]); record->epoch = _gnutls_read_uint16(record->sequence.i); } else { memset(&record->sequence, 0, sizeof(record->sequence)); record->length = _gnutls_read_uint16 (&headers[3]); record->epoch = 0; } _gnutls_record_log ("REC[%p]: SSL %d.%d %s packet received. Epoch %d, length: %d\n", session, (int)record->version[0], (int)record->version[1], _gnutls_packet2str (record->type), (int)record->epoch, record->length); } record->packet_size += record->length; }
/* This function will check if the received record type is * the one we actually expect and adds it to the proper * buffer. The bufel will be deinitialized after calling * this function, even if it fails. */ static int record_add_to_buffers (gnutls_session_t session, struct tls_record_st *recv, content_type_t type, gnutls_handshake_description_t htype, uint64* seq, mbuffer_st* bufel) { int ret; if ((recv->type == type) && (type == GNUTLS_APPLICATION_DATA || type == GNUTLS_CHANGE_CIPHER_SPEC || type == GNUTLS_HANDSHAKE)) { _gnutls_record_buffer_put (session, type, seq, bufel); /* if we received application data as expected then we * deactivate the async timer */ _dtls_async_timer_delete(session); } else { /* if the expected type is different than the received */ switch (recv->type) { case GNUTLS_ALERT: if (bufel->msg.size < 2) { ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); goto unexpected_packet; } _gnutls_record_log ("REC[%p]: Alert[%d|%d] - %s - was received\n", session, bufel->msg.data[0], bufel->msg.data[1], gnutls_alert_get_name ((int) bufel->msg.data[1])); session->internals.last_alert = bufel->msg.data[1]; /* if close notify is received and * the alert is not fatal */ if (bufel->msg.data[1] == GNUTLS_A_CLOSE_NOTIFY && bufel->msg.data[0] != GNUTLS_AL_FATAL) { /* If we have been expecting for an alert do */ session->internals.read_eof = 1; ret = GNUTLS_E_SESSION_EOF; goto cleanup; } else { /* if the alert is FATAL or WARNING * return the apropriate message */ gnutls_assert (); ret = GNUTLS_E_WARNING_ALERT_RECEIVED; if (bufel->msg.data[0] == GNUTLS_AL_FATAL) { session_unresumable (session); session_invalidate (session); ret = gnutls_assert_val(GNUTLS_E_FATAL_ALERT_RECEIVED); } goto cleanup; } break; case GNUTLS_CHANGE_CIPHER_SPEC: if (!(IS_DTLS(session))) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); _gnutls_record_buffer_put (session, recv->type, seq, bufel); break; #ifdef ENABLE_HEARTBEAT case GNUTLS_HEARTBEAT: ret = _gnutls_heartbeat_handle (session, bufel); goto cleanup; #endif case GNUTLS_APPLICATION_DATA: if (session->internals.initial_negotiation_completed == 0) { ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); goto unexpected_packet; } /* the got_application data is only returned * if expecting client hello (for rehandshake * reasons). Otherwise it is an unexpected packet */ if (type == GNUTLS_ALERT || (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && type == GNUTLS_HANDSHAKE)) { /* even if data is unexpected put it into the buffer */ if ((ret = _gnutls_record_buffer_put (session, recv->type, seq, bufel)) < 0) { gnutls_assert (); goto cleanup; } return gnutls_assert_val(GNUTLS_E_GOT_APPLICATION_DATA); } else { ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); goto unexpected_packet; } break; case GNUTLS_HANDSHAKE: /* In DTLS we might receive a handshake replay from the peer to indicate * the our last TLS handshake messages were not received. */ if (IS_DTLS(session)) { if (type == GNUTLS_CHANGE_CIPHER_SPEC) { ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); goto unexpected_packet; } if (_dtls_is_async(session) && _dtls_async_timer_active(session)) { if (session->security_parameters.entity == GNUTLS_SERVER && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO) { /* client requested rehandshake. Delete the timer */ _dtls_async_timer_delete(session); } else { session->internals.recv_state = RECV_STATE_DTLS_RETRANSMIT; ret = _dtls_retransmit(session); if (ret == 0) { session->internals.recv_state = RECV_STATE_0; ret = gnutls_assert_val(GNUTLS_E_AGAIN); goto unexpected_packet; } goto cleanup; } } } /* This is legal if HELLO_REQUEST is received - and we are a client. * If we are a server, a client may initiate a renegotiation at any time. */ if (session->security_parameters.entity == GNUTLS_SERVER && bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO) { gnutls_assert (); ret = _gnutls_record_buffer_put (session, recv->type, seq, bufel); if (ret < 0) { gnutls_assert (); goto cleanup; } return GNUTLS_E_REHANDSHAKE; } /* If we are already in a handshake then a Hello * Request is illegal. But here we don't really care * since this message will never make it up here. */ /* So we accept it, if it is a Hello. If not, this will * fail and trigger flight retransmissions after some time. */ ret = _gnutls_recv_hello_request (session, bufel->msg.data, bufel->msg.size); goto unexpected_packet; break; default: _gnutls_record_log ("REC[%p]: Received unexpected packet %d (%s) expecting %d (%s)\n", session, recv->type, _gnutls_packet2str(recv->type), type, _gnutls_packet2str(type)); gnutls_assert (); ret = GNUTLS_E_UNEXPECTED_PACKET; goto unexpected_packet; } } return 0; unexpected_packet: if (IS_DTLS(session) && ret != GNUTLS_E_REHANDSHAKE) { _mbuffer_xfree(&bufel); RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret); } cleanup: _mbuffer_xfree(&bufel); return ret; }
/* Here we check if the advertized version is the one we * negotiated in the handshake. */ inline static int record_check_version (gnutls_session_t session, gnutls_handshake_description_t htype, uint8_t version[2]) { const version_entry_st* vers = get_version (session); int diff = 0; if (vers->major != version[0] || vers->minor != version[1]) diff = 1; if (!IS_DTLS(session)) { if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO || htype == GNUTLS_HANDSHAKE_SERVER_HELLO) { if (version[0] != 3) { gnutls_assert (); _gnutls_record_log ("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n", session, htype, version[0], version[1]); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; } } else if (diff != 0) { /* Reject record packets that have a different version than the * one negotiated. Note that this version is not protected by any * mac. I don't really think that this check serves any purpose. */ gnutls_assert (); _gnutls_record_log ("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n", session, htype, version[0], version[1]); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; } } else /* DTLS */ { /* In DTLS the only information we have here is whether we * expect a handshake message or not. */ if (htype == (gnutls_handshake_description_t)-1) { if (diff) { /* Reject record packets that have a different version than the * one negotiated. Note that this version is not protected by any * mac. I don't really think that this check serves any purpose. */ gnutls_assert (); _gnutls_record_log ("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n", session, htype, version[0], version[1]); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; } } else if (vers->id > GNUTLS_DTLS1_0 && version[0] > 254) { gnutls_assert (); _gnutls_record_log("REC[%p]: INVALID DTLS VERSION PACKET: (%d) %d.%d\n", session, htype, version[0], version[1]); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; } else if (vers->id == GNUTLS_DTLS0_9 && version[0] > 1) { gnutls_assert (); _gnutls_record_log("REC[%p]: INVALID DTLS VERSION PACKET: (%d) %d.%d\n", session, htype, version[0], version[1]); return GNUTLS_E_UNSUPPORTED_VERSION_PACKET; } } 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; }
/* @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; }
static int recv_headers( gnutls_session_t session, content_type_t type, gnutls_handshake_description_t htype, struct tls_record_st* record, unsigned int *ms) { int ret; gnutls_datum_t raw; /* raw headers */ /* Read the headers. */ record->header_size = record->packet_size = RECORD_HEADER_SIZE(session); ret = _gnutls_io_read_buffered (session, record->header_size, -1, ms); if (ret != record->header_size) { if (ret < 0 && gnutls_error_is_fatal (ret) == 0) return ret; if (ret > 0) ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH; else if (ret == 0) ret = GNUTLS_E_PREMATURE_TERMINATION; return gnutls_assert_val(ret); } ret = _mbuffer_linearize (&session->internals.record_recv_buffer); if (ret < 0) return gnutls_assert_val(ret); _mbuffer_head_get_first (&session->internals.record_recv_buffer, &raw); if (raw.size < RECORD_HEADER_SIZE(session)) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); record_read_headers (session, raw.data, type, htype, record); /* Check if the DTLS epoch is valid */ if (IS_DTLS(session)) { if (_gnutls_epoch_is_valid(session, record->epoch) == 0) { _gnutls_audit_log(session, "Discarded message[%u] with invalid epoch %u.\n", (unsigned int)_gnutls_uint64touint32 (&record->sequence), (unsigned int)record->sequence.i[0]*256+(unsigned int)record->sequence.i[1]); gnutls_assert(); /* doesn't matter, just a fatal error */ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH; } } /* Here we check if the Type of the received packet is * ok. */ if ((ret = check_recv_type (session, record->type)) < 0) return gnutls_assert_val(ret); /* Here we check if the advertized version is the one we * negotiated in the handshake. */ if ((ret = record_check_version (session, htype, record->version)) < 0) return gnutls_assert_val(ret); if (record->length > MAX_RECV_SIZE(session)) { _gnutls_audit_log (session, "Received packet with illegal length: %u\n", (unsigned int)record->length); return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); } _gnutls_record_log ("REC[%p]: Expected Packet %s(%d)\n", session, _gnutls_packet2str (type), type); _gnutls_record_log ("REC[%p]: Received Packet %s(%d) with length: %d\n", session, _gnutls_packet2str (record->type), record->type, record->length); return 0; }
/* 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 will check if the received record type is * the one we actually expect. */ static int record_check_type (gnutls_session_t session, content_type_t recv_type, content_type_t type, gnutls_handshake_description_t htype, opaque * data, int data_size) { int ret; if ((recv_type == type) && (type == GNUTLS_APPLICATION_DATA || type == GNUTLS_HANDSHAKE || type == GNUTLS_INNER_APPLICATION)) { _gnutls_record_buffer_put (type, session, (void *) data, data_size); } else { switch (recv_type) { case GNUTLS_ALERT: _gnutls_record_log ("REC[%p]: Alert[%d|%d] - %s - was received\n", session, data[0], data[1], gnutls_alert_get_name ((int) data[1])); session->internals.last_alert = data[1]; /* if close notify is received and * the alert is not fatal */ if (data[1] == GNUTLS_A_CLOSE_NOTIFY && data[0] != GNUTLS_AL_FATAL) { /* If we have been expecting for an alert do */ session->internals.read_eof = 1; return GNUTLS_E_INT_RET_0; /* EOF */ } else { /* if the alert is FATAL or WARNING * return the apropriate message */ gnutls_assert (); ret = GNUTLS_E_WARNING_ALERT_RECEIVED; if (data[0] == GNUTLS_AL_FATAL) { session_unresumable (session); session_invalidate (session); ret = GNUTLS_E_FATAL_ALERT_RECEIVED; } return ret; } break; case GNUTLS_CHANGE_CIPHER_SPEC: /* this packet is now handled in the recv_int() * function */ gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET; case GNUTLS_APPLICATION_DATA: if (session->internals.initial_negotiation_completed == 0) { return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); } /* even if data is unexpected put it into the buffer */ if ((ret = _gnutls_record_buffer_put (recv_type, session, (void *) data, data_size)) < 0) { gnutls_assert (); return ret; } /* the got_application data is only returned * if expecting client hello (for rehandshake * reasons). Otherwise it is an unexpected packet */ if (type == GNUTLS_ALERT || (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && type == GNUTLS_HANDSHAKE)) return GNUTLS_E_GOT_APPLICATION_DATA; else { gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET; } break; case GNUTLS_HANDSHAKE: /* This is legal if HELLO_REQUEST is received - and we are a client. * If we are a server, a client may initiate a renegotiation at any time. */ if (session->security_parameters.entity == GNUTLS_SERVER) { gnutls_assert (); ret = _gnutls_record_buffer_put (recv_type, session, (void *) data, data_size); if (ret < 0) { gnutls_assert (); return ret; } return GNUTLS_E_REHANDSHAKE; } /* If we are already in a handshake then a Hello * Request is illegal. But here we don't really care * since this message will never make it up here. */ /* So we accept it */ return _gnutls_recv_hello_request (session, data, data_size); break; case GNUTLS_INNER_APPLICATION: /* even if data is unexpected put it into the buffer */ if ((ret = _gnutls_record_buffer_put (recv_type, session, (void *) data, data_size)) < 0) { gnutls_assert (); return ret; } gnutls_assert (); return GNUTLS_E_UNEXPECTED_PACKET; break; default: _gnutls_record_log ("REC[%p]: Received Unknown packet %d expecting %d\n", session, recv_type, type); gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } } 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; }
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size. * Returns the actual compressed packet size. */ int _gnutls_ciphertext2compressed (gnutls_session_t session, opaque * compress_data, int compress_size, gnutls_datum_t ciphertext, uint8_t type) { uint8_t MAC[MAX_HASH_SIZE]; uint16_t c_length; uint8_t pad; int length; digest_hd_st td; uint16_t blocksize; int ret, i, pad_failed = 0; uint8_t major, minor; gnutls_protocol_t ver; int hash_size = _gnutls_hash_get_algo_len (session->security_parameters. read_mac_algorithm); ver = gnutls_protocol_get_version (session); minor = _gnutls_version_get_minor (ver); major = _gnutls_version_get_major (ver); blocksize = _gnutls_cipher_get_block_size (session->security_parameters. read_bulk_cipher_algorithm); /* initialize MAC */ ret = mac_init (&td, session->security_parameters.read_mac_algorithm, session->connection_state.read_mac_secret.data, session->connection_state.read_mac_secret.size, ver); if (ret < 0 && session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } /* actual decryption (inplace) */ switch (_gnutls_cipher_is_block (session->security_parameters.read_bulk_cipher_algorithm)) { case CIPHER_STREAM: if ((ret = _gnutls_cipher_decrypt (&session->connection_state. read_cipher_state, ciphertext.data, ciphertext.size)) < 0) { gnutls_assert (); return ret; } length = ciphertext.size - hash_size; break; case CIPHER_BLOCK: if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0)) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } if ((ret = _gnutls_cipher_decrypt (&session->connection_state. read_cipher_state, ciphertext.data, ciphertext.size)) < 0) { gnutls_assert (); return ret; } /* ignore the IV in TLS 1.1. */ if (session->security_parameters.version >= GNUTLS_TLS1_1) { ciphertext.size -= blocksize; ciphertext.data += blocksize; if (ciphertext.size == 0) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } } pad = ciphertext.data[ciphertext.size - 1] + 1; /* pad */ if ((int) pad > (int) ciphertext.size - hash_size) { gnutls_assert (); _gnutls_record_log ("REC[%x]: Short record length %d > %d - %d (under attack?)\n", session, pad, ciphertext.size, hash_size); /* We do not fail here. We check below for the * the pad_failed. If zero means success. */ pad_failed = GNUTLS_E_DECRYPTION_FAILED; } length = ciphertext.size - hash_size - pad; /* Check the pading bytes (TLS 1.x) */ if (ver >= GNUTLS_TLS1 && pad_failed == 0) for (i = 2; i < pad; i++) { if (ciphertext.data[ciphertext.size - i] != ciphertext.data[ciphertext.size - 1]) pad_failed = GNUTLS_E_DECRYPTION_FAILED; } break; default: gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } if (length < 0) length = 0; c_length = _gnutls_conv_uint16 ((uint16_t) length); /* Pass the type, version, length and compressed through * MAC. */ if (session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL) { _gnutls_hmac (&td, UINT64DATA (session->connection_state. read_sequence_number), 8); _gnutls_hmac (&td, &type, 1); if (ver >= GNUTLS_TLS1) { /* TLS 1.x */ _gnutls_hmac (&td, &major, 1); _gnutls_hmac (&td, &minor, 1); } _gnutls_hmac (&td, &c_length, 2); if (length > 0) _gnutls_hmac (&td, ciphertext.data, length); mac_deinit (&td, MAC, ver); } /* This one was introduced to avoid a timing attack against the TLS * 1.0 protocol. */ if (pad_failed != 0) return pad_failed; /* HMAC was not the same. */ if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } /* copy the decrypted stuff to compress_data. */ if (compress_size < length) { gnutls_assert (); return GNUTLS_E_DECOMPRESSION_FAILED; } memcpy (compress_data, ciphertext.data, length); return length; }
/* * 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); } }
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size. * Returns the actual compressed packet size. */ static int ciphertext_to_compressed (gnutls_session_t session, gnutls_datum_t *ciphertext, opaque * compress_data, int compress_size, uint8_t type, record_parameters_st * params, uint64* sequence) { uint8_t tag[MAX_HASH_SIZE]; uint8_t pad; int length, length_to_decrypt; uint16_t blocksize; int ret, i, pad_failed = 0; opaque preamble[MAX_PREAMBLE_SIZE]; int preamble_size; int ver = gnutls_protocol_get_version (session); int tag_size = _gnutls_auth_cipher_tag_len (¶ms->read.cipher_state); int explicit_iv = _gnutls_version_has_explicit_iv (session->security_parameters.version); blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm); /* actual decryption (inplace) */ switch (_gnutls_cipher_is_block (params->cipher_algorithm)) { case CIPHER_STREAM: /* The way AEAD ciphers are defined in RFC5246, it allows * only stream ciphers. */ if (explicit_iv && _gnutls_auth_cipher_is_aead(¶ms->read.cipher_state)) { uint8_t nonce[blocksize]; /* Values in AEAD are pretty fixed in TLS 1.2 for 128-bit block */ if (params->read.IV.data == NULL || params->read.IV.size != 4) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); if (ciphertext->size < tag_size+AEAD_EXPLICIT_DATA_SIZE) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); memcpy(nonce, params->read.IV.data, AEAD_IMPLICIT_DATA_SIZE); memcpy(&nonce[AEAD_IMPLICIT_DATA_SIZE], ciphertext->data, AEAD_EXPLICIT_DATA_SIZE); _gnutls_auth_cipher_setiv(¶ms->read.cipher_state, nonce, AEAD_EXPLICIT_DATA_SIZE+AEAD_IMPLICIT_DATA_SIZE); ciphertext->data += AEAD_EXPLICIT_DATA_SIZE; ciphertext->size -= AEAD_EXPLICIT_DATA_SIZE; length_to_decrypt = ciphertext->size - tag_size; } else { if (ciphertext->size < tag_size) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); length_to_decrypt = ciphertext->size; } length = ciphertext->size - tag_size; /* Pass the type, version, length and compressed through * MAC. */ preamble_size = make_preamble (UINT64DATA(*sequence), type, length, ver, preamble); _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, preamble, preamble_size); if ((ret = _gnutls_auth_cipher_decrypt (¶ms->read.cipher_state, ciphertext->data, length_to_decrypt)) < 0) return gnutls_assert_val(ret); break; case CIPHER_BLOCK: if (ciphertext->size < MAX(blocksize, tag_size) || (ciphertext->size % blocksize != 0)) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); /* ignore the IV in TLS 1.1+ */ if (explicit_iv) { _gnutls_auth_cipher_setiv(¶ms->read.cipher_state, ciphertext->data, blocksize); ciphertext->size -= blocksize; ciphertext->data += blocksize; if (ciphertext->size == 0) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } } /* we don't use the auth_cipher interface here, since * TLS with block ciphers is impossible to be used under such * an API. (the length of plaintext is required to calculate * auth_data, but it is not available before decryption). */ if ((ret = _gnutls_cipher_decrypt (¶ms->read.cipher_state.cipher, ciphertext->data, ciphertext->size)) < 0) return gnutls_assert_val(ret); pad = ciphertext->data[ciphertext->size - 1] + 1; /* pad */ if ((int) pad > (int) ciphertext->size - tag_size) { gnutls_assert (); _gnutls_record_log ("REC[%p]: Short record length %d > %d - %d (under attack?)\n", session, pad, ciphertext->size, tag_size); /* We do not fail here. We check below for the * the pad_failed. If zero means success. */ pad_failed = GNUTLS_E_DECRYPTION_FAILED; pad %= blocksize; } length = ciphertext->size - tag_size - pad; /* Check the pading bytes (TLS 1.x) */ if (ver != GNUTLS_SSL3) for (i = 2; i < pad; i++) { if (ciphertext->data[ciphertext->size - i] != ciphertext->data[ciphertext->size - 1]) pad_failed = GNUTLS_E_DECRYPTION_FAILED; } if (length < 0) length = 0; /* Pass the type, version, length and compressed through * MAC. */ preamble_size = make_preamble (UINT64DATA(*sequence), type, length, ver, preamble); _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, preamble, preamble_size); _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, ciphertext->data, length); break; default: return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } ret = _gnutls_auth_cipher_tag(¶ms->read.cipher_state, tag, tag_size); if (ret < 0) return gnutls_assert_val(ret); /* This one was introduced to avoid a timing attack against the TLS * 1.0 protocol. */ /* HMAC was not the same. */ if (memcmp (tag, &ciphertext->data[length], tag_size) != 0 || pad_failed != 0) return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); /* copy the decrypted stuff to compress_data. */ if (compress_size < length) return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED); if (compress_data != ciphertext->data) memcpy (compress_data, ciphertext->data, length); return length; }
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; }
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size. * Returns the actual compressed packet size. */ int _gnutls_ciphertext2compressed (gnutls_session_t session, opaque * compress_data, int compress_size, gnutls_datum_t ciphertext, uint8_t type, record_parameters_st * params) { uint8_t MAC[MAX_HASH_SIZE]; uint16_t c_length; uint8_t pad; int length; uint16_t blocksize; int ret, i, pad_failed = 0; opaque preamble[PREAMBLE_SIZE]; int preamble_size; int ver = gnutls_protocol_get_version (session); int hash_size = _gnutls_hash_get_algo_len (params->mac_algorithm); blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm); /* actual decryption (inplace) */ switch (_gnutls_cipher_is_block (params->cipher_algorithm)) { case CIPHER_STREAM: if ((ret = _gnutls_cipher_decrypt (¶ms->read.cipher_state, ciphertext.data, ciphertext.size)) < 0) { gnutls_assert (); return ret; } length = ciphertext.size - hash_size; break; case CIPHER_BLOCK: if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0)) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } if ((ret = _gnutls_cipher_decrypt (¶ms->read.cipher_state, ciphertext.data, ciphertext.size)) < 0) { gnutls_assert (); return ret; } /* ignore the IV in TLS 1.1. */ if (_gnutls_version_has_explicit_iv (session->security_parameters.version)) { ciphertext.size -= blocksize; ciphertext.data += blocksize; if (ciphertext.size == 0) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } } pad = ciphertext.data[ciphertext.size - 1] + 1; /* pad */ if ((int) pad > (int) ciphertext.size - hash_size) { gnutls_assert (); _gnutls_record_log ("REC[%p]: Short record length %d > %d - %d (under attack?)\n", session, pad, ciphertext.size, hash_size); /* We do not fail here. We check below for the * the pad_failed. If zero means success. */ pad_failed = GNUTLS_E_DECRYPTION_FAILED; } length = ciphertext.size - hash_size - pad; /* Check the pading bytes (TLS 1.x) */ if (_gnutls_version_has_variable_padding (ver) && pad_failed == 0) for (i = 2; i < pad; i++) { if (ciphertext.data[ciphertext.size - i] != ciphertext.data[ciphertext.size - 1]) pad_failed = GNUTLS_E_DECRYPTION_FAILED; } break; default: gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } if (length < 0) length = 0; c_length = _gnutls_conv_uint16 ((uint16_t) length); /* Pass the type, version, length and compressed through * MAC. */ if (params->mac_algorithm != GNUTLS_MAC_NULL) { digest_hd_st td; ret = mac_init (&td, params->mac_algorithm, params->read.mac_secret.data, params->read.mac_secret.size, ver); if (ret < 0) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } preamble_size = make_preamble (UINT64DATA (params->read.sequence_number), type, c_length, ver, preamble); mac_hash (&td, preamble, preamble_size, ver); if (length > 0) mac_hash (&td, ciphertext.data, length, ver); mac_deinit (&td, MAC, ver); } /* This one was introduced to avoid a timing attack against the TLS * 1.0 protocol. */ if (pad_failed != 0) { gnutls_assert (); return pad_failed; } /* HMAC was not the same. */ if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } /* copy the decrypted stuff to compress_data. */ if (compress_size < length) { gnutls_assert (); return GNUTLS_E_DECOMPRESSION_FAILED; } memcpy (compress_data, ciphertext.data, length); return length; }
/* 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; }