Esempio n. 1
0
/**
 * Get the username and password from the basic authorization header sent by the client
 *
 * @param connection The MHD connection structure
 * @param password a pointer for the password
 * @return NULL if no username could be found, a pointer
 * 			to the username if found
 * @ingroup authentication
 */
char *
MHD_basic_auth_get_username_password (struct MHD_Connection *connection,
				      char** password)
{
  const char *header;
  char *decode;
  const char *separator;
  char *user;

  if ( (NULL == (header = MHD_lookup_connection_value (connection,
						       MHD_HEADER_KIND,
						       MHD_HTTP_HEADER_AUTHORIZATION))) ||
       (0 != strncmp (header,
                      _BASIC_BASE,
                      strlen (_BASIC_BASE))) )
    return NULL;
  header += strlen (_BASIC_BASE);
  if (NULL == (decode = BASE64Decode (header)))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (connection->daemon,
		_("Error decoding basic authentication\n"));
#endif
      return NULL;
    }
  /* Find user:password pattern */
  if (NULL == (separator = strchr (decode,
                                   ':')))
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG(connection->daemon,
	       _("Basic authentication doesn't contain ':' separator\n"));
#endif
      free (decode);
      return NULL;
    }
  if (NULL == (user = strdup (decode)))
    {
      free (decode);
      return NULL;
    }
  user[separator - decode] = '\0'; /* cut off at ':' */
  if (NULL != password)
    {
      *password = strdup (separator + 1);
      if (NULL == *password)
	{
#ifdef HAVE_MESSAGES
	  MHD_DLOG(connection->daemon,
		   _("Failed to allocate memory for password\n"));
#endif
	  free (decode);
	  free (user);
	  return NULL;
	}
    }
  free (decode);
  return user;
}
/**
 * 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;
    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;

  connection->last_activity = time (NULL);
  if (connection->state == MHD_TLS_CONNECTION_INIT)
    {
      ret = gnutls_handshake (connection->tls_session);
      if (ret == GNUTLS_E_SUCCESS) 
	{
	  /* set connection state to enable HTTP processing */
	  connection->state = MHD_CONNECTION_INIT;
	  return MHD_YES;	  
	}
      if ( (ret == GNUTLS_E_AGAIN) || 
	   (ret == GNUTLS_E_INTERRUPTED) )
	{
	  /* handshake not done */
	  return MHD_YES;
	}
      /* handshake failed */
#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;
    }
  return MHD_connection_handle_read (connection);
}
Esempio n. 4
0
/**
 * Give gnuTLS chance to work on the TLS handshake.
 *
 * @param connection connection to handshake on
 * @return #MHD_YES on error or if the handshake is progressing
 *         #MHD_NO if the handshake has completed successfully
 *         and we should start to read/write data
 */
static int
run_tls_handshake (struct MHD_Connection *connection)
{
  int ret;

  connection->last_activity = MHD_monotonic_sec_counter ();
  if (MHD_TLS_CONNECTION_INIT == connection->state)
    {
      ret = gnutls_handshake (connection->tls_session);
      if (ret == GNUTLS_E_SUCCESS)
	{
	  /* set connection state to enable HTTP processing */
	  connection->state = MHD_CONNECTION_INIT;
	  return MHD_YES;
	}
      if ( (GNUTLS_E_AGAIN == ret) ||
	   (GNUTLS_E_INTERRUPTED == ret) )
	{
	  /* handshake not done */
	  return MHD_YES;
	}
      /* handshake failed */
#ifdef HAVE_MESSAGES
      MHD_DLOG (connection->daemon,
		_("Error: received handshake message out of context\n"));
#endif
      MHD_connection_close_ (connection,
                             MHD_REQUEST_TERMINATED_WITH_ERROR);
      return MHD_YES;
    }
  return MHD_NO;
}
Esempio n. 5
0
/**
 * 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->connection_timeout;
  if ( (timeout != 0) && (time (NULL) - timeout > connection->last_activity))
    MHD_connection_close (connection,
			  MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
  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:
      gnutls_bye (connection->tls_session, GNUTLS_SHUT_RDWR);
      return MHD_connection_handle_idle (connection);
    default:
      if ( (0 != gnutls_record_check_pending (connection->tls_session)) &&
	   (MHD_YES != MHD_tls_connection_handle_read (connection)) )
	return MHD_YES;
      return MHD_connection_handle_idle (connection);
    }
  return MHD_YES;
}
Esempio n. 6
0
/**
 * Queues a response to request basic authentication from the client.
 * The given response object is expected to include the payload for
 * the response; the "WWW-Authenticate" header will be added and the
 * response queued with the 'UNAUTHORIZED' status code.
 *
 * @param connection The MHD connection structure
 * @param realm the realm presented to the client
 * @param response response object to modify and queue
 * @return #MHD_YES on success, #MHD_NO otherwise
 * @ingroup authentication
 */
int
MHD_queue_basic_auth_fail_response (struct MHD_Connection *connection,
				    const char *realm,
				    struct MHD_Response *response)
{
  int ret;
  int res;
  size_t hlen = strlen(realm) + strlen("Basic realm=\"\"") + 1;
  char *header;

  header = (char *) malloc(hlen);
  if (NULL == header)
  {
#ifdef HAVE_MESSAGES
    MHD_DLOG(connection->daemon,
		   "Failed to allocate memory for auth header\n");
#endif /* HAVE_MESSAGES */
    return MHD_NO;
  }
  res = MHD_snprintf_ (header,
                       hlen,
                       "Basic realm=\"%s\"",
                       realm);
  if (res > 0 && (size_t)res < hlen)
    ret = MHD_add_response_header (response,
                                   MHD_HTTP_HEADER_WWW_AUTHENTICATE,
                                   header);
  else
    ret = MHD_NO;

  free(header);
  if (MHD_YES == ret)
    ret = MHD_queue_response (connection,
			      MHD_HTTP_UNAUTHORIZED,
			      response);
  else
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (connection->daemon,
                _("Failed to add Basic auth header\n"));
#endif /* HAVE_MESSAGES */
    }
  return ret;
}
Esempio n. 7
0
/**
 * Resume handling of network data for suspended request.  It is
 * safe to resume a suspended request at any time.  Calling this
 * function on a request that was not previously suspended will
 * result in undefined behavior.
 *
 * If you are using this function in ``external'' select mode, you must
 * make sure to run #MHD_run() afterwards (before again calling
 * #MHD_get_fdset(), as otherwise the change may not be reflected in
 * the set returned by #MHD_get_fdset() and you may end up with a
 * request that is stuck until the next network activity.
 *
 * @param request the request to resume
 */
void
MHD_request_resume (struct MHD_Request *request)
{
  struct MHD_Daemon *daemon = request->daemon;

  if (daemon->disallow_suspend_resume)
    MHD_PANIC (_("Cannot resume connections without enabling MHD_ALLOW_SUSPEND_RESUME!\n"));
  MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
  request->connection->resuming = true;
  daemon->resuming = true;
  MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
  if ( (MHD_ITC_IS_VALID_(daemon->itc)) &&
       (! MHD_itc_activate_ (daemon->itc,
			     "r")) )
    {
#ifdef HAVE_MESSAGES
      MHD_DLOG (daemon,
		MHD_SC_ITC_USE_FAILED,
                _("Failed to signal resume via inter-thread communication channel."));
#endif
    }
}
Esempio n. 8
0
/**
 * 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,
            _("In function %s handling connection at state: %s\n"),
            __FUNCTION__,
            MHD_state_to_string (connection->state));
#endif
  timeout = connection->connection_timeout;
  if ( (timeout != 0) &&
       (timeout <= (MHD_monotonic_sec_counter() - connection->last_activity)))
    MHD_connection_close_ (connection,
                           MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
  switch (connection->state)
    {
      /* on newly created connections we might reach here before any reply has been received */
    case MHD_TLS_CONNECTION_INIT:
      break;
      /* close connection if necessary */
    case MHD_CONNECTION_CLOSED:
      return MHD_connection_handle_idle (connection);
    default:
      if ( (0 != gnutls_record_check_pending (connection->tls_session)) &&
	   (MHD_YES != MHD_tls_connection_handle_read (connection)) )
	return MHD_YES;
      return MHD_connection_handle_idle (connection);
    }
#ifdef EPOLL_SUPPORT
  return MHD_connection_epoll_update_ (connection);
#else
  return MHD_YES;
#endif
}
Esempio n. 9
0
/**
 * Run through the suspended connections and move any that are no
 * longer suspended back to the active state.
 *
 * @remark To be called only from thread that process
 * daemon's select()/poll()/etc.
 *
 * @param daemon daemon context
 * @return true if a connection was actually resumed
 */
bool
MHD_resume_suspended_connections_ (struct MHD_Daemon *daemon)
/* FIXME: rename connections -> requests? */
{
  struct MHD_Connection *pos;
  struct MHD_Connection *prev = NULL;
  bool ret;
  const bool used_thr_p_c = (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode);

  mhd_assert (NULL == daemon->worker_pool);
  ret = false;
  MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);

  if (daemon->resuming)
    {
      prev = daemon->suspended_connections_tail;
      /* During shutdown check for resuming is forced. */
      mhd_assert((NULL != prev) || (daemon->shutdown));
    }

  daemon->resuming = false;

  while (NULL != (pos = prev))
    {
#ifdef UPGRADE_SUPPORT
      struct MHD_UpgradeResponseHandle * const urh = pos->request.urh;
#else  /* ! UPGRADE_SUPPORT */
      static const void * const urh = NULL;
#endif /* ! UPGRADE_SUPPORT */
      prev = pos->prev;
      if ( (! pos->resuming)
#ifdef UPGRADE_SUPPORT
          || ( (NULL != urh) &&
               ( (! urh->was_closed) ||
                 (! urh->clean_ready) ) )
#endif /* UPGRADE_SUPPORT */
         )
        continue;
      ret = true;
      mhd_assert (pos->suspended);
      DLL_remove (daemon->suspended_connections_head,
                  daemon->suspended_connections_tail,
                  pos);
      pos->suspended = false;
      if (NULL == urh)
        {
          DLL_insert (daemon->connections_head,
                      daemon->connections_tail,
                      pos);
          if (! used_thr_p_c)
            {
              /* Reset timeout timer on resume. */
              if (0 != pos->connection_timeout)
                pos->last_activity = MHD_monotonic_sec_counter();

              if (pos->connection_timeout == daemon->connection_default_timeout)
                XDLL_insert (daemon->normal_timeout_head,
                             daemon->normal_timeout_tail,
                             pos);
              else
                XDLL_insert (daemon->manual_timeout_head,
                             daemon->manual_timeout_tail,
                             pos);
            }
#ifdef EPOLL_SUPPORT
          if (MHD_ELS_EPOLL == daemon->event_loop_syscall)
            {
              if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
                MHD_PANIC ("Resumed connection was already in EREADY set\n");
              /* we always mark resumed connections as ready, as we
                 might have missed the edge poll event during suspension */
              EDLL_insert (daemon->eready_head,
                           daemon->eready_tail,
                           pos);
              pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL | \
                  MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY;
              pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED;
            }
#endif
        }
#ifdef UPGRADE_SUPPORT
      else
        {
          struct MHD_Response *response = pos->request.response;

          /* Data forwarding was finished (for TLS connections) AND
           * application was closed upgraded connection.
           * Insert connection into cleanup list. */
          if ( (NULL != response) &&
               (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) &&
               (NULL != response->termination_cb) )
            response->termination_cb (response->termination_cb_cls,
                                      MHD_REQUEST_TERMINATED_COMPLETED_OK,
                                      &pos->request.client_context);
          DLL_insert (daemon->cleanup_head,
                      daemon->cleanup_tail,
                      pos);

        }
#endif /* UPGRADE_SUPPORT */
      pos->resuming = false;
    }
  MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
  if ( (used_thr_p_c) &&
       (ret) )
    { /* Wake up suspended connections. */
      if (! MHD_itc_activate_(daemon->itc,
                              "w"))
	{
#ifdef HAVE_MESSAGES
	  MHD_DLOG (daemon,
		    MHD_SC_ITC_USE_FAILED,
		    _("Failed to signal resume of connection via inter-thread communication channel."));
#endif
	}
    }
  return ret;
}