|
Packit |
549fdc |
/*
|
|
Packit |
549fdc |
* Copyright (C) 2000-2016 Free Software Foundation, Inc.
|
|
Packit |
549fdc |
* Copyright (C) 2012-2016 Nikos Mavrogiannopoulos
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Author: Nikos Mavrogiannopoulos
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This file is part of GnuTLS.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* The GnuTLS is free software; you can redistribute it and/or
|
|
Packit |
549fdc |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit |
549fdc |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
549fdc |
* the License, or (at your option) any later version.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This library is distributed in the hope that it will be useful, but
|
|
Packit |
549fdc |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
549fdc |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
549fdc |
* Lesser General Public License for more details.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit |
549fdc |
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Functions that are record layer specific, are included in this file.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* allocate this many bytes more when encrypting or decrypting, to
|
|
Packit |
549fdc |
* compensate for broken backends such as cryptodev.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
#define CIPHER_SLACK_SIZE 32
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#include "gnutls_int.h"
|
|
Packit |
549fdc |
#include "errors.h"
|
|
Packit |
549fdc |
#include "debug.h"
|
|
Packit |
549fdc |
#include "cipher.h"
|
|
Packit |
549fdc |
#include "buffers.h"
|
|
Packit |
549fdc |
#include "mbuffers.h"
|
|
Packit |
549fdc |
#include "handshake.h"
|
|
Packit |
549fdc |
#include "hash_int.h"
|
|
Packit |
549fdc |
#include "cipher_int.h"
|
|
Packit |
549fdc |
#include "algorithms.h"
|
|
Packit |
549fdc |
#include "db.h"
|
|
Packit |
549fdc |
#include "auth.h"
|
|
Packit |
549fdc |
#include "num.h"
|
|
Packit |
549fdc |
#include "record.h"
|
|
Packit |
549fdc |
#include "datum.h"
|
|
Packit |
549fdc |
#include "constate.h"
|
|
Packit |
549fdc |
#include "ext/max_record.h"
|
|
Packit |
549fdc |
#include <ext/heartbeat.h>
|
|
Packit |
549fdc |
#include <state.h>
|
|
Packit |
549fdc |
#include <dtls.h>
|
|
Packit |
549fdc |
#include <dh.h>
|
|
Packit |
549fdc |
#include <random.h>
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
struct tls_record_st {
|
|
Packit |
549fdc |
uint16_t header_size;
|
|
Packit |
549fdc |
uint8_t version[2];
|
|
Packit |
549fdc |
gnutls_uint64 sequence; /* DTLS */
|
|
Packit |
549fdc |
uint16_t length;
|
|
Packit |
549fdc |
uint16_t packet_size; /* header_size + length */
|
|
Packit |
549fdc |
content_type_t type;
|
|
Packit |
549fdc |
uint16_t epoch; /* valid in DTLS only */
|
|
Packit |
549fdc |
#ifdef ENABLE_SSL2
|
|
Packit |
549fdc |
unsigned v2:1; /* whether an SSLv2 client hello */
|
|
Packit |
549fdc |
#endif
|
|
Packit |
549fdc |
/* the data */
|
|
Packit |
549fdc |
};
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_record_disable_padding:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Used to disabled padding in TLS 1.0 and above. Normally you do not
|
|
Packit |
549fdc |
* need to use this function, but there are buggy clients that
|
|
Packit |
549fdc |
* complain if a server pads the encrypted data. This of course will
|
|
Packit |
549fdc |
* disable protection against statistical attacks on the data.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This functions is defunt since 3.1.7. Random padding is disabled
|
|
Packit |
549fdc |
* by default unless requested using gnutls_range_send_message().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
void gnutls_record_disable_padding(gnutls_session_t session)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
return;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_transport_set_ptr:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @ptr: is the value.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Used to set the first argument of the transport function (for push
|
|
Packit |
549fdc |
* and pull callbacks). In berkeley style sockets this function will set the
|
|
Packit |
549fdc |
* connection descriptor.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
void
|
|
Packit |
549fdc |
gnutls_transport_set_ptr(gnutls_session_t session,
|
|
Packit |
549fdc |
gnutls_transport_ptr_t ptr)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
session->internals.transport_recv_ptr = ptr;
|
|
Packit |
549fdc |
session->internals.transport_send_ptr = ptr;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_transport_set_ptr2:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @recv_ptr: is the value for the pull function
|
|
Packit |
549fdc |
* @send_ptr: is the value for the push function
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Used to set the first argument of the transport function (for push
|
|
Packit |
549fdc |
* and pull callbacks). In berkeley style sockets this function will set the
|
|
Packit |
549fdc |
* connection descriptor. With this function you can use two different
|
|
Packit |
549fdc |
* pointers for receiving and sending.
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
void
|
|
Packit |
549fdc |
gnutls_transport_set_ptr2(gnutls_session_t session,
|
|
Packit |
549fdc |
gnutls_transport_ptr_t recv_ptr,
|
|
Packit |
549fdc |
gnutls_transport_ptr_t send_ptr)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
session->internals.transport_send_ptr = send_ptr;
|
|
Packit |
549fdc |
session->internals.transport_recv_ptr = recv_ptr;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_transport_set_int2:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @recv_fd: is socket descriptor for the pull function
|
|
Packit |
549fdc |
* @send_fd: is socket descriptor for the push function
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function sets the first argument of the transport functions,
|
|
Packit |
549fdc |
* such as send() and recv() for the default callbacks using the
|
|
Packit |
549fdc |
* system's socket API. With this function you can set two different
|
|
Packit |
549fdc |
* descriptors for receiving and sending.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function is equivalent to calling gnutls_transport_set_ptr2()
|
|
Packit |
549fdc |
* with the descriptors, but requires no casts.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.1.9
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
void
|
|
Packit |
549fdc |
gnutls_transport_set_int2(gnutls_session_t session,
|
|
Packit |
549fdc |
int recv_fd, int send_fd)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
session->internals.transport_send_ptr =
|
|
Packit |
549fdc |
(gnutls_transport_ptr_t) (long) send_fd;
|
|
Packit |
549fdc |
session->internals.transport_recv_ptr =
|
|
Packit |
549fdc |
(gnutls_transport_ptr_t) (long) recv_fd;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#if 0
|
|
Packit |
549fdc |
/* this will be a macro */
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_transport_set_int:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @fd: is the socket descriptor for the connection.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function sets the first argument of the transport function, such
|
|
Packit |
549fdc |
* as send() and recv() for the default callbacks using the
|
|
Packit |
549fdc |
* system's socket API.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function is equivalent to calling gnutls_transport_set_ptr()
|
|
Packit |
549fdc |
* with the descriptor, but requires no casts.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.1.9
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
void gnutls_transport_set_int(gnutls_session_t session, int fd)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
session->internals.transport_recv_ptr =
|
|
Packit |
549fdc |
(gnutls_transport_ptr_t) (long) fd;
|
|
Packit |
549fdc |
session->internals.transport_send_ptr =
|
|
Packit |
549fdc |
(gnutls_transport_ptr_t) (long) fd;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
#endif
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_transport_get_ptr:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Used to get the first argument of the transport function (like
|
|
Packit |
549fdc |
* PUSH and PULL). This must have been set using
|
|
Packit |
549fdc |
* gnutls_transport_set_ptr().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: The first argument of the transport function.
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
gnutls_transport_ptr_t gnutls_transport_get_ptr(gnutls_session_t session)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
return session->internals.transport_recv_ptr;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_transport_get_ptr2:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @recv_ptr: will hold the value for the pull function
|
|
Packit |
549fdc |
* @send_ptr: will hold the value for the push function
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Used to get the arguments of the transport functions (like PUSH
|
|
Packit |
549fdc |
* and PULL). These should have been set using
|
|
Packit |
549fdc |
* gnutls_transport_set_ptr2().
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
void
|
|
Packit |
549fdc |
gnutls_transport_get_ptr2(gnutls_session_t session,
|
|
Packit |
549fdc |
gnutls_transport_ptr_t * recv_ptr,
|
|
Packit |
549fdc |
gnutls_transport_ptr_t * send_ptr)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
*recv_ptr = session->internals.transport_recv_ptr;
|
|
Packit |
549fdc |
*send_ptr = session->internals.transport_send_ptr;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_transport_get_int2:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @recv_int: will hold the value for the pull function
|
|
Packit |
549fdc |
* @send_int: will hold the value for the push function
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Used to get the arguments of the transport functions (like PUSH
|
|
Packit |
549fdc |
* and PULL). These should have been set using
|
|
Packit |
549fdc |
* gnutls_transport_set_int2().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.1.9
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
void
|
|
Packit |
549fdc |
gnutls_transport_get_int2(gnutls_session_t session,
|
|
Packit |
549fdc |
int *recv_int, int *send_int)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
*recv_int = (long) session->internals.transport_recv_ptr;
|
|
Packit |
549fdc |
*send_int = (long) session->internals.transport_send_ptr;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_transport_get_int:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Used to get the first argument of the transport function (like
|
|
Packit |
549fdc |
* PUSH and PULL). This must have been set using
|
|
Packit |
549fdc |
* gnutls_transport_set_int().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: The first argument of the transport function.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.1.9
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
int gnutls_transport_get_int(gnutls_session_t session)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
return (long) session->internals.transport_recv_ptr;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_bye:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @how: is an integer
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Terminates the current TLS/SSL connection. The connection should
|
|
Packit |
549fdc |
* have been initiated using gnutls_handshake(). @how should be one
|
|
Packit |
549fdc |
* of %GNUTLS_SHUT_RDWR, %GNUTLS_SHUT_WR.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* In case of %GNUTLS_SHUT_RDWR the TLS session gets
|
|
Packit |
549fdc |
* terminated and further receives and sends will be disallowed. If
|
|
Packit |
549fdc |
* the return value is zero you may continue using the underlying
|
|
Packit |
549fdc |
* transport layer. %GNUTLS_SHUT_RDWR sends an alert containing a close
|
|
Packit |
549fdc |
* request and waits for the peer to reply with the same message.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* In case of %GNUTLS_SHUT_WR the TLS session gets terminated
|
|
Packit |
549fdc |
* and further sends will be disallowed. In order to reuse the
|
|
Packit |
549fdc |
* connection you should wait for an EOF from the peer.
|
|
Packit |
549fdc |
* %GNUTLS_SHUT_WR sends an alert containing a close request.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Note that not all implementations will properly terminate a TLS
|
|
Packit |
549fdc |
* connection. Some of them, usually for performance reasons, will
|
|
Packit |
549fdc |
* terminate only the underlying transport layer, and thus not
|
|
Packit |
549fdc |
* distinguishing between a malicious party prematurely terminating
|
|
Packit |
549fdc |
* the connection and normal termination.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function may also return %GNUTLS_E_AGAIN or
|
|
Packit |
549fdc |
* %GNUTLS_E_INTERRUPTED; cf. gnutls_record_get_direction().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: %GNUTLS_E_SUCCESS on success, or an error code, see
|
|
Packit |
549fdc |
* function documentation for entire semantics.
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
int gnutls_bye(gnutls_session_t session, gnutls_close_request_t how)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
int ret = 0;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
switch (BYE_STATE) {
|
|
Packit |
549fdc |
case BYE_STATE0:
|
|
Packit |
549fdc |
ret = _gnutls_io_write_flush(session);
|
|
Packit |
549fdc |
BYE_STATE = BYE_STATE0;
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
/* fall through */
|
|
Packit |
549fdc |
case BYE_STATE1:
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
gnutls_alert_send(session, GNUTLS_AL_WARNING,
|
|
Packit |
549fdc |
GNUTLS_A_CLOSE_NOTIFY);
|
|
Packit |
549fdc |
BYE_STATE = BYE_STATE1;
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
/* fall through */
|
|
Packit |
549fdc |
case BYE_STATE2:
|
|
Packit |
549fdc |
BYE_STATE = BYE_STATE2;
|
|
Packit |
549fdc |
if (how == GNUTLS_SHUT_RDWR) {
|
|
Packit |
549fdc |
do {
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_gnutls_recv_int(session, GNUTLS_ALERT,
|
|
Packit |
549fdc |
NULL, 0, NULL,
|
|
Packit |
549fdc |
session->internals.
|
|
Packit |
549fdc |
record_timeout_ms);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
while (ret == GNUTLS_E_GOT_APPLICATION_DATA);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (ret >= 0)
|
|
Packit |
549fdc |
session->internals.may_not_read = 1;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
BYE_STATE = BYE_STATE2;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
break;
|
|
Packit |
549fdc |
default:
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return GNUTLS_E_INTERNAL_ERROR;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
BYE_STATE = BYE_STATE0;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session->internals.may_not_write = 1;
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
inline static void session_unresumable(gnutls_session_t session)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
session->internals.resumable = RESUME_FALSE;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* returns 0 if session is valid
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
inline static int session_is_valid(gnutls_session_t session)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
if (session->internals.invalid_connection != 0)
|
|
Packit |
549fdc |
return GNUTLS_E_INVALID_SESSION;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Copies the record version into the headers. The
|
|
Packit |
549fdc |
* version must have 2 bytes at least.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
inline static int
|
|
Packit |
549fdc |
copy_record_version(gnutls_session_t session,
|
|
Packit |
549fdc |
gnutls_handshake_description_t htype,
|
|
Packit |
549fdc |
uint8_t version[2])
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
const version_entry_st *lver;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
lver = get_version(session);
|
|
Packit |
549fdc |
if (session->internals.initial_negotiation_completed ||
|
|
Packit |
549fdc |
htype != GNUTLS_HANDSHAKE_CLIENT_HELLO ||
|
|
Packit |
549fdc |
session->internals.default_record_version[0] == 0) {
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (unlikely(lver == NULL))
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
version[0] = lver->major;
|
|
Packit |
549fdc |
version[1] = lver->minor;
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
version[0] = session->internals.default_record_version[0];
|
|
Packit |
549fdc |
version[1] = session->internals.default_record_version[1];
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Increments the sequence value
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
inline static int
|
|
Packit |
549fdc |
sequence_increment(gnutls_session_t session, gnutls_uint64 * value)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
if (IS_DTLS(session)) {
|
|
Packit |
549fdc |
return _gnutls_uint48pp(value);
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
return _gnutls_uint64pp(value);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* This function behaves exactly like write(). The only difference is
|
|
Packit |
549fdc |
* that it accepts, the gnutls_session_t and the content_type_t of data to
|
|
Packit |
549fdc |
* send (if called by the user the Content is specific)
|
|
Packit |
549fdc |
* It is intended to transfer data, under the current session.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* @type: The content type to send
|
|
Packit |
549fdc |
* @htype: If this is a handshake message then the handshake type
|
|
Packit |
549fdc |
* @epoch_rel: %EPOCH_READ_* or %EPOCH_WRITE_*
|
|
Packit |
549fdc |
* @data: the data to be sent
|
|
Packit |
549fdc |
* @data_size: the size of the @data
|
|
Packit |
549fdc |
* @min_pad: the minimum required padding
|
|
Packit |
549fdc |
* @mflags: zero or %MBUFFER_FLUSH
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Oct 30 2001: Removed capability to send data more than MAX_RECORD_SIZE.
|
|
Packit |
549fdc |
* This makes the function much easier to read, and more error resistant
|
|
Packit |
549fdc |
* (there were cases were the old function could mess everything up).
|
|
Packit |
549fdc |
* --nmav
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function may accept a NULL pointer for data, and 0 for size, if
|
|
Packit |
549fdc |
* and only if the previous send was interrupted for some reason.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
ssize_t
|
|
Packit |
549fdc |
_gnutls_send_tlen_int(gnutls_session_t session, content_type_t type,
|
|
Packit |
549fdc |
gnutls_handshake_description_t htype,
|
|
Packit |
549fdc |
unsigned int epoch_rel, const void *_data,
|
|
Packit |
549fdc |
size_t data_size, size_t min_pad,
|
|
Packit |
549fdc |
unsigned int mflags)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
mbuffer_st *bufel;
|
|
Packit |
549fdc |
ssize_t cipher_size;
|
|
Packit |
549fdc |
int retval, ret;
|
|
Packit |
549fdc |
int send_data_size;
|
|
Packit |
549fdc |
uint8_t *headers;
|
|
Packit |
549fdc |
int header_size;
|
|
Packit |
549fdc |
const uint8_t *data = _data;
|
|
Packit |
549fdc |
record_parameters_st *record_params;
|
|
Packit |
549fdc |
size_t max_send_size;
|
|
Packit |
549fdc |
record_state_st *record_state;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = _gnutls_epoch_get(session, epoch_rel, &record_params);
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Safeguard against processing data with an incomplete cipher state. */
|
|
Packit |
549fdc |
if (!record_params->initialized)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
record_state = &record_params->write;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Do not allow null pointer if the send buffer is empty.
|
|
Packit |
549fdc |
* If the previous send was interrupted then a null pointer is
|
|
Packit |
549fdc |
* ok, and means to resume.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (session->internals.record_send_buffer.byte_length == 0 &&
|
|
Packit |
549fdc |
(data_size == 0 && _data == NULL)) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return GNUTLS_E_INVALID_REQUEST;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (type != GNUTLS_ALERT) /* alert messages are sent anyway */
|
|
Packit |
549fdc |
if (session_is_valid(session)
|
|
Packit |
549fdc |
|| session->internals.may_not_write != 0) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return GNUTLS_E_INVALID_SESSION;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
max_send_size = max_user_send_size(session, record_params);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (data_size > max_send_size) {
|
|
Packit |
549fdc |
if (IS_DTLS(session))
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_LARGE_PACKET);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
send_data_size = max_send_size;
|
|
Packit |
549fdc |
} else
|
|
Packit |
549fdc |
send_data_size = data_size;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Only encrypt if we don't have data to send
|
|
Packit |
549fdc |
* from the previous run. - probably interrupted.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (mflags != 0
|
|
Packit |
549fdc |
&& session->internals.record_send_buffer.byte_length > 0) {
|
|
Packit |
549fdc |
ret = _gnutls_io_write_flush(session);
|
|
Packit |
549fdc |
if (ret > 0)
|
|
Packit |
549fdc |
cipher_size = ret;
|
|
Packit |
549fdc |
else
|
|
Packit |
549fdc |
cipher_size = 0;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
retval = session->internals.record_send_buffer_user_size;
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
if (unlikely((send_data_size == 0 && min_pad == 0)))
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* now proceed to packet encryption
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
cipher_size = MAX_RECORD_SEND_SIZE(session);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
bufel = _mbuffer_alloc_align16(cipher_size + CIPHER_SLACK_SIZE,
|
|
Packit |
549fdc |
get_total_headers2(session, record_params));
|
|
Packit |
549fdc |
if (bufel == NULL)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
headers = _mbuffer_get_uhead_ptr(bufel);
|
|
Packit |
549fdc |
headers[0] = type;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Use the default record version, if it is
|
|
Packit |
549fdc |
* set. */
|
|
Packit |
549fdc |
ret = copy_record_version(session, htype, &headers[1]);
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Adjust header length and add sequence for DTLS */
|
|
Packit |
549fdc |
if (IS_DTLS(session))
|
|
Packit |
549fdc |
memcpy(&headers[3],
|
|
Packit |
549fdc |
record_state->sequence_number.i, 8);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: Preparing Packet %s(%d) with length: %d and min pad: %d\n",
|
|
Packit |
549fdc |
session, _gnutls_packet2str(type), type,
|
|
Packit |
549fdc |
(int) data_size, (int) min_pad);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
header_size = RECORD_HEADER_SIZE(session);
|
|
Packit |
549fdc |
_mbuffer_set_udata_size(bufel, cipher_size);
|
|
Packit |
549fdc |
_mbuffer_set_uhead_size(bufel, header_size);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_gnutls_encrypt(session,
|
|
Packit |
549fdc |
data, send_data_size, min_pad,
|
|
Packit |
549fdc |
bufel, type, record_params);
|
|
Packit |
549fdc |
if (ret <= 0) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
if (ret == 0)
|
|
Packit |
549fdc |
ret = GNUTLS_E_ENCRYPTION_FAILED;
|
|
Packit |
549fdc |
gnutls_free(bufel);
|
|
Packit |
549fdc |
return ret; /* error */
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
cipher_size = _mbuffer_get_udata_size(bufel);
|
|
Packit |
549fdc |
retval = send_data_size;
|
|
Packit |
549fdc |
session->internals.record_send_buffer_user_size =
|
|
Packit |
549fdc |
send_data_size;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* increase sequence number
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (sequence_increment
|
|
Packit |
549fdc |
(session, &record_state->sequence_number) != 0) {
|
|
Packit |
549fdc |
session_invalidate(session);
|
|
Packit |
549fdc |
gnutls_free(bufel);
|
|
Packit |
549fdc |
return
|
|
Packit |
549fdc |
gnutls_assert_val
|
|
Packit |
549fdc |
(GNUTLS_E_RECORD_LIMIT_REACHED);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = _gnutls_io_write_buffered(session, bufel, mflags);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (ret != cipher_size) {
|
|
Packit |
549fdc |
/* If we have sent any data then just return
|
|
Packit |
549fdc |
* the error value. Do not invalidate the session.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (ret < 0 && gnutls_error_is_fatal(ret) == 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (ret > 0)
|
|
Packit |
549fdc |
ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session_unresumable(session);
|
|
Packit |
549fdc |
session->internals.may_not_write = 1;
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session->internals.record_send_buffer_user_size = 0;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: Sent Packet[%d] %s(%d) in epoch %d and length: %d\n",
|
|
Packit |
549fdc |
session, (unsigned int)
|
|
Packit |
549fdc |
_gnutls_uint64touint32(&record_state->sequence_number),
|
|
Packit |
549fdc |
_gnutls_packet2str(type), type, (int) record_params->epoch,
|
|
Packit |
549fdc |
(int) cipher_size);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return retval;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
inline static int
|
|
Packit |
549fdc |
check_recv_type(gnutls_session_t session, content_type_t recv_type)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
switch (recv_type) {
|
|
Packit |
549fdc |
case GNUTLS_CHANGE_CIPHER_SPEC:
|
|
Packit |
549fdc |
case GNUTLS_ALERT:
|
|
Packit |
549fdc |
case GNUTLS_HANDSHAKE:
|
|
Packit |
549fdc |
case GNUTLS_HEARTBEAT:
|
|
Packit |
549fdc |
case GNUTLS_APPLICATION_DATA:
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
default:
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
_gnutls_audit_log(session,
|
|
Packit |
549fdc |
"Received record packet of unknown type %u\n",
|
|
Packit |
549fdc |
(unsigned int) recv_type);
|
|
Packit |
549fdc |
return GNUTLS_E_UNEXPECTED_PACKET;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Checks if there are pending data in the record buffers. If there are
|
|
Packit |
549fdc |
* then it copies the data.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
static int
|
|
Packit |
549fdc |
get_data_from_buffers(gnutls_session_t session, content_type_t type,
|
|
Packit |
549fdc |
uint8_t * data, int data_size, void *seq)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
if ((type == GNUTLS_APPLICATION_DATA ||
|
|
Packit |
549fdc |
type == GNUTLS_HANDSHAKE || type == GNUTLS_CHANGE_CIPHER_SPEC)
|
|
Packit |
549fdc |
&& _gnutls_record_buffer_get_size(session) > 0) {
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_gnutls_record_buffer_get(type, session, data,
|
|
Packit |
549fdc |
data_size, seq);
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
if (IS_DTLS(session)) {
|
|
Packit |
549fdc |
if (ret == GNUTLS_E_UNEXPECTED_PACKET) {
|
|
Packit |
549fdc |
ret = GNUTLS_E_AGAIN;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Checks and retrieves any pending data in the application data record buffers.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
static int
|
|
Packit |
549fdc |
get_packet_from_buffers(gnutls_session_t session, content_type_t type,
|
|
Packit |
549fdc |
gnutls_packet_t *packet)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
if (_gnutls_record_buffer_get_size(session) > 0) {
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_gnutls_record_buffer_get_packet(type, session, packet);
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
if (IS_DTLS(session)) {
|
|
Packit |
549fdc |
if (ret == GNUTLS_E_UNEXPECTED_PACKET) {
|
|
Packit |
549fdc |
ret = GNUTLS_E_AGAIN;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
*packet = NULL;
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Here we check if the advertized version is the one we
|
|
Packit |
549fdc |
* negotiated in the handshake.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
inline static int
|
|
Packit |
549fdc |
record_check_version(gnutls_session_t session,
|
|
Packit |
549fdc |
gnutls_handshake_description_t htype,
|
|
Packit |
549fdc |
uint8_t version[2])
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
const version_entry_st *vers = get_version(session);
|
|
Packit |
549fdc |
int diff = 0;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (vers->major != version[0] || vers->minor != version[1])
|
|
Packit |
549fdc |
diff = 1;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (!IS_DTLS(session)) {
|
|
Packit |
549fdc |
if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO ||
|
|
Packit |
549fdc |
htype == GNUTLS_HANDSHAKE_SERVER_HELLO) {
|
|
Packit |
549fdc |
if (version[0] != 3) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n",
|
|
Packit |
549fdc |
session, htype, version[0],
|
|
Packit |
549fdc |
version[1]);
|
|
Packit |
549fdc |
return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
} else if (diff != 0) {
|
|
Packit |
549fdc |
/* Reject record packets that have a different version than the
|
|
Packit |
549fdc |
* one negotiated. Note that this version is not protected by any
|
|
Packit |
549fdc |
* mac. I don't really think that this check serves any purpose.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n",
|
|
Packit |
549fdc |
session, htype, version[0], version[1]);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
} else { /* DTLS */
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* In DTLS the only information we have here is whether we
|
|
Packit |
549fdc |
* expect a handshake message or not.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (htype == (gnutls_handshake_description_t) - 1) {
|
|
Packit |
549fdc |
if (diff) {
|
|
Packit |
549fdc |
/* Reject record packets that have a different version than the
|
|
Packit |
549fdc |
* one negotiated. Note that this version is not protected by any
|
|
Packit |
549fdc |
* mac. I don't really think that this check serves any purpose.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n",
|
|
Packit |
549fdc |
session, htype, version[0],
|
|
Packit |
549fdc |
version[1]);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
} else if (vers->id > GNUTLS_DTLS1_0 && version[0] > 254) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: INVALID DTLS VERSION PACKET: (%d) %d.%d\n",
|
|
Packit |
549fdc |
session, htype, version[0], version[1]);
|
|
Packit |
549fdc |
return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
|
|
Packit |
549fdc |
} else if (vers->id == GNUTLS_DTLS0_9 && version[0] > 1) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: INVALID DTLS VERSION PACKET: (%d) %d.%d\n",
|
|
Packit |
549fdc |
session, htype, version[0], version[1]);
|
|
Packit |
549fdc |
return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* This function will check if the received record type is
|
|
Packit |
549fdc |
* the one we actually expect and adds it to the proper
|
|
Packit |
549fdc |
* buffer. The bufel will be deinitialized after calling
|
|
Packit |
549fdc |
* this function, even if it fails.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
static int
|
|
Packit |
549fdc |
record_add_to_buffers(gnutls_session_t session,
|
|
Packit |
549fdc |
struct tls_record_st *recv, content_type_t type,
|
|
Packit |
549fdc |
gnutls_handshake_description_t htype,
|
|
Packit |
549fdc |
gnutls_uint64 * seq, mbuffer_st * bufel)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if ((recv->type == type)
|
|
Packit |
549fdc |
&& (type == GNUTLS_APPLICATION_DATA ||
|
|
Packit |
549fdc |
type == GNUTLS_CHANGE_CIPHER_SPEC ||
|
|
Packit |
549fdc |
type == GNUTLS_HANDSHAKE)) {
|
|
Packit |
549fdc |
_gnutls_record_buffer_put(session, type, seq, bufel);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* if we received application data as expected then we
|
|
Packit |
549fdc |
* deactivate the async timer */
|
|
Packit |
549fdc |
_dtls_async_timer_delete(session);
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
/* if the expected type is different than the received
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
switch (recv->type) {
|
|
Packit |
549fdc |
case GNUTLS_ALERT:
|
|
Packit |
549fdc |
if (bufel->msg.size < 2) {
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
gnutls_assert_val
|
|
Packit |
549fdc |
(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
|
|
Packit |
549fdc |
goto unexpected_packet;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: Alert[%d|%d] - %s - was received\n",
|
|
Packit |
549fdc |
session, bufel->msg.data[0],
|
|
Packit |
549fdc |
bufel->msg.data[1],
|
|
Packit |
549fdc |
gnutls_alert_get_name((int) bufel->msg.
|
|
Packit |
549fdc |
data[1]));
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (!session->internals.initial_negotiation_completed &&
|
|
Packit |
549fdc |
session->internals.handshake_in_progress && STATE == STATE0) { /* handshake hasn't started */
|
|
Packit |
549fdc |
ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
|
|
Packit |
549fdc |
goto unexpected_packet;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session->internals.last_alert = bufel->msg.data[1];
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* if close notify is received and
|
|
Packit |
549fdc |
* the alert is not fatal
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (bufel->msg.data[1] == GNUTLS_A_CLOSE_NOTIFY
|
|
Packit |
549fdc |
&& bufel->msg.data[0] != GNUTLS_AL_FATAL) {
|
|
Packit |
549fdc |
/* If we have been expecting for an alert do
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
session->internals.read_eof = 1;
|
|
Packit |
549fdc |
ret = GNUTLS_E_SESSION_EOF;
|
|
Packit |
549fdc |
goto cleanup;
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
/* if the alert is FATAL or WARNING
|
|
Packit |
549fdc |
* return the apropriate message
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
ret = GNUTLS_E_WARNING_ALERT_RECEIVED;
|
|
Packit |
549fdc |
if (bufel->msg.data[0] == GNUTLS_AL_FATAL) {
|
|
Packit |
549fdc |
session_unresumable(session);
|
|
Packit |
549fdc |
session_invalidate(session);
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
gnutls_assert_val
|
|
Packit |
549fdc |
(GNUTLS_E_FATAL_ALERT_RECEIVED);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
goto cleanup;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
break;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
case GNUTLS_CHANGE_CIPHER_SPEC:
|
|
Packit |
549fdc |
if (!(IS_DTLS(session))) {
|
|
Packit |
549fdc |
ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
|
|
Packit |
549fdc |
goto cleanup;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_gnutls_record_buffer_put(session, recv->type, seq,
|
|
Packit |
549fdc |
bufel);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
break;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#ifdef ENABLE_HEARTBEAT
|
|
Packit |
549fdc |
case GNUTLS_HEARTBEAT:
|
|
Packit |
549fdc |
ret = _gnutls_heartbeat_handle(session, bufel);
|
|
Packit |
549fdc |
goto cleanup;
|
|
Packit |
549fdc |
#endif
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
case GNUTLS_APPLICATION_DATA:
|
|
Packit |
549fdc |
if (session->internals.
|
|
Packit |
549fdc |
initial_negotiation_completed == 0) {
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
gnutls_assert_val
|
|
Packit |
549fdc |
(GNUTLS_E_UNEXPECTED_PACKET);
|
|
Packit |
549fdc |
goto unexpected_packet;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* the got_application data is only returned
|
|
Packit |
549fdc |
* if expecting client hello (for rehandshake
|
|
Packit |
549fdc |
* reasons). Otherwise it is an unexpected packet
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (type == GNUTLS_ALERT
|
|
Packit |
549fdc |
|| ((htype == GNUTLS_HANDSHAKE_SERVER_HELLO || htype == GNUTLS_HANDSHAKE_CLIENT_HELLO)
|
|
Packit |
549fdc |
&& type == GNUTLS_HANDSHAKE)) {
|
|
Packit |
549fdc |
/* even if data is unexpected put it into the buffer */
|
|
Packit |
549fdc |
_gnutls_record_buffer_put(session, recv->type,
|
|
Packit |
549fdc |
seq, bufel);
|
|
Packit |
549fdc |
return
|
|
Packit |
549fdc |
gnutls_assert_val
|
|
Packit |
549fdc |
(GNUTLS_E_GOT_APPLICATION_DATA);
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
gnutls_assert_val
|
|
Packit |
549fdc |
(GNUTLS_E_UNEXPECTED_PACKET);
|
|
Packit |
549fdc |
goto unexpected_packet;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
break;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
case GNUTLS_HANDSHAKE:
|
|
Packit |
549fdc |
/* In DTLS we might receive a handshake replay from the peer to indicate
|
|
Packit |
549fdc |
* the our last TLS handshake messages were not received.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (IS_DTLS(session)) {
|
|
Packit |
549fdc |
if (type == GNUTLS_CHANGE_CIPHER_SPEC) {
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
gnutls_assert_val
|
|
Packit |
549fdc |
(GNUTLS_E_UNEXPECTED_PACKET);
|
|
Packit |
549fdc |
goto unexpected_packet;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (_dtls_is_async(session)
|
|
Packit |
549fdc |
&& _dtls_async_timer_active(session)) {
|
|
Packit |
549fdc |
if (session->security_parameters.
|
|
Packit |
549fdc |
entity == GNUTLS_SERVER
|
|
Packit |
549fdc |
&& bufel->htype ==
|
|
Packit |
549fdc |
GNUTLS_HANDSHAKE_CLIENT_HELLO)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
/* client requested rehandshake. Delete the timer */
|
|
Packit |
549fdc |
_dtls_async_timer_delete
|
|
Packit |
549fdc |
(session);
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
session->internals.
|
|
Packit |
549fdc |
recv_state =
|
|
Packit |
549fdc |
RECV_STATE_DTLS_RETRANSMIT;
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_dtls_retransmit
|
|
Packit |
549fdc |
(session);
|
|
Packit |
549fdc |
if (ret == 0) {
|
|
Packit |
549fdc |
session->internals.
|
|
Packit |
549fdc |
recv_state =
|
|
Packit |
549fdc |
RECV_STATE_0;
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
gnutls_assert_val
|
|
Packit |
549fdc |
(GNUTLS_E_AGAIN);
|
|
Packit |
549fdc |
goto unexpected_packet;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
goto cleanup;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* This is legal if HELLO_REQUEST is received - and we are a client.
|
|
Packit |
549fdc |
* If we are a server, a client may initiate a renegotiation at any time.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (session->security_parameters.entity ==
|
|
Packit |
549fdc |
GNUTLS_SERVER
|
|
Packit |
549fdc |
&& session->internals.handshake_in_progress == 0
|
|
Packit |
549fdc |
&& bufel->htype ==
|
|
Packit |
549fdc |
GNUTLS_HANDSHAKE_CLIENT_HELLO) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
_gnutls_record_buffer_put(session,
|
|
Packit |
549fdc |
recv->type,
|
|
Packit |
549fdc |
seq, bufel);
|
|
Packit |
549fdc |
return GNUTLS_E_REHANDSHAKE;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* If we are already in a handshake then a Hello
|
|
Packit |
549fdc |
* Request is illegal. But here we don't really care
|
|
Packit |
549fdc |
* since this message will never make it up here.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* So we accept it, if it is a Hello. If not, this will
|
|
Packit |
549fdc |
* fail and trigger flight retransmissions after some time. */
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_gnutls_recv_hello_request(session,
|
|
Packit |
549fdc |
bufel->msg.data,
|
|
Packit |
549fdc |
bufel->msg.size);
|
|
Packit |
549fdc |
goto unexpected_packet;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
break;
|
|
Packit |
549fdc |
default:
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: Received unexpected packet %d (%s) expecting %d (%s)\n",
|
|
Packit |
549fdc |
session, recv->type,
|
|
Packit |
549fdc |
_gnutls_packet2str(recv->type), type,
|
|
Packit |
549fdc |
_gnutls_packet2str(type));
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
ret = GNUTLS_E_UNEXPECTED_PACKET;
|
|
Packit |
549fdc |
goto unexpected_packet;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
unexpected_packet:
|
|
Packit |
549fdc |
if (IS_DTLS(session) && ret != GNUTLS_E_REHANDSHAKE) {
|
|
Packit |
549fdc |
_mbuffer_xfree(&bufel);
|
|
Packit |
549fdc |
RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
cleanup:
|
|
Packit |
549fdc |
_mbuffer_xfree(&bufel);
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Checks the record headers and returns the length, version and
|
|
Packit |
549fdc |
* content type.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
static void
|
|
Packit |
549fdc |
record_read_headers(gnutls_session_t session,
|
|
Packit |
549fdc |
uint8_t headers[MAX_RECORD_HEADER_SIZE],
|
|
Packit |
549fdc |
content_type_t type,
|
|
Packit |
549fdc |
gnutls_handshake_description_t htype,
|
|
Packit |
549fdc |
struct tls_record_st *record)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Read the first two bytes to determine if this is a
|
|
Packit |
549fdc |
* version 2 message
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#ifdef ENABLE_SSL2
|
|
Packit |
549fdc |
if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO
|
|
Packit |
549fdc |
&& type == GNUTLS_HANDSHAKE && headers[0] > 127
|
|
Packit |
549fdc |
&& !(IS_DTLS(session))) {
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* if msb set and expecting handshake message
|
|
Packit |
549fdc |
* it should be SSL 2 hello
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
record->version[0] = 3; /* assume SSL 3.0 */
|
|
Packit |
549fdc |
record->version[1] = 0;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
record->length = (((headers[0] & 0x7f) << 8)) | headers[1];
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* SSL 2.0 headers */
|
|
Packit |
549fdc |
record->header_size = record->packet_size = 2;
|
|
Packit |
549fdc |
record->type = GNUTLS_HANDSHAKE; /* we accept only v2 client hello
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* in order to assist the handshake protocol.
|
|
Packit |
549fdc |
* V2 compatibility is a mess.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
record->v2 = 1;
|
|
Packit |
549fdc |
record->epoch = 0;
|
|
Packit |
549fdc |
memset(&record->sequence, 0, sizeof(record->sequence));
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: SSL 2.0 %s packet received. Length: %d\n",
|
|
Packit |
549fdc |
session, _gnutls_packet2str(record->type),
|
|
Packit |
549fdc |
record->length);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
} else
|
|
Packit |
549fdc |
#endif
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
/* dtls version 1.0 and TLS version 1.x */
|
|
Packit |
549fdc |
#ifdef ENABLE_SSL2
|
|
Packit |
549fdc |
record->v2 = 0;
|
|
Packit |
549fdc |
#endif
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
record->type = headers[0];
|
|
Packit |
549fdc |
record->version[0] = headers[1];
|
|
Packit |
549fdc |
record->version[1] = headers[2];
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (IS_DTLS(session)) {
|
|
Packit |
549fdc |
memcpy(record->sequence.i, &headers[3], 8);
|
|
Packit |
549fdc |
record->length = _gnutls_read_uint16(&headers[11]);
|
|
Packit |
549fdc |
record->epoch =
|
|
Packit |
549fdc |
_gnutls_read_uint16(record->sequence.i);
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
memset(&record->sequence, 0,
|
|
Packit |
549fdc |
sizeof(record->sequence));
|
|
Packit |
549fdc |
record->length = _gnutls_read_uint16(&headers[3]);
|
|
Packit |
549fdc |
record->epoch = 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: SSL %d.%d %s packet received. Epoch %d, length: %d\n",
|
|
Packit |
549fdc |
session, (int) record->version[0],
|
|
Packit |
549fdc |
(int) record->version[1],
|
|
Packit |
549fdc |
_gnutls_packet2str(record->type), (int) record->epoch,
|
|
Packit |
549fdc |
record->length);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
record->packet_size += record->length;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
static int recv_headers(gnutls_session_t session,
|
|
Packit |
549fdc |
record_parameters_st *record_params,
|
|
Packit |
549fdc |
content_type_t type,
|
|
Packit |
549fdc |
gnutls_handshake_description_t htype,
|
|
Packit |
549fdc |
struct tls_record_st *record, unsigned int *ms)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
gnutls_datum_t raw; /* raw headers */
|
|
Packit |
549fdc |
/* Read the headers.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
record->header_size = record->packet_size =
|
|
Packit |
549fdc |
RECORD_HEADER_SIZE(session);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_gnutls_io_read_buffered(session, record->header_size, -1, ms);
|
|
Packit |
549fdc |
if (ret != record->header_size) {
|
|
Packit |
549fdc |
if (ret < 0 && gnutls_error_is_fatal(ret) == 0)
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (ret > 0)
|
|
Packit |
549fdc |
ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
|
|
Packit |
549fdc |
else if (ret == 0)
|
|
Packit |
549fdc |
ret = GNUTLS_E_PREMATURE_TERMINATION;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = _mbuffer_linearize_align16(&session->internals.record_recv_buffer,
|
|
Packit |
549fdc |
get_total_headers2(session, record_params));
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_mbuffer_head_get_first(&session->internals.record_recv_buffer,
|
|
Packit |
549fdc |
&raw;;
|
|
Packit |
549fdc |
if (raw.size < RECORD_HEADER_SIZE(session))
|
|
Packit |
549fdc |
return
|
|
Packit |
549fdc |
gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
record_read_headers(session, raw.data, type, htype, record);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Check if the DTLS epoch is valid */
|
|
Packit |
549fdc |
if (IS_DTLS(session)) {
|
|
Packit |
549fdc |
if (_gnutls_epoch_is_valid(session, record->epoch) == 0) {
|
|
Packit |
549fdc |
_gnutls_audit_log(session,
|
|
Packit |
549fdc |
"Discarded message[%u] with invalid epoch %u.\n",
|
|
Packit |
549fdc |
(unsigned int)
|
|
Packit |
549fdc |
_gnutls_uint64touint32(&record->
|
|
Packit |
549fdc |
sequence),
|
|
Packit |
549fdc |
(unsigned int) record->sequence.
|
|
Packit |
549fdc |
i[0] * 256 +
|
|
Packit |
549fdc |
(unsigned int) record->sequence.
|
|
Packit |
549fdc |
i[1]);
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
/* doesn't matter, just a fatal error */
|
|
Packit |
549fdc |
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Here we check if the Type of the received packet is
|
|
Packit |
549fdc |
* ok.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if ((ret = check_recv_type(session, record->type)) < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Here we check if the advertized version is the one we
|
|
Packit |
549fdc |
* negotiated in the handshake.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if ((ret =
|
|
Packit |
549fdc |
record_check_version(session, htype, record->version)) < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (record->length == 0 || record->length > max_record_recv_size(session)) {
|
|
Packit |
549fdc |
_gnutls_audit_log
|
|
Packit |
549fdc |
(session, "Received packet with illegal length: %u\n",
|
|
Packit |
549fdc |
(unsigned int) record->length);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (record->length == 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
|
|
Packit |
549fdc |
return
|
|
Packit |
549fdc |
gnutls_assert_val(GNUTLS_E_RECORD_OVERFLOW);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: Expected Packet %s(%d)\n", session,
|
|
Packit |
549fdc |
_gnutls_packet2str(type), type);
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: Received Packet %s(%d) with length: %d\n", session,
|
|
Packit |
549fdc |
_gnutls_packet2str(record->type), record->type,
|
|
Packit |
549fdc |
record->length);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* @ms: is the number of milliseconds to wait for data. Use zero for indefinite.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This will receive record layer packets and add them to
|
|
Packit |
549fdc |
* application_data_buffer and handshake_data_buffer.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* If the htype is not -1 then handshake timeouts
|
|
Packit |
549fdc |
* will be enforced.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
ssize_t
|
|
Packit |
549fdc |
_gnutls_recv_in_buffers(gnutls_session_t session, content_type_t type,
|
|
Packit |
549fdc |
gnutls_handshake_description_t htype,
|
|
Packit |
549fdc |
unsigned int ms)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
gnutls_uint64 *packet_sequence;
|
|
Packit |
549fdc |
gnutls_datum_t ciphertext;
|
|
Packit |
549fdc |
mbuffer_st *bufel = NULL, *decrypted = NULL;
|
|
Packit |
549fdc |
gnutls_datum_t t;
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
unsigned int empty_fragments = 0;
|
|
Packit |
549fdc |
record_parameters_st *record_params;
|
|
Packit |
549fdc |
record_state_st *record_state;
|
|
Packit |
549fdc |
struct tls_record_st record;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
begin:
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (empty_fragments > DEFAULT_MAX_EMPTY_RECORDS) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return GNUTLS_E_TOO_MANY_EMPTY_PACKETS;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (session->internals.read_eof != 0) {
|
|
Packit |
549fdc |
/* if we have already read an EOF
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
} else if (session_is_valid(session) != 0
|
|
Packit |
549fdc |
|| session->internals.may_not_read != 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INVALID_SESSION);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* get the record state parameters */
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_gnutls_epoch_get(session, EPOCH_READ_CURRENT, &record_params);
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Safeguard against processing data with an incomplete cipher state. */
|
|
Packit |
549fdc |
if (!record_params->initialized)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
record_state = &record_params->read;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* receive headers */
|
|
Packit |
549fdc |
ret = recv_headers(session, record_params, type, htype, &record, (!(session->internals.flags & GNUTLS_NONBLOCK))?&ms:0);
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
ret = gnutls_assert_val_fatal(ret);
|
|
Packit |
549fdc |
goto recv_error;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (IS_DTLS(session))
|
|
Packit |
549fdc |
packet_sequence = &record.sequence;
|
|
Packit |
549fdc |
else
|
|
Packit |
549fdc |
packet_sequence = &record_state->sequence_number;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Read the packet data and insert it to record_recv_buffer.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_gnutls_io_read_buffered(session, record.packet_size,
|
|
Packit |
549fdc |
record.type, (!(session->internals.flags & GNUTLS_NONBLOCK))?&ms:0);
|
|
Packit |
549fdc |
if (ret != record.packet_size) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
goto recv_error;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* ok now we are sure that we have read all the data - so
|
|
Packit |
549fdc |
* move on !
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
ret = _mbuffer_linearize_align16(&session->internals.record_recv_buffer,
|
|
Packit |
549fdc |
get_total_headers2(session, record_params));
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
bufel =
|
|
Packit |
549fdc |
_mbuffer_head_get_first(&session->internals.record_recv_buffer,
|
|
Packit |
549fdc |
NULL);
|
|
Packit |
549fdc |
if (bufel == NULL)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* We allocate the maximum possible to allow few compressed bytes to expand to a
|
|
Packit |
549fdc |
* full record. Moreover we add space for any pad and the MAC (in case
|
|
Packit |
549fdc |
* they are encrypted).
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
ret = max_decrypted_size(session) + MAX_PAD_SIZE + MAX_HASH_SIZE;
|
|
Packit |
549fdc |
decrypted = _mbuffer_alloc_align16(ret, 0);
|
|
Packit |
549fdc |
if (decrypted == NULL)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_mbuffer_set_udata_size(decrypted, ret);
|
|
Packit |
549fdc |
ciphertext.data =
|
|
Packit |
549fdc |
(uint8_t *) _mbuffer_get_udata_ptr(bufel) + record.header_size;
|
|
Packit |
549fdc |
ciphertext.size = record.length;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* decrypt the data we got.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
t.data = _mbuffer_get_udata_ptr(decrypted);
|
|
Packit |
549fdc |
t.size = _mbuffer_get_udata_size(decrypted);
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_gnutls_decrypt(session, &ciphertext, &t,
|
|
Packit |
549fdc |
record.type, record_params, packet_sequence);
|
|
Packit |
549fdc |
if (ret >= 0)
|
|
Packit |
549fdc |
_mbuffer_set_udata_size(decrypted, ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_mbuffer_head_remove_bytes(&session->internals.record_recv_buffer,
|
|
Packit |
549fdc |
record.header_size + record.length);
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
_gnutls_audit_log(session,
|
|
Packit |
549fdc |
"Discarded message[%u] due to invalid decryption\n",
|
|
Packit |
549fdc |
(unsigned int)
|
|
Packit |
549fdc |
_gnutls_uint64touint32(packet_sequence));
|
|
Packit |
549fdc |
goto sanity_check_error;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (IS_DTLS(session)) {
|
|
Packit |
549fdc |
/* check for duplicates. We check after the message
|
|
Packit |
549fdc |
* is processed and authenticated to avoid someone
|
|
Packit |
549fdc |
* messing with our windows. */
|
|
Packit |
549fdc |
if (likely(!(session->internals.flags & GNUTLS_NO_REPLAY_PROTECTION))) {
|
|
Packit |
549fdc |
ret = _dtls_record_check(record_params, packet_sequence);
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: Discarded duplicate message[%u.%u]: %s\n",
|
|
Packit |
549fdc |
session,
|
|
Packit |
549fdc |
(unsigned int) record.sequence.i[0] * 256 +
|
|
Packit |
549fdc |
(unsigned int) record.sequence.i[1],
|
|
Packit |
549fdc |
(unsigned int)
|
|
Packit |
549fdc |
_gnutls_uint64touint32(packet_sequence),
|
|
Packit |
549fdc |
_gnutls_packet2str(record.type));
|
|
Packit |
549fdc |
goto sanity_check_error;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: Decrypted Packet[%u.%u] %s(%d) with length: %d\n",
|
|
Packit |
549fdc |
session,
|
|
Packit |
549fdc |
(unsigned int) record.sequence.i[0] * 256 +
|
|
Packit |
549fdc |
(unsigned int) record.sequence.i[1],
|
|
Packit |
549fdc |
(unsigned int)
|
|
Packit |
549fdc |
_gnutls_uint64touint32(packet_sequence),
|
|
Packit |
549fdc |
_gnutls_packet2str(record.type), record.type,
|
|
Packit |
549fdc |
(int) _mbuffer_get_udata_size(decrypted));
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* store the last valid sequence number. We don't use that internally but
|
|
Packit |
549fdc |
* callers of gnutls_record_get_state() could take advantage of it. */
|
|
Packit |
549fdc |
memcpy(&record_state->sequence_number, packet_sequence, 8);
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
_gnutls_record_log
|
|
Packit |
549fdc |
("REC[%p]: Decrypted Packet[%u] %s(%d) with length: %d\n",
|
|
Packit |
549fdc |
session,
|
|
Packit |
549fdc |
(unsigned int)
|
|
Packit |
549fdc |
_gnutls_uint64touint32(packet_sequence),
|
|
Packit |
549fdc |
_gnutls_packet2str(record.type), record.type,
|
|
Packit |
549fdc |
(int) _mbuffer_get_udata_size(decrypted));
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Increase sequence number. We do both for TLS and DTLS, since in
|
|
Packit |
549fdc |
* DTLS we also rely on that number (roughly) since it may get reported
|
|
Packit |
549fdc |
* to application via gnutls_record_get_state().
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (sequence_increment(session, &record_state->sequence_number) != 0) {
|
|
Packit |
549fdc |
session_invalidate(session);
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
ret = GNUTLS_E_RECORD_LIMIT_REACHED;
|
|
Packit |
549fdc |
goto sanity_check_error;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* (originally for) TLS 1.0 CBC protection.
|
|
Packit |
549fdc |
* Actually this code is called if we just received
|
|
Packit |
549fdc |
* an empty packet. An empty TLS packet is usually
|
|
Packit |
549fdc |
* sent to protect some vulnerabilities in the CBC mode.
|
|
Packit |
549fdc |
* In that case we go to the beginning and start reading
|
|
Packit |
549fdc |
* the next packet.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
if (_mbuffer_get_udata_size(decrypted) == 0) {
|
|
Packit |
549fdc |
_mbuffer_xfree(&decrypted);
|
|
Packit |
549fdc |
empty_fragments++;
|
|
Packit |
549fdc |
goto begin;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
#ifdef ENABLE_SSL2
|
|
Packit |
549fdc |
if (record.v2) {
|
|
Packit |
549fdc |
decrypted->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2;
|
|
Packit |
549fdc |
} else
|
|
Packit |
549fdc |
#endif
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
uint8_t *p = _mbuffer_get_udata_ptr(decrypted);
|
|
Packit |
549fdc |
decrypted->htype = p[0];
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
record_add_to_buffers(session, &record, type, htype,
|
|
Packit |
549fdc |
packet_sequence, decrypted);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* decrypted is now either deinitialized or buffered somewhere else */
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
discard:
|
|
Packit |
549fdc |
session->internals.dtls.packets_dropped++;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* discard the whole received fragment. */
|
|
Packit |
549fdc |
bufel =
|
|
Packit |
549fdc |
_mbuffer_head_pop_first(&session->internals.
|
|
Packit |
549fdc |
record_recv_buffer);
|
|
Packit |
549fdc |
_mbuffer_xfree(&bufel);
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_AGAIN);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
sanity_check_error:
|
|
Packit |
549fdc |
if (IS_DTLS(session)) {
|
|
Packit |
549fdc |
session->internals.dtls.packets_dropped++;
|
|
Packit |
549fdc |
ret = gnutls_assert_val(GNUTLS_E_AGAIN);
|
|
Packit |
549fdc |
goto cleanup;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session_unresumable(session);
|
|
Packit |
549fdc |
session_invalidate(session);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
cleanup:
|
|
Packit |
549fdc |
_mbuffer_xfree(&decrypted);
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
recv_error:
|
|
Packit |
549fdc |
if (ret < 0
|
|
Packit |
549fdc |
&& (gnutls_error_is_fatal(ret) == 0
|
|
Packit |
549fdc |
|| ret == GNUTLS_E_TIMEDOUT))
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (type == GNUTLS_ALERT) { /* we were expecting close notify */
|
|
Packit |
549fdc |
session_invalidate(session);
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (IS_DTLS(session) && (ret == GNUTLS_E_DECRYPTION_FAILED ||
|
|
Packit |
549fdc |
ret == GNUTLS_E_UNSUPPORTED_VERSION_PACKET ||
|
|
Packit |
549fdc |
ret == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
|
|
Packit |
549fdc |
ret == GNUTLS_E_RECORD_OVERFLOW ||
|
|
Packit |
549fdc |
ret == GNUTLS_E_UNEXPECTED_PACKET ||
|
|
Packit |
549fdc |
ret == GNUTLS_E_ERROR_IN_FINISHED_PACKET ||
|
|
Packit |
549fdc |
ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET)) {
|
|
Packit |
549fdc |
goto discard;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session_invalidate(session);
|
|
Packit |
549fdc |
session_unresumable(session);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (ret == 0)
|
|
Packit |
549fdc |
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
|
|
Packit |
549fdc |
else
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Returns a value greater than zero (>= 0) if buffers should be checked
|
|
Packit |
549fdc |
* for data. */
|
|
Packit |
549fdc |
static ssize_t
|
|
Packit |
549fdc |
check_session_status(gnutls_session_t session)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (session->internals.read_eof != 0) {
|
|
Packit |
549fdc |
/* if we have already read an EOF
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
return 0;
|
|
Packit |
549fdc |
} else if (session_is_valid(session) != 0
|
|
Packit |
549fdc |
|| session->internals.may_not_read != 0) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return GNUTLS_E_INVALID_SESSION;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
switch (session->internals.recv_state) {
|
|
Packit |
549fdc |
case RECV_STATE_FALSE_START_HANDLING:
|
|
Packit |
549fdc |
return 1;
|
|
Packit |
549fdc |
case RECV_STATE_FALSE_START:
|
|
Packit |
549fdc |
/* if false start is not complete we always expect for handshake packets
|
|
Packit |
549fdc |
* prior to anything else. */
|
|
Packit |
549fdc |
if (session->security_parameters.entity == GNUTLS_CLIENT &&
|
|
Packit |
549fdc |
(session->internals.flags & GNUTLS_ENABLE_FALSE_START)) {
|
|
Packit |
549fdc |
/* Attempt to complete handshake */
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session->internals.recv_state = RECV_STATE_FALSE_START_HANDLING;
|
|
Packit |
549fdc |
ret = gnutls_handshake(session);
|
|
Packit |
549fdc |
if (ret < 0) {
|
|
Packit |
549fdc |
/* a temp or fatal error, make sure we reset the state
|
|
Packit |
549fdc |
* so we can resume or temp errors */
|
|
Packit |
549fdc |
session->internals.recv_state = RECV_STATE_FALSE_START;
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session->internals.recv_state = RECV_STATE_0;
|
|
Packit |
549fdc |
return 1;
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
case RECV_STATE_DTLS_RETRANSMIT:
|
|
Packit |
549fdc |
ret = _dtls_retransmit(session);
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session->internals.recv_state = RECV_STATE_0;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* Fall through: */
|
|
Packit |
549fdc |
FALLTHROUGH;
|
|
Packit |
549fdc |
case RECV_STATE_0:
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
_dtls_async_timer_check(session);
|
|
Packit |
549fdc |
return 1;
|
|
Packit |
549fdc |
default:
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* This function behaves exactly like read(). The only difference is
|
|
Packit |
549fdc |
* that it accepts the gnutls_session_t and the content_type_t of data to
|
|
Packit |
549fdc |
* receive (if called by the user the Content is Userdata only)
|
|
Packit |
549fdc |
* It is intended to receive data, under the current session.
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
ssize_t
|
|
Packit |
549fdc |
_gnutls_recv_int(gnutls_session_t session, content_type_t type,
|
|
Packit |
549fdc |
uint8_t * data, size_t data_size, void *seq,
|
|
Packit |
549fdc |
unsigned int ms)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if ((type != GNUTLS_ALERT && type != GNUTLS_HEARTBEAT)
|
|
Packit |
549fdc |
&& (data_size == 0 || data == NULL))
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = check_session_status(session);
|
|
Packit |
549fdc |
if (ret <= 0)
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/* If we have enough data in the cache do not bother receiving
|
|
Packit |
549fdc |
* a new packet. (in order to flush the cache)
|
|
Packit |
549fdc |
*/
|
|
Packit |
549fdc |
ret = get_data_from_buffers(session, type, data, data_size, seq);
|
|
Packit |
549fdc |
if (ret != 0)
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = _gnutls_recv_in_buffers(session, type, -1, ms);
|
|
Packit |
549fdc |
if (ret < 0 && ret != GNUTLS_E_SESSION_EOF)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return get_data_from_buffers(session, type, data, data_size, seq);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_packet_get:
|
|
Packit |
549fdc |
* @packet: is a #gnutls_packet_t type.
|
|
Packit |
549fdc |
* @data: will contain the data present in the @packet structure (may be %NULL)
|
|
Packit |
549fdc |
* @sequence: the 8-bytes of the packet sequence number (may be %NULL)
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function returns the data and sequence number associated with
|
|
Packit |
549fdc |
* the received packet.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.3.5
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
void gnutls_packet_get(gnutls_packet_t packet, gnutls_datum_t *data, unsigned char *sequence)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
if (unlikely(packet == NULL)) {
|
|
Packit |
549fdc |
gnutls_assert();
|
|
Packit |
549fdc |
if (data) {
|
|
Packit |
549fdc |
data->data = NULL;
|
|
Packit |
549fdc |
data->size = 0;
|
|
Packit |
549fdc |
return;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (sequence) {
|
|
Packit |
549fdc |
memcpy(sequence, packet->record_sequence.i, 8);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (data) {
|
|
Packit |
549fdc |
data->size = packet->msg.size - packet->mark;
|
|
Packit |
549fdc |
data->data = packet->msg.data + packet->mark;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_packet_deinit:
|
|
Packit |
549fdc |
* @packet: is a pointer to a #gnutls_packet_st structure.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function will deinitialize all data associated with
|
|
Packit |
549fdc |
* the received packet.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.3.5
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
void gnutls_packet_deinit(gnutls_packet_t packet)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
gnutls_free(packet);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_record_discard_queued:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function discards all queued to be sent packets in a TLS or DTLS session.
|
|
Packit |
549fdc |
* These are the packets queued after an interrupted gnutls_record_send().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: The number of bytes discarded.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.4.0
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
size_t
|
|
Packit |
549fdc |
gnutls_record_discard_queued(gnutls_session_t session)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
size_t ret = session->internals.record_send_buffer.byte_length;
|
|
Packit |
549fdc |
_mbuffer_head_clear(&session->internals.record_send_buffer);
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_record_recv_packet:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @packet: the structure that will hold the packet data
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This is a lower-level function than gnutls_record_recv() and allows
|
|
Packit |
549fdc |
* to directly receive the whole decrypted packet. That avoids a
|
|
Packit |
549fdc |
* memory copy, and is intended to be used by applications seeking high
|
|
Packit |
549fdc |
* performance.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* The received packet is accessed using gnutls_packet_get() and
|
|
Packit |
549fdc |
* must be deinitialized using gnutls_packet_deinit(). The returned
|
|
Packit |
549fdc |
* packet will be %NULL if the return value is zero (EOF).
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: The number of bytes received and zero on EOF (for stream
|
|
Packit |
549fdc |
* connections). A negative error code is returned in case of an error.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.3.5
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
ssize_t
|
|
Packit |
549fdc |
gnutls_record_recv_packet(gnutls_session_t session,
|
|
Packit |
549fdc |
gnutls_packet_t *packet)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (packet == NULL)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = check_session_status(session);
|
|
Packit |
549fdc |
if (ret <= 0)
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = get_packet_from_buffers(session, GNUTLS_APPLICATION_DATA, packet);
|
|
Packit |
549fdc |
if (ret != 0)
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret = _gnutls_recv_in_buffers(session, GNUTLS_APPLICATION_DATA, -1,
|
|
Packit |
549fdc |
session->internals.record_timeout_ms);
|
|
Packit |
549fdc |
if (ret < 0 && ret != GNUTLS_E_SESSION_EOF)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return get_packet_from_buffers(session, GNUTLS_APPLICATION_DATA, packet);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_record_send:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @data: contains the data to send
|
|
Packit |
549fdc |
* @data_size: is the length of the data
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function has the similar semantics with send(). The only
|
|
Packit |
549fdc |
* difference is that it accepts a GnuTLS session, and uses different
|
|
Packit |
549fdc |
* error codes.
|
|
Packit |
549fdc |
* Note that if the send buffer is full, send() will block this
|
|
Packit |
549fdc |
* function. See the send() documentation for more information.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* You can replace the default push function which is send(), by using
|
|
Packit |
549fdc |
* gnutls_transport_set_push_function().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* If the EINTR is returned by the internal push function
|
|
Packit |
549fdc |
* then %GNUTLS_E_INTERRUPTED will be returned. If
|
|
Packit |
549fdc |
* %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must
|
|
Packit |
549fdc |
* call this function again, with the exact same parameters; alternatively
|
|
Packit |
549fdc |
* you could provide a %NULL pointer for data, and 0 for
|
|
Packit |
549fdc |
* size. cf. gnutls_record_get_direction().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Note that in DTLS this function will return the %GNUTLS_E_LARGE_PACKET
|
|
Packit |
549fdc |
* error code if the send data exceed the data MTU value - as returned
|
|
Packit |
549fdc |
* by gnutls_dtls_get_data_mtu(). The errno value EMSGSIZE
|
|
Packit |
549fdc |
* also maps to %GNUTLS_E_LARGE_PACKET.
|
|
Packit |
549fdc |
* Note that since 3.2.13 this function can be called under cork in DTLS
|
|
Packit |
549fdc |
* mode, and will refuse to send data over the MTU size by returning
|
|
Packit |
549fdc |
* %GNUTLS_E_LARGE_PACKET.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: The number of bytes sent, or a negative error code. The
|
|
Packit |
549fdc |
* number of bytes sent might be less than @data_size. The maximum
|
|
Packit |
549fdc |
* number of bytes this function can send in a single call depends
|
|
Packit |
549fdc |
* on the negotiated maximum record size.
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
ssize_t
|
|
Packit |
549fdc |
gnutls_record_send(gnutls_session_t session, const void *data,
|
|
Packit |
549fdc |
size_t data_size)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
if (unlikely(!session->internals.initial_negotiation_completed)) {
|
|
Packit |
549fdc |
/* this is to protect buggy applications from sending unencrypted
|
|
Packit |
549fdc |
* data. We allow sending however, if we are in false start handshake
|
|
Packit |
549fdc |
* state. */
|
|
Packit |
549fdc |
if (session->internals.recv_state != RECV_STATE_FALSE_START)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_UNAVAILABLE_DURING_HANDSHAKE);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (session->internals.record_flush_mode == RECORD_FLUSH) {
|
|
Packit |
549fdc |
return _gnutls_send_int(session, GNUTLS_APPLICATION_DATA,
|
|
Packit |
549fdc |
-1, EPOCH_WRITE_CURRENT, data,
|
|
Packit |
549fdc |
data_size, MBUFFER_FLUSH);
|
|
Packit |
549fdc |
} else { /* GNUTLS_CORKED */
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (IS_DTLS(session)) {
|
|
Packit |
549fdc |
if (data_size + session->internals.record_presend_buffer.length >
|
|
Packit |
549fdc |
gnutls_dtls_get_data_mtu(session)) {
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_LARGE_PACKET);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
_gnutls_buffer_append_data(&session->internals.
|
|
Packit |
549fdc |
record_presend_buffer, data,
|
|
Packit |
549fdc |
data_size);
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
return gnutls_assert_val(ret);
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return data_size;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_record_cork:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* If called, gnutls_record_send() will no longer send any records.
|
|
Packit |
549fdc |
* Any sent records will be cached until gnutls_record_uncork() is called.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function is safe to use with DTLS after GnuTLS 3.3.0.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.1.9
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
void gnutls_record_cork(gnutls_session_t session)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
session->internals.record_flush_mode = RECORD_CORKED;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_record_uncork:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @flags: Could be zero or %GNUTLS_RECORD_WAIT
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This resets the effect of gnutls_record_cork(), and flushes any pending
|
|
Packit |
549fdc |
* data. If the %GNUTLS_RECORD_WAIT flag is specified then this
|
|
Packit |
549fdc |
* function will block until the data is sent or a fatal error
|
|
Packit |
549fdc |
* occurs (i.e., the function will retry on %GNUTLS_E_AGAIN and
|
|
Packit |
549fdc |
* %GNUTLS_E_INTERRUPTED).
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* If the flag %GNUTLS_RECORD_WAIT is not specified and the function
|
|
Packit |
549fdc |
* is interrupted then the %GNUTLS_E_AGAIN or %GNUTLS_E_INTERRUPTED
|
|
Packit |
549fdc |
* errors will be returned. To obtain the data left in the corked
|
|
Packit |
549fdc |
* buffer use gnutls_record_check_corked().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: On success the number of transmitted data is returned, or
|
|
Packit |
549fdc |
* otherwise a negative error code.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.1.9
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
int gnutls_record_uncork(gnutls_session_t session, unsigned int flags)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
int ret;
|
|
Packit |
549fdc |
ssize_t total = 0;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
if (session->internals.record_flush_mode == RECORD_FLUSH)
|
|
Packit |
549fdc |
return 0; /* nothing to be done */
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session->internals.record_flush_mode = RECORD_FLUSH;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
while (session->internals.record_presend_buffer.length > 0) {
|
|
Packit |
549fdc |
if (flags == GNUTLS_RECORD_WAIT) {
|
|
Packit |
549fdc |
do {
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
gnutls_record_send(session,
|
|
Packit |
549fdc |
session->internals.
|
|
Packit |
549fdc |
record_presend_buffer.
|
|
Packit |
549fdc |
data,
|
|
Packit |
549fdc |
session->internals.
|
|
Packit |
549fdc |
record_presend_buffer.
|
|
Packit |
549fdc |
length);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
while (ret < 0 && (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED));
|
|
Packit |
549fdc |
} else {
|
|
Packit |
549fdc |
ret =
|
|
Packit |
549fdc |
gnutls_record_send(session,
|
|
Packit |
549fdc |
session->internals.
|
|
Packit |
549fdc |
record_presend_buffer.data,
|
|
Packit |
549fdc |
session->internals.
|
|
Packit |
549fdc |
record_presend_buffer.
|
|
Packit |
549fdc |
length);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
if (ret < 0)
|
|
Packit |
549fdc |
goto fail;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
session->internals.record_presend_buffer.data += ret;
|
|
Packit |
549fdc |
session->internals.record_presend_buffer.length -= ret;
|
|
Packit |
549fdc |
total += ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return total;
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
fail:
|
|
Packit |
549fdc |
session->internals.record_flush_mode = RECORD_CORKED;
|
|
Packit |
549fdc |
return ret;
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_record_recv:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @data: the buffer that the data will be read into
|
|
Packit |
549fdc |
* @data_size: the number of requested bytes
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function has the similar semantics with recv(). The only
|
|
Packit |
549fdc |
* difference is that it accepts a GnuTLS session, and uses different
|
|
Packit |
549fdc |
* error codes.
|
|
Packit |
549fdc |
* In the special case that the peer requests a renegotiation, the
|
|
Packit |
549fdc |
* caller will receive an error code of %GNUTLS_E_REHANDSHAKE. In case
|
|
Packit |
549fdc |
* of a client, this message may be simply ignored, replied with an alert
|
|
Packit |
549fdc |
* %GNUTLS_A_NO_RENEGOTIATION, or replied with a new handshake,
|
|
Packit |
549fdc |
* depending on the client's will. A server receiving this error code
|
|
Packit |
549fdc |
* can only initiate a new handshake or terminate the session.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* If %EINTR is returned by the internal pull function (the default
|
|
Packit |
549fdc |
* is recv()) then %GNUTLS_E_INTERRUPTED will be returned. If
|
|
Packit |
549fdc |
* %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must
|
|
Packit |
549fdc |
* call this function again to get the data. See also
|
|
Packit |
549fdc |
* gnutls_record_get_direction().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: The number of bytes received and zero on EOF (for stream
|
|
Packit |
549fdc |
* connections). A negative error code is returned in case of an error.
|
|
Packit |
549fdc |
* The number of bytes received might be less than the requested @data_size.
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
ssize_t
|
|
Packit |
549fdc |
gnutls_record_recv(gnutls_session_t session, void *data, size_t data_size)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
if (unlikely(!session->internals.initial_negotiation_completed)) {
|
|
Packit |
549fdc |
/* this is to protect buggy applications from sending unencrypted
|
|
Packit |
549fdc |
* data. We allow sending however, if we are in false start handshake
|
|
Packit |
549fdc |
* state. */
|
|
Packit |
549fdc |
if (session->internals.recv_state != RECV_STATE_FALSE_START)
|
|
Packit |
549fdc |
return gnutls_assert_val(GNUTLS_E_UNAVAILABLE_DURING_HANDSHAKE);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA,
|
|
Packit |
549fdc |
data, data_size, NULL,
|
|
Packit |
549fdc |
session->internals.record_timeout_ms);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_record_recv_seq:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @data: the buffer that the data will be read into
|
|
Packit |
549fdc |
* @data_size: the number of requested bytes
|
|
Packit |
549fdc |
* @seq: is the packet's 64-bit sequence number. Should have space for 8 bytes.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function is the same as gnutls_record_recv(), except that
|
|
Packit |
549fdc |
* it returns in addition to data, the sequence number of the data.
|
|
Packit |
549fdc |
* This is useful in DTLS where record packets might be received
|
|
Packit |
549fdc |
* out-of-order. The returned 8-byte sequence number is an
|
|
Packit |
549fdc |
* integer in big-endian format and should be
|
|
Packit |
549fdc |
* treated as a unique message identification.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Returns: The number of bytes received and zero on EOF. A negative
|
|
Packit |
549fdc |
* error code is returned in case of an error. The number of bytes
|
|
Packit |
549fdc |
* received might be less than @data_size.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.0
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
ssize_t
|
|
Packit |
549fdc |
gnutls_record_recv_seq(gnutls_session_t session, void *data,
|
|
Packit |
549fdc |
size_t data_size, unsigned char *seq)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA,
|
|
Packit |
549fdc |
data, data_size, seq,
|
|
Packit |
549fdc |
session->internals.record_timeout_ms);
|
|
Packit |
549fdc |
}
|
|
Packit |
549fdc |
|
|
Packit |
549fdc |
/**
|
|
Packit |
549fdc |
* gnutls_record_set_timeout:
|
|
Packit |
549fdc |
* @session: is a #gnutls_session_t type.
|
|
Packit |
549fdc |
* @ms: is a timeout value in milliseconds
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function sets the receive timeout for the record layer
|
|
Packit |
549fdc |
* to the provided value. Use an @ms value of zero to disable
|
|
Packit |
549fdc |
* timeout (the default), or %GNUTLS_INDEFINITE_TIMEOUT, to
|
|
Packit |
549fdc |
* set an indefinite timeout.
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* This function requires to set a pull timeout callback. See
|
|
Packit |
549fdc |
* gnutls_transport_set_pull_timeout_function().
|
|
Packit |
549fdc |
*
|
|
Packit |
549fdc |
* Since: 3.1.7
|
|
Packit |
549fdc |
**/
|
|
Packit |
549fdc |
void gnutls_record_set_timeout(gnutls_session_t session, unsigned int ms)
|
|
Packit |
549fdc |
{
|
|
Packit |
549fdc |
session->internals.record_timeout_ms = ms;
|
|
Packit |
549fdc |
}
|