Blame src/microhttpd/connection_https.c

Packit 875988
/*
Packit 875988
     This file is part of libmicrohttpd
Packit 875988
     Copyright (C) 2007, 2008, 2010 Daniel Pittman and Christian Grothoff
Packit 875988
Packit 875988
     This library is free software; you can redistribute it and/or
Packit 875988
     modify it under the terms of the GNU Lesser General Public
Packit 875988
     License as published by the Free Software Foundation; either
Packit 875988
     version 2.1 of the License, or (at your option) any later version.
Packit 875988
Packit 875988
     This library is distributed in the hope that it will be useful,
Packit 875988
     but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 875988
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 875988
     Lesser General Public License for more details.
Packit 875988
Packit 875988
     You should have received a copy of the GNU Lesser General Public
Packit 875988
     License along with this library; if not, write to the Free Software
Packit 875988
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 875988
Packit 875988
*/
Packit 875988
Packit 875988
/**
Packit 875988
 * @file connection_https.c
Packit 875988
 * @brief  Methods for managing SSL/TLS connections. This file is only
Packit 875988
 *         compiled if ENABLE_HTTPS is set.
Packit 875988
 * @author Sagie Amir
Packit 875988
 * @author Christian Grothoff
Packit 875988
 */
Packit 875988
Packit 875988
#include "internal.h"
Packit 875988
#include "connection.h"
Packit 875988
#include "connection_https.h"
Packit 875988
#include "memorypool.h"
Packit 875988
#include "response.h"
Packit 875988
#include "mhd_mono_clock.h"
Packit 875988
#include <gnutls/gnutls.h>
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Callback for receiving data from the socket.
Packit 875988
 *
Packit 875988
 * @param connection the MHD_Connection structure
Packit 875988
 * @param other where to write received data to
Packit 875988
 * @param i maximum size of other (in bytes)
Packit 875988
 * @return positive value for number of bytes actually received or
Packit 875988
 *         negative value for error number MHD_ERR_xxx_
Packit 875988
 */
Packit 875988
static ssize_t
Packit 875988
recv_tls_adapter (struct MHD_Connection *connection,
Packit 875988
                  void *other,
Packit 875988
                  size_t i)
Packit 875988
{
Packit 875988
  ssize_t res;
Packit 875988
Packit 875988
  if (i > SSIZE_MAX)
Packit 875988
    i = SSIZE_MAX;
Packit 875988
Packit 875988
  res = gnutls_record_recv (connection->tls_session,
Packit 875988
                            other,
Packit 875988
                            i);
Packit 875988
  if ( (GNUTLS_E_AGAIN == res) ||
Packit 875988
       (GNUTLS_E_INTERRUPTED == res) )
Packit 875988
    {
Packit 875988
#ifdef EPOLL_SUPPORT
Packit 875988
      if (GNUTLS_E_AGAIN == res)
Packit 875988
        connection->epoll_state &= ~MHD_EPOLL_STATE_READ_READY;
Packit 875988
#endif
Packit 875988
      /* Any network errors means that buffer is empty. */
Packit 875988
      connection->tls_read_ready = false;
Packit 875988
      return MHD_ERR_AGAIN_;
Packit 875988
    }
Packit 875988
  if (res < 0)
Packit 875988
    {
Packit 875988
      /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication
Packit 875988
         disrupted); interpret as a hard error */
Packit 875988
      connection->tls_read_ready = false;
Packit 875988
      return MHD_ERR_NOTCONN_;
Packit 875988
    }
Packit 875988
Packit 875988
#ifdef EPOLL_SUPPORT
Packit 875988
  /* Unlike non-TLS connections, do not reset "read-ready" if
Packit 875988
   * received amount smaller than provided amount, as TLS
Packit 875988
   * connections may receive data by fixed-size chunks. */
Packit 875988
#endif /* EPOLL_SUPPORT */
Packit 875988
Packit 875988
  /* Check whether TLS buffers still have some unread data. */
Packit 875988
  connection->tls_read_ready = ( ((size_t)res == i) &&
Packit 875988
                                 (0 != gnutls_record_check_pending (connection->tls_session)) );
Packit 875988
  return res;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Callback for writing data to the socket.
Packit 875988
 *
Packit 875988
 * @param connection the MHD connection structure
Packit 875988
 * @param other data to write
Packit 875988
 * @param i number of bytes to write
Packit 875988
 * @return positive value for number of bytes actually sent or
Packit 875988
 *         negative value for error number MHD_ERR_xxx_
Packit 875988
 */
Packit 875988
static ssize_t
Packit 875988
send_tls_adapter (struct MHD_Connection *connection,
Packit 875988
                  const void *other,
Packit 875988
                  size_t i)
Packit 875988
{
Packit 875988
  ssize_t res;
Packit 875988
Packit 875988
  if (i > SSIZE_MAX)
Packit 875988
    i = SSIZE_MAX;
Packit 875988
Packit 875988
  res = gnutls_record_send (connection->tls_session,
Packit 875988
                            other,
Packit 875988
                            i);
Packit 875988
  if ( (GNUTLS_E_AGAIN == res) ||
Packit 875988
       (GNUTLS_E_INTERRUPTED == res) )
Packit 875988
    {
Packit 875988
#ifdef EPOLL_SUPPORT
Packit 875988
      if (GNUTLS_E_AGAIN == res)
Packit 875988
        connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
Packit 875988
#endif
Packit 875988
      return MHD_ERR_AGAIN_;
Packit 875988
    }
Packit 875988
  if (res < 0)
Packit 875988
    {
Packit 875988
      /* Likely 'GNUTLS_E_INVALID_SESSION' (client communication
Packit 875988
         disrupted); interpret as a hard error */
Packit 875988
      return MHD_ERR_NOTCONN_;
Packit 875988
    }
Packit 875988
#ifdef EPOLL_SUPPORT
Packit 875988
  /* Unlike non-TLS connections, do not reset "write-ready" if
Packit 875988
   * sent amount smaller than provided amount, as TLS
Packit 875988
   * connections may break data into smaller parts for sending. */
Packit 875988
#endif /* EPOLL_SUPPORT */
Packit 875988
  return res;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Give gnuTLS chance to work on the TLS handshake.
Packit 875988
 *
Packit 875988
 * @param connection connection to handshake on
Packit 875988
 * @return true if the handshake has completed successfully
Packit 875988
 *         and we should start to read/write data,
Packit 875988
 *         false is handshake in progress or in case
Packit 875988
 *         of error
Packit 875988
 */
Packit 875988
bool
Packit 875988
MHD_run_tls_handshake_ (struct MHD_Connection *connection)
Packit 875988
{
Packit 875988
  int ret;
Packit 875988
Packit 875988
  if ((MHD_TLS_CONN_INIT == connection->tls_state) ||
Packit 875988
      (MHD_TLS_CONN_HANDSHAKING == connection->tls_state))
Packit 875988
    {
Packit 875988
      ret = gnutls_handshake (connection->tls_session);
Packit 875988
      if (ret == GNUTLS_E_SUCCESS)
Packit 875988
	{
Packit 875988
	  /* set connection TLS state to enable HTTP processing */
Packit 875988
	  connection->tls_state = MHD_TLS_CONN_CONNECTED;
Packit 875988
	  MHD_update_last_activity_ (connection);
Packit 875988
	  return true;
Packit 875988
	}
Packit 875988
      if ( (GNUTLS_E_AGAIN == ret) ||
Packit 875988
	   (GNUTLS_E_INTERRUPTED == ret) )
Packit 875988
	{
Packit 875988
          connection->tls_state = MHD_TLS_CONN_HANDSHAKING;
Packit 875988
	  /* handshake not done */
Packit 875988
	  return false;
Packit 875988
	}
Packit 875988
      /* handshake failed */
Packit 875988
      connection->tls_state = MHD_TLS_CONN_TLS_FAILED;
Packit 875988
#ifdef HAVE_MESSAGES
Packit 875988
      MHD_DLOG (connection->daemon,
Packit 875988
		_("Error: received handshake message out of context\n"));
Packit 875988
#endif
Packit 875988
      MHD_connection_close_ (connection,
Packit 875988
                             MHD_REQUEST_TERMINATED_WITH_ERROR);
Packit 875988
      return false;
Packit 875988
    }
Packit 875988
  return true;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Set connection callback function to be used through out
Packit 875988
 * the processing of this secure connection.
Packit 875988
 *
Packit 875988
 * @param connection which callbacks should be modified
Packit 875988
 */
Packit 875988
void
Packit 875988
MHD_set_https_callbacks (struct MHD_Connection *connection)
Packit 875988
{
Packit 875988
  connection->recv_cls = &recv_tls_adapter;
Packit 875988
  connection->send_cls = &send_tls_adapter;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Initiate shutdown of TLS layer of connection.
Packit 875988
 *
Packit 875988
 * @param connection to use
Packit 875988
 * @return true if succeed, false otherwise.
Packit 875988
 */
Packit 875988
bool
Packit 875988
MHD_tls_connection_shutdown (struct MHD_Connection *connection)
Packit 875988
{
Packit 875988
  if (MHD_TLS_CONN_WR_CLOSED > connection->tls_state)
Packit 875988
    {
Packit 875988
      const int res =
Packit 875988
          gnutls_bye(connection->tls_session, GNUTLS_SHUT_WR);
Packit 875988
      if (GNUTLS_E_SUCCESS == res)
Packit 875988
        {
Packit 875988
          connection->tls_state = MHD_TLS_CONN_WR_CLOSED;
Packit 875988
          return true;
Packit 875988
        }
Packit 875988
      if ((GNUTLS_E_AGAIN == res) ||
Packit 875988
          (GNUTLS_E_INTERRUPTED == res))
Packit 875988
        {
Packit 875988
          connection->tls_state = MHD_TLS_CONN_WR_CLOSING;
Packit 875988
          return true;
Packit 875988
        }
Packit 875988
      else
Packit 875988
        connection->tls_state = MHD_TLS_CONN_TLS_FAILED;
Packit 875988
    }
Packit 875988
  return false;
Packit 875988
}
Packit 875988
Packit 875988
/* end of connection_https.c */