mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
362 lines
12 KiB
C
362 lines
12 KiB
C
/*
|
|
This file is part of libmicrohttpd
|
|
(C) 2007, 2008 Daniel Pittman and Christian Grothoff
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
/**
|
|
* @file connection.c
|
|
* @brief Methods for managing SSL/TLS connections. This file is only
|
|
* compiled if ENABLE_HTTPS is set.
|
|
* @author Sagie Amir
|
|
* @author Christian Grothoff
|
|
*/
|
|
|
|
#include "internal.h"
|
|
#include "connection.h"
|
|
#include "memorypool.h"
|
|
#include "response.h"
|
|
#include "reason_phrase.h"
|
|
|
|
/* get opaque type */
|
|
#include "gnutls_int.h"
|
|
#include "gnutls_record.h"
|
|
|
|
/* TODO #include rm "gnutls_errors.h" */
|
|
#include "gnutls_errors.h"
|
|
|
|
/**
|
|
* Obtain information about the given connection.
|
|
*
|
|
* @param connection what connection to get information about
|
|
* @param infoType what information is desired?
|
|
* @param ... depends on infoType
|
|
* @return NULL if this information is not available
|
|
* (or if the infoType is unknown)
|
|
*/
|
|
const union MHD_ConnectionInfo *
|
|
MHD_get_connection_info (struct MHD_Connection *connection,
|
|
enum MHD_ConnectionInfoType infoType, ...)
|
|
{
|
|
if (connection->tls_session == NULL)
|
|
return NULL;
|
|
switch (infoType)
|
|
{
|
|
#if HTTPS_SUPPORT
|
|
case MHD_CONNECTION_INFO_CIPHER_ALGO:
|
|
return (const union MHD_ConnectionInfo *) &connection->
|
|
tls_session->security_parameters.read_bulk_cipher_algorithm;
|
|
case MHD_CONNECTION_INFO_KX_ALGO:
|
|
return (const union MHD_ConnectionInfo *) &connection->
|
|
tls_session->security_parameters.kx_algorithm;
|
|
case MHD_CONNECTION_INFO_CREDENTIALS_TYPE:
|
|
return (const union MHD_ConnectionInfo *) &connection->
|
|
tls_session->key->cred->algorithm;
|
|
case MHD_CONNECTION_INFO_MAC_ALGO:
|
|
return (const union MHD_ConnectionInfo *) &connection->
|
|
tls_session->security_parameters.read_mac_algorithm;
|
|
case MHD_CONNECTION_INFO_COMPRESSION_METHOD:
|
|
return (const union MHD_ConnectionInfo *) &connection->
|
|
tls_session->security_parameters.read_compression_algorithm;
|
|
case MHD_CONNECTION_INFO_PROTOCOL:
|
|
return (const union MHD_ConnectionInfo *) &connection->
|
|
tls_session->security_parameters.version;
|
|
case MHD_CONNECTION_INFO_CERT_TYPE:
|
|
return (const union MHD_ConnectionInfo *) &connection->
|
|
tls_session->security_parameters.cert_type;
|
|
#endif
|
|
default:
|
|
return NULL;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* This function is called once a secure connection has been marked
|
|
* for closure.
|
|
*
|
|
* NOTE: Some code duplication with connection_close_error
|
|
* in connection.c
|
|
*
|
|
* @param connection: the connection to close
|
|
* @param termination_code: the termination code with which the notify completed callback function is called.
|
|
*/
|
|
static void
|
|
MHD_tls_connection_close (struct MHD_Connection *connection,
|
|
enum MHD_RequestTerminationCode
|
|
termination_code)
|
|
{
|
|
MHD_gnutls_bye (connection->tls_session, GNUTLS_SHUT_WR);
|
|
connection->tls_session->internals.read_eof = 1;
|
|
SHUTDOWN (connection->socket_fd, SHUT_RDWR);
|
|
CLOSE (connection->socket_fd);
|
|
connection->socket_fd = -1;
|
|
connection->state = MHD_CONNECTION_CLOSED;
|
|
if (connection->daemon->notify_completed != NULL)
|
|
connection->daemon->notify_completed (connection->daemon->
|
|
notify_completed_cls, connection,
|
|
&connection->client_context,
|
|
termination_code);
|
|
}
|
|
|
|
/**
|
|
* This function was created to handle per-connection processing that
|
|
* has to happen even if the socket cannot be read or written to. All
|
|
* implementations (multithreaded, external select, internal select)
|
|
* call this function.
|
|
*
|
|
* @param connection being handled
|
|
* @return MHD_YES if we should continue to process the
|
|
* connection (not dead yet), MHD_NO if it died
|
|
*/
|
|
static int
|
|
MHD_tls_connection_handle_idle (struct MHD_Connection *connection)
|
|
{
|
|
unsigned int timeout;
|
|
|
|
#if DEBUG_STATES
|
|
MHD_DLOG (connection->daemon, "%s: state: %s\n",
|
|
__FUNCTION__, MHD_state_to_string (connection->state));
|
|
#endif
|
|
timeout = connection->daemon->connection_timeout;
|
|
if ((connection->socket_fd != -1) && (timeout != 0)
|
|
&& (time (NULL) - timeout > connection->last_activity))
|
|
{
|
|
MHD_tls_connection_close (connection,
|
|
MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
|
|
return MHD_NO;
|
|
}
|
|
switch (connection->state)
|
|
{
|
|
/* on newly created connections we might reach here before any reply has been received */
|
|
case MHD_TLS_CONNECTION_INIT:
|
|
return MHD_YES;
|
|
/* close connection if necessary */
|
|
case MHD_CONNECTION_CLOSED:
|
|
if (connection->socket_fd != -1)
|
|
MHD_tls_connection_close (connection,
|
|
MHD_REQUEST_TERMINATED_COMPLETED_OK);
|
|
return MHD_NO;
|
|
case MHD_TLS_HANDSHAKE_FAILED:
|
|
MHD_tls_connection_close (connection,
|
|
MHD_REQUEST_TERMINATED_WITH_ERROR);
|
|
return MHD_NO;
|
|
/* some HTTP state */
|
|
default:
|
|
return MHD_connection_handle_idle (connection);
|
|
}
|
|
return MHD_YES;
|
|
}
|
|
|
|
/**
|
|
* This function handles a particular SSL/TLS connection when
|
|
* it has been determined that there is data to be read off a
|
|
* socket. Message processing is done by message type which is
|
|
* determined by peeking into the first message type byte of the
|
|
* stream.
|
|
*
|
|
* Error message handling: all fatal level messages cause the
|
|
* connection to be terminated.
|
|
*
|
|
* Application data is forwarded to the underlying daemon for
|
|
* processing.
|
|
*
|
|
* @param connection : the source connection
|
|
* @return MHD_YES if we should continue to process the
|
|
* connection (not dead yet), MHD_NO if it died
|
|
*/
|
|
static int
|
|
MHD_tls_connection_handle_read (struct MHD_Connection *connection)
|
|
{
|
|
int ret;
|
|
unsigned char msg_type;
|
|
|
|
connection->last_activity = time (NULL);
|
|
if (connection->state == MHD_CONNECTION_CLOSED ||
|
|
connection->state == MHD_TLS_HANDSHAKE_FAILED)
|
|
return MHD_NO;
|
|
|
|
#if DEBUG_STATES
|
|
MHD_DLOG (connection->daemon, "%s: state: %s\n",
|
|
__FUNCTION__, MHD_state_to_string (connection->state));
|
|
#endif
|
|
|
|
/* discover content type */
|
|
if (RECV (connection->socket_fd, &msg_type, 1, MSG_PEEK) == -1)
|
|
{
|
|
#if HAVE_MESSAGES
|
|
MHD_DLOG (connection->daemon, "Failed to peek into TLS content type\n");
|
|
#endif
|
|
return MHD_NO;
|
|
}
|
|
|
|
switch (msg_type)
|
|
{
|
|
/* check for handshake messages first */
|
|
case GNUTLS_HANDSHAKE:
|
|
/* negotiate handshake only while in INIT & HELLO_REQUEST states */
|
|
if (connection->state == MHD_TLS_CONNECTION_INIT ||
|
|
connection->state == MHD_TLS_HELLO_REQUEST)
|
|
{
|
|
ret = MHD_gnutls_handshake (connection->tls_session);
|
|
if (ret == 0)
|
|
{
|
|
/* set connection state to enable HTTP processing */
|
|
connection->state = MHD_CONNECTION_INIT;
|
|
break;
|
|
}
|
|
/* set connection as closed */
|
|
else
|
|
{
|
|
#if HAVE_MESSAGES
|
|
MHD_DLOG (connection->daemon,
|
|
"Error: Handshake has failed (%d)\n", ret);
|
|
#endif
|
|
connection->state = MHD_TLS_HANDSHAKE_FAILED;
|
|
return MHD_NO;
|
|
}
|
|
}
|
|
/* a handshake message has been received out of bound */
|
|
else
|
|
{
|
|
#if HAVE_MESSAGES
|
|
MHD_DLOG (connection->daemon,
|
|
"Error: received handshake message out of context\n");
|
|
#endif
|
|
MHD_tls_connection_close (connection,
|
|
MHD_REQUEST_TERMINATED_WITH_ERROR);
|
|
return MHD_NO;
|
|
}
|
|
|
|
/* ignore any out of bound change chiper spec messages */
|
|
case GNUTLS_CHANGE_CIPHER_SPEC:
|
|
MHD_tls_connection_close (connection,
|
|
MHD_REQUEST_TERMINATED_WITH_ERROR);
|
|
return MHD_NO;
|
|
|
|
case GNUTLS_ALERT:
|
|
/*
|
|
* this call of mhd_gtls_recv_int expects 0 bytes read.
|
|
* done to decrypt alert message
|
|
*/
|
|
mhd_gtls_recv_int (connection->tls_session, GNUTLS_ALERT,
|
|
GNUTLS_HANDSHAKE_FINISHED, 0, 0);
|
|
|
|
/* CLOSE_NOTIFY */
|
|
if (connection->tls_session->internals.last_alert ==
|
|
GNUTLS_A_CLOSE_NOTIFY)
|
|
{
|
|
connection->state = MHD_CONNECTION_CLOSED;
|
|
return MHD_YES;
|
|
}
|
|
/* non FATAL or WARNING */
|
|
else if (connection->tls_session->internals.last_alert_level !=
|
|
GNUTLS_AL_FATAL)
|
|
{
|
|
#if HAVE_MESSAGES
|
|
MHD_DLOG (connection->daemon,
|
|
"Received TLS alert: %s\n",
|
|
MHD_gnutls_alert_get_name ((int) connection->tls_session->
|
|
internals.last_alert));
|
|
#endif
|
|
return MHD_YES;
|
|
}
|
|
/* FATAL */
|
|
else if (connection->tls_session->internals.last_alert_level ==
|
|
GNUTLS_AL_FATAL)
|
|
{
|
|
MHD_tls_connection_close (connection,
|
|
MHD_REQUEST_TERMINATED_WITH_ERROR);
|
|
return MHD_NO;
|
|
}
|
|
/* this should never execute */
|
|
else
|
|
{
|
|
#if HAVE_MESSAGES
|
|
MHD_DLOG (connection->daemon,
|
|
"Received unrecognized alert: %d\n",
|
|
connection->tls_session->internals.last_alert);
|
|
#endif
|
|
return MHD_NO;
|
|
}
|
|
|
|
|
|
/* forward application level content to MHD */
|
|
case GNUTLS_APPLICATION_DATA:
|
|
return MHD_connection_handle_read (connection);
|
|
|
|
case GNUTLS_INNER_APPLICATION:
|
|
break;
|
|
default:
|
|
#if HAVE_MESSAGES
|
|
MHD_DLOG (connection->daemon,
|
|
"Error: unrecognized TLS message type: %d, connection state: %s. l: %d, f: %s\n",
|
|
msg_type, MHD_state_to_string(connection->state), __LINE__, __FUNCTION__);
|
|
#endif
|
|
/* close connection upon reception of unrecognized message type */
|
|
MHD_tls_connection_close (connection,
|
|
MHD_REQUEST_TERMINATED_WITH_ERROR);
|
|
return MHD_NO;
|
|
}
|
|
|
|
return MHD_YES;
|
|
}
|
|
|
|
/**
|
|
* This function was created to handle writes to sockets when it has
|
|
* been determined that the socket can be written to. This function
|
|
* will forward all write requests to the underlying daemon unless
|
|
* the connection has been marked for closing.
|
|
*
|
|
* @return MHD_connection_handle_write() if we should continue to
|
|
* process the connection (not dead yet), MHD_NO if it died
|
|
*/
|
|
static int
|
|
MHD_tls_connection_handle_write (struct MHD_Connection *connection)
|
|
{
|
|
connection->last_activity = time (NULL);
|
|
|
|
#if DEBUG_STATES
|
|
MHD_DLOG (connection->daemon, "%s: state: %s\n",
|
|
__FUNCTION__, MHD_state_to_string (connection->state));
|
|
#endif
|
|
|
|
switch (connection->state)
|
|
{
|
|
case MHD_CONNECTION_CLOSED:
|
|
case MHD_TLS_HANDSHAKE_FAILED:
|
|
return MHD_NO;
|
|
/* some HTTP connection state */
|
|
default:
|
|
return MHD_connection_handle_write (connection);
|
|
}
|
|
return MHD_NO;
|
|
}
|
|
|
|
/**
|
|
* Set connection callback function to be used through out
|
|
* the processing of this secure connection.
|
|
*/
|
|
void
|
|
MHD_set_https_calbacks (struct MHD_Connection *connection)
|
|
{
|
|
connection->read_handler = &MHD_tls_connection_handle_read;
|
|
connection->write_handler = &MHD_tls_connection_handle_write;
|
|
connection->idle_handler = &MHD_tls_connection_handle_idle;
|
|
}
|
|
|
|
/* end of connection_https.c */
|