/* Send TLS/IA data. If data==NULL && sizeofdata==NULL, then the last send was interrupted for some reason, and then we try to send it again. Returns the number of bytes sent, or an error code. If this return E_AGAIN and E_INTERRUPTED, call this function again with data==NULL&&sizeofdata=0NULL until it returns successfully. */ static ssize_t _gnutls_send_inner_application (gnutls_session_t session, gnutls_ia_apptype_t msg_type, const char *data, size_t sizeofdata) { opaque *p = NULL; size_t plen = 0; ssize_t len; if (data != NULL) { plen = sizeofdata + 4; p = gnutls_malloc (plen); if (!p) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } *(unsigned char *) p = (unsigned char) (msg_type & 0xFF); _gnutls_write_uint24 (sizeofdata, p + 1); memcpy (p + 4, data, sizeofdata); } len = _gnutls_send_int (session, GNUTLS_INNER_APPLICATION, -1, p, plen); if (p) gnutls_free (p); return len; }
void _gnutls_write_datum24 (opaque * dest, gnutls_datum_t dat) { _gnutls_write_uint24 (dat.size, dest); if (dat.data != NULL) memcpy (&dest[3], dat.data, dat.size); }
int _gnutls_buffer_append_prefix (gnutls_buffer_st * buf, int pfx_size, size_t data_size) { opaque ss[4]; if (pfx_size == 32) { _gnutls_write_uint32 (data_size, ss); pfx_size = 4; } else if (pfx_size == 24) { _gnutls_write_uint24 (data_size, ss); pfx_size = 3; } else if (pfx_size == 16) { _gnutls_write_uint16 (data_size, ss); pfx_size = 2; } else if (pfx_size == 8) { ss[0] = data_size; pfx_size = 1; } else return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); return _gnutls_buffer_append_data (buf, ss, pfx_size); }
int _gnutls_send_server_certificate_status(gnutls_session_t session, int again) { mbuffer_st *bufel = NULL; uint8_t *data; int data_size = 0; int ret; status_request_ext_st *priv = NULL; extension_priv_data_t epriv; if (again == 0) { ret = _gnutls_ext_get_session_data(session, GNUTLS_EXTENSION_STATUS_REQUEST, &epriv); if (ret < 0) return 0; priv = epriv.ptr; if (!priv->response.size) return 0; data_size = priv->response.size + 4; bufel = _gnutls_handshake_alloc(session, data_size, data_size); if (!bufel) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); data = _mbuffer_get_udata_ptr(bufel); data[0] = 0x01; _gnutls_write_uint24(priv->response.size, &data[1]); memcpy(&data[4], priv->response.data, priv->response.size); _gnutls_free_datum(&priv->response); } return _gnutls_send_handshake(session, data_size ? bufel : NULL, GNUTLS_HANDSHAKE_CERTIFICATE_STATUS); }
/* will merge the given handshake_buffer_st to the handshake_recv_buffer * list. The given hsk packet will be released in any case (success or failure). * Only used in DTLS. */ static int merge_handshake_packet(gnutls_session_t session, handshake_buffer_st * hsk) { int exists = 0, i, pos = 0; int ret; for (i = 0; i < session->internals.handshake_recv_buffer_size; i++) { if (session->internals.handshake_recv_buffer[i].htype == hsk->htype) { exists = 1; pos = i; break; } } if (!exists) pos = session->internals.handshake_recv_buffer_size; if (pos >= MAX_HANDSHAKE_MSGS) return gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS); if (!exists) { if (hsk->length > 0 && hsk->end_offset > 0 && hsk->end_offset - hsk->start_offset + 1 != hsk->length) { ret = _gnutls_buffer_resize(&hsk->data, hsk->length); if (ret < 0) return gnutls_assert_val(ret); hsk->data.length = hsk->length; memmove(&hsk->data.data[hsk->start_offset], hsk->data.data, hsk->end_offset - hsk->start_offset + 1); } session->internals.handshake_recv_buffer_size++; /* rewrite headers to make them look as each packet came as a single fragment */ _gnutls_write_uint24(hsk->length, &hsk->header[1]); _gnutls_write_uint24(0, &hsk->header[6]); _gnutls_write_uint24(hsk->length, &hsk->header[9]); _gnutls_handshake_buffer_move(&session->internals. handshake_recv_buffer[pos], hsk); } else { if (hsk->start_offset < session->internals.handshake_recv_buffer[pos]. start_offset && hsk->end_offset + 1 >= session->internals.handshake_recv_buffer[pos]. start_offset) { memcpy(&session->internals. handshake_recv_buffer[pos].data.data[hsk-> start_offset], hsk->data.data, hsk->data.length); session->internals.handshake_recv_buffer[pos]. start_offset = hsk->start_offset; session->internals.handshake_recv_buffer[pos]. end_offset = MIN(hsk->end_offset, session->internals. handshake_recv_buffer[pos].end_offset); } else if (hsk->end_offset > session->internals.handshake_recv_buffer[pos]. end_offset && hsk->start_offset <= session->internals.handshake_recv_buffer[pos]. end_offset + 1) { memcpy(&session->internals. handshake_recv_buffer[pos].data.data[hsk-> start_offset], hsk->data.data, hsk->data.length); session->internals.handshake_recv_buffer[pos]. end_offset = hsk->end_offset; session->internals.handshake_recv_buffer[pos]. start_offset = MIN(hsk->start_offset, session->internals. handshake_recv_buffer[pos].start_offset); } _gnutls_handshake_buffer_clear(hsk); } return 0; }
/** * gnutls_dtls_cookie_send: * @key: is a random key to be used at cookie generation * @client_data: contains data identifying the client (i.e. address) * @client_data_size: The size of client's data * @prestate: The previous cookie returned by gnutls_dtls_cookie_verify() * @ptr: A transport pointer to be used by @push_func * @push_func: A function that will be used to reply * * This function can be used to prevent denial of service * attacks to a DTLS server by requiring the client to * reply using a cookie sent by this function. That way * it can be ensured that a client we allocated resources * for (i.e. #gnutls_session_t) is the one that the * original incoming packet was originated from. * * Returns: the number of bytes sent, or a negative error code. * * Since: 3.0 **/ int gnutls_dtls_cookie_send(gnutls_datum_t* key, void* client_data, size_t client_data_size, gnutls_dtls_prestate_st* prestate, gnutls_transport_ptr_t ptr, gnutls_push_func push_func) { uint8_t hvr[20+DTLS_HANDSHAKE_HEADER_SIZE+COOKIE_SIZE]; int hvr_size = 0, ret; uint8_t digest[C_HASH_SIZE]; if (key == NULL || key->data == NULL || key->size == 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); /* send * struct { * ContentType type - 1 byte GNUTLS_HANDSHAKE; * ProtocolVersion version; - 2 bytes (254,255) * uint16 epoch; - 2 bytes (0, 0) * uint48 sequence_number; - 4 bytes (0,0,0,0) * uint16 length; - 2 bytes (COOKIE_SIZE+1+2)+DTLS_HANDSHAKE_HEADER_SIZE * uint8_t fragment[DTLSPlaintext.length]; * } DTLSPlaintext; * * * struct { * HandshakeType msg_type; 1 byte - GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST * uint24 length; - COOKIE_SIZE+3 * uint16 message_seq; - 2 bytes (0,0) * uint24 fragment_offset; - 3 bytes (0,0,0) * uint24 fragment_length; - same as length * } * * struct { * ProtocolVersion server_version; * uint8_t cookie<0..32>; * } HelloVerifyRequest; */ hvr[hvr_size++] = GNUTLS_HANDSHAKE; /* version */ hvr[hvr_size++] = 254; hvr[hvr_size++] = 255; /* epoch + seq */ memset(&hvr[hvr_size], 0, 8); hvr_size += 7; hvr[hvr_size++] = prestate->record_seq; /* length */ _gnutls_write_uint16(DTLS_HANDSHAKE_HEADER_SIZE+COOKIE_SIZE+3, &hvr[hvr_size]); hvr_size += 2; /* now handshake headers */ hvr[hvr_size++] = GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST; _gnutls_write_uint24(COOKIE_SIZE+3, &hvr[hvr_size]); hvr_size += 3; /* handshake seq */ hvr[hvr_size++] = 0; hvr[hvr_size++] = prestate->hsk_write_seq; _gnutls_write_uint24(0, &hvr[hvr_size]); hvr_size += 3; _gnutls_write_uint24(COOKIE_SIZE+3, &hvr[hvr_size]); hvr_size += 3; /* version */ hvr[hvr_size++] = 254; hvr[hvr_size++] = 255; hvr[hvr_size++] = COOKIE_SIZE; ret = _gnutls_hmac_fast(C_HASH, key->data, key->size, client_data, client_data_size, digest); if (ret < 0) return gnutls_assert_val(ret); memcpy(&hvr[hvr_size], digest, COOKIE_MAC_SIZE); hvr_size+= COOKIE_MAC_SIZE; ret = push_func(ptr, hvr, hvr_size); if (ret < 0) ret = GNUTLS_E_PUSH_ERROR; return ret; }
/* This function fragments and transmits a previously buffered * outgoing message. It accepts mtu_data which is a buffer to * be reused (should be set to NULL initially). */ static inline int transmit_message (gnutls_session_t session, mbuffer_st *bufel, uint8_t **buf) { uint8_t *data, *mtu_data; int ret = 0; unsigned int offset, frag_len, data_size; const unsigned int mtu = gnutls_dtls_get_data_mtu(session) - DTLS_HANDSHAKE_HEADER_SIZE; if (bufel->type == GNUTLS_CHANGE_CIPHER_SPEC) { _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d)\n", session, bufel->handshake_sequence, _gnutls_handshake2str (bufel->htype), bufel->htype); return _gnutls_send_int (session, bufel->type, -1, bufel->epoch, _mbuffer_get_uhead_ptr(bufel), _mbuffer_get_uhead_size(bufel), 0); } if (*buf == NULL) *buf = gnutls_malloc(mtu + DTLS_HANDSHAKE_HEADER_SIZE); if (*buf == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); mtu_data = *buf; data = _mbuffer_get_udata_ptr( bufel); data_size = _mbuffer_get_udata_size(bufel); /* Write fixed headers */ /* Handshake type */ mtu_data[0] = (uint8_t) bufel->htype; /* Total length */ _gnutls_write_uint24 (data_size, &mtu_data[1]); /* Handshake sequence */ _gnutls_write_uint16 (bufel->handshake_sequence, &mtu_data[4]); /* Chop up and send handshake message into mtu-size pieces. */ for (offset=0; offset <= data_size; offset += mtu) { /* Calculate fragment length */ if(offset + mtu > data_size) frag_len = data_size - offset; else frag_len = mtu; /* Fragment offset */ _gnutls_write_uint24 (offset, &mtu_data[6]); /* Fragment length */ _gnutls_write_uint24 (frag_len, &mtu_data[9]); memcpy (&mtu_data[DTLS_HANDSHAKE_HEADER_SIZE], data+offset, frag_len); _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with " "length: %u, offset: %u, fragment length: %u\n", session, bufel->handshake_sequence, _gnutls_handshake2str (bufel->htype), bufel->htype, data_size, offset, frag_len); ret = _gnutls_send_int (session, bufel->type, bufel->htype, bufel->epoch, mtu_data, DTLS_HANDSHAKE_HEADER_SIZE + frag_len, 0); if (ret < 0) { gnutls_assert(); break; } } return ret; }
int _gnutls13_send_certificate(gnutls_session_t session, unsigned again) { int ret; gnutls_pcert_st *apr_cert_list = NULL; gnutls_privkey_t apr_pkey = NULL; int apr_cert_list_length = 0; mbuffer_st *bufel = NULL; gnutls_buffer_st buf; unsigned pos_mark, ext_pos_mark; unsigned i; struct ocsp_req_ctx_st ctx; gnutls_certificate_credentials_t cred; if (again == 0) { if (!session->internals.initial_negotiation_completed && session->internals.hsk_flags & HSK_PSK_SELECTED) return 0; if (session->security_parameters.entity == GNUTLS_SERVER && session->internals.resumed) return 0; cred = (gnutls_certificate_credentials_t) _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE); if (cred == NULL) { gnutls_assert(); return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } if (session->security_parameters.entity == GNUTLS_CLIENT && !(session->internals.hsk_flags & HSK_CRT_ASKED)) { return 0; } ret = _gnutls_get_selected_cert(session, &apr_cert_list, &apr_cert_list_length, &apr_pkey); if (ret < 0) return gnutls_assert_val(ret); ret = _gnutls_buffer_init_handshake_mbuffer(&buf); if (ret < 0) return gnutls_assert_val(ret); if (session->security_parameters.entity == GNUTLS_CLIENT) { ret = _gnutls_buffer_append_data_prefix(&buf, 8, session->internals.post_handshake_cr_context.data, session->internals.post_handshake_cr_context.size); if (ret < 0) { gnutls_assert(); goto cleanup; } } else { ret = _gnutls_buffer_append_prefix(&buf, 8, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } } /* mark total size */ pos_mark = buf.length; ret = _gnutls_buffer_append_prefix(&buf, 24, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } for (i=0;i<(unsigned)apr_cert_list_length;i++) { ret = _gnutls_buffer_append_data_prefix(&buf, 24, apr_cert_list[i].cert.data, apr_cert_list[i].cert.size); if (ret < 0) { gnutls_assert(); goto cleanup; } #ifdef ENABLE_OCSP if ((session->internals.selected_ocsp_length > 0 || session->internals.selected_ocsp_func) && _gnutls_hello_ext_is_present(session, GNUTLS_EXTENSION_STATUS_REQUEST)) { /* append status response if available */ ret = _gnutls_extv_append_init(&buf); if (ret < 0) { gnutls_assert(); goto cleanup; } ext_pos_mark = ret; ctx.pcert = &apr_cert_list[i]; ctx.cert_index = i; ctx.session = session; ctx.cred = cred; ret = _gnutls_extv_append(&buf, STATUS_REQUEST_TLS_ID, &ctx, append_status_request); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = _gnutls_extv_append_final(&buf, ext_pos_mark, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } } else #endif { ret = _gnutls_buffer_append_prefix(&buf, 16, 0); if (ret < 0) { gnutls_assert(); goto cleanup; } } } _gnutls_write_uint24(buf.length-pos_mark-3, &buf.data[pos_mark]); bufel = _gnutls_buffer_to_mbuffer(&buf); } return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_PKT); cleanup: _gnutls_buffer_clear(&buf); return ret; }
static int compute_psk_binder(gnutls_session_t session, const mac_entry_st *prf, unsigned binders_length, int exts_length, int ext_offset, const gnutls_datum_t *psk, const gnutls_datum_t *client_hello, bool resuming, void *out) { int ret; unsigned client_hello_pos, extensions_len_pos; gnutls_buffer_st handshake_buf; uint8_t binder_key[MAX_HASH_SIZE]; _gnutls_buffer_init(&handshake_buf); if (session->security_parameters.entity == GNUTLS_CLIENT) { if (session->internals.hsk_flags & HSK_HRR_RECEIVED) { ret = gnutls_buffer_append_data(&handshake_buf, (const void *) session->internals.handshake_hash_buffer.data, session->internals.handshake_hash_buffer.length); if (ret < 0) { gnutls_assert(); goto error; } } client_hello_pos = handshake_buf.length; ret = gnutls_buffer_append_data(&handshake_buf, client_hello->data, client_hello->size); if (ret < 0) { gnutls_assert(); goto error; } /* This is a ClientHello message */ handshake_buf.data[client_hello_pos] = GNUTLS_HANDSHAKE_CLIENT_HELLO; /* At this point we have not yet added the binders to the ClientHello, * but we have to overwrite the size field, pretending as if binders * of the correct length were present. */ _gnutls_write_uint24(handshake_buf.length - client_hello_pos + binders_length - 2, &handshake_buf.data[client_hello_pos + 1]); _gnutls_write_uint16(handshake_buf.length - client_hello_pos + binders_length - ext_offset, &handshake_buf.data[client_hello_pos + ext_offset]); extensions_len_pos = handshake_buf.length - client_hello_pos - exts_length - 2; _gnutls_write_uint16(exts_length + binders_length + 2, &handshake_buf.data[client_hello_pos + extensions_len_pos]); } else { if (session->internals.hsk_flags & HSK_HRR_SENT) { if (unlikely(session->internals.handshake_hash_buffer.length <= client_hello->size)) { ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); goto error; } ret = gnutls_buffer_append_data(&handshake_buf, session->internals.handshake_hash_buffer.data, session->internals.handshake_hash_buffer.length - client_hello->size); if (ret < 0) { gnutls_assert(); goto error; } } if (unlikely(client_hello->size <= binders_length)) { ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER); goto error; } ret = gnutls_buffer_append_data(&handshake_buf, (const void *) client_hello->data, client_hello->size - binders_length); if (ret < 0) { gnutls_assert(); goto error; } } ret = compute_binder_key(prf, psk->data, psk->size, resuming, binder_key); if (ret < 0) { gnutls_assert(); goto error; } ret = _gnutls13_compute_finished(prf, binder_key, &handshake_buf, out); if (ret < 0) { gnutls_assert(); goto error; } ret = 0; error: _gnutls_buffer_clear(&handshake_buf); return ret; }