Beispiel #1
0
/* this function does a SSL/TLS (re-)handshake */
static CURLcode handshake(struct connectdata *conn,
                          int sockindex,
                          bool duringconnect,
                          bool nonblocking)
{
  struct SessionHandle *data = conn->data;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  gnutls_session session = conn->ssl[sockindex].session;
  curl_socket_t sockfd = conn->sock[sockindex];
  long timeout_ms;
  int rc;
  int what;

  for(;;) {
    /* check allowed time left */
    timeout_ms = Curl_timeleft(data, NULL, duringconnect);

    if(timeout_ms < 0) {
      /* no need to continue if time already is up */
      failf(data, "SSL connection timeout");
      return CURLE_OPERATION_TIMEDOUT;
    }

    /* if ssl is expecting something, check if it's available. */
    if(connssl->connecting_state == ssl_connect_2_reading
       || connssl->connecting_state == ssl_connect_2_writing) {

      curl_socket_t writefd = ssl_connect_2_writing==
        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
      curl_socket_t readfd = ssl_connect_2_reading==
        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;

      what = Curl_socket_ready(readfd, writefd,
                               nonblocking?0:
                               timeout_ms?timeout_ms:1000);
      if(what < 0) {
        /* fatal error */
        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
        return CURLE_SSL_CONNECT_ERROR;
      }
      else if(0 == what) {
        if(nonblocking)
          return CURLE_OK;
        else if(timeout_ms) {
          /* timeout */
          failf(data, "SSL connection timeout at %ld", timeout_ms);
          return CURLE_OPERATION_TIMEDOUT;
        }
      }
      /* socket is readable or writable */
    }

    rc = gnutls_handshake(session);

    if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
      connssl->connecting_state =
        gnutls_record_get_direction(session)?
        ssl_connect_2_writing:ssl_connect_2_reading;
      continue;
      if(nonblocking)
        return CURLE_OK;
    }
    else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
      const char *strerr = NULL;

      if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
        int alert = gnutls_alert_get(session);
        strerr = gnutls_alert_get_name(alert);
      }

      if(strerr == NULL)
        strerr = gnutls_strerror(rc);

      failf(data, "gnutls_handshake() warning: %s", strerr);
    }
    else if(rc < 0) {
      const char *strerr = NULL;

      if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
        int alert = gnutls_alert_get(session);
        strerr = gnutls_alert_get_name(alert);
      }

      if(strerr == NULL)
        strerr = gnutls_strerror(rc);

      failf(data, "gnutls_handshake() failed: %s", strerr);
      return CURLE_SSL_CONNECT_ERROR;
    }

    /* Reset our connect state machine */
    connssl->connecting_state = ssl_connect_1;
    return CURLE_OK;
  }
}
Beispiel #2
0
static apr_status_t gnutls_io_input_read(mgs_handle_t * ctxt,
        char *buf, apr_size_t * len) {
    apr_size_t wanted = *len;
    apr_size_t bytes = 0;
    int rc;

    *len = 0;

    /* If we have something leftover from last time, try that first. */
    if ((bytes = char_buffer_read(&ctxt->input_cbuf, buf, wanted))) {
        *len = bytes;
        if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
            /* We want to rollback this read. */
            if (ctxt->input_cbuf.length > 0) {
                ctxt->input_cbuf.value -= bytes;
                ctxt->input_cbuf.length += bytes;
            } else {
                char_buffer_write(&ctxt->input_cbuf, buf,
                        (int) bytes);
            }
            return APR_SUCCESS;
        }
        /* This could probably be *len == wanted, but be safe from stray
         * photons.
         */
        if (*len >= wanted) {
            return APR_SUCCESS;
        }
        if (ctxt->input_mode == AP_MODE_GETLINE) {
            if (memchr(buf, APR_ASCII_LF, *len)) {
                return APR_SUCCESS;
            }
        } else {
            /* Down to a nonblock pattern as we have some data already
             */
            ctxt->input_block = APR_NONBLOCK_READ;
        }
    }

    if (ctxt->session == NULL) {
        return APR_EGENERAL;
    }

    while (1) {

        rc = gnutls_record_recv(ctxt->session, buf + bytes,
                wanted - bytes);

        if (rc > 0) {
            *len += rc;
            if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
                /* We want to rollback this read. */
                char_buffer_write(&ctxt->input_cbuf, buf,
                        rc);
            }
            return ctxt->input_rc;
        } else if (rc == 0) {
            /* If EAGAIN, we will loop given a blocking read,
             * otherwise consider ourselves at EOF.
             */
            if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
                    || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
                /* Already read something, return APR_SUCCESS instead.
                 * On win32 in particular, but perhaps on other kernels,
                 * a blocking call isn't 'always' blocking.
                 */
                if (*len > 0) {
                    ctxt->input_rc = APR_SUCCESS;
                    break;
                }
                if (ctxt->input_block == APR_NONBLOCK_READ) {
                    break;
                }
            } else {
                if (*len > 0) {
                    ctxt->input_rc = APR_SUCCESS;
                } else {
                    ctxt->input_rc = APR_EOF;
                }
                break;
            }
        } else { /* (rc < 0) */

            if (rc == GNUTLS_E_REHANDSHAKE) {
                /* A client has asked for a new Hankshake. Currently, we don't do it */
                ap_log_error(APLOG_MARK, APLOG_INFO,
                        ctxt->input_rc,
                        ctxt->c->base_server,
                        "GnuTLS: Error reading data. Client Requested a New Handshake."
                        " (%d) '%s'", rc,
                        gnutls_strerror(rc));
            } else if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
                rc = gnutls_alert_get(ctxt->session);
                ap_log_error(APLOG_MARK, APLOG_INFO,
                        ctxt->input_rc,
                        ctxt->c->base_server,
                        "GnuTLS: Warning Alert From Client: "
                        " (%d) '%s'", rc,
                        gnutls_alert_get_name(rc));
            } else if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
                rc = gnutls_alert_get(ctxt->session);
                ap_log_error(APLOG_MARK, APLOG_INFO,
                        ctxt->input_rc,
                        ctxt->c->base_server,
                        "GnuTLS: Fatal Alert From Client: "
                        "(%d) '%s'", rc,
                        gnutls_alert_get_name(rc));
                ctxt->input_rc = APR_EGENERAL;
                break;
            } else {
                /* Some Other Error. Report it. Die. */
                if (gnutls_error_is_fatal(rc)) {
                    ap_log_error(APLOG_MARK,
                            APLOG_INFO,
                            ctxt->input_rc,
                            ctxt->c->base_server,
                            "GnuTLS: Error reading data. (%d) '%s'",
                            rc,
                            gnutls_strerror(rc));
                } else if (*len > 0) {
                    ctxt->input_rc = APR_SUCCESS;
                    break;
                }
            }

            if (ctxt->input_rc == APR_SUCCESS) {
                ctxt->input_rc = APR_EGENERAL;
            }
            break;
        }
    }
    return ctxt->input_rc;
}
Beispiel #3
0
/* This function will check if the received record type is
 * the one we actually expect.
 */
static int
record_check_type (gnutls_session_t session,
		   content_type_t recv_type, content_type_t type,
		   gnutls_handshake_description_t htype, opaque * data,
		   int data_size)
{

  int ret;

  if ((recv_type == type)
      && (type == GNUTLS_APPLICATION_DATA ||
	  type == GNUTLS_HANDSHAKE || type == GNUTLS_INNER_APPLICATION))
    {
      _gnutls_record_buffer_put (type, session, (void *) data, data_size);
    }
  else
    {
      switch (recv_type)
	{
	case GNUTLS_ALERT:

	  _gnutls_record_log
	    ("REC[%x]: Alert[%d|%d] - %s - was received\n", session,
	     data[0], data[1], gnutls_alert_get_name ((int) data[1]));

	  session->internals.last_alert = data[1];

	  /* if close notify is received and
	   * the alert is not fatal
	   */
	  if (data[1] == GNUTLS_A_CLOSE_NOTIFY && data[0] != GNUTLS_AL_FATAL)
	    {
	      /* If we have been expecting for an alert do 
	       */
	      session->internals.read_eof = 1;
	      return GNUTLS_E_INT_RET_0;	/* EOF */
	    }
	  else
	    {

	      /* if the alert is FATAL or WARNING
	       * return the apropriate message
	       */

	      gnutls_assert ();
	      ret = GNUTLS_E_WARNING_ALERT_RECEIVED;
	      if (data[0] == GNUTLS_AL_FATAL)
		{
		  session_unresumable (session);
		  session_invalidate (session);
		  ret = GNUTLS_E_FATAL_ALERT_RECEIVED;
		}

	      return ret;
	    }
	  break;

	case GNUTLS_CHANGE_CIPHER_SPEC:
	  /* this packet is now handled in the recv_int()
	   * function
	   */
	  gnutls_assert ();

	  return GNUTLS_E_UNEXPECTED_PACKET;

	case GNUTLS_APPLICATION_DATA:
	  /* even if data is unexpected put it into the buffer */
	  if ((ret =
	       _gnutls_record_buffer_put (recv_type, session,
					  (void *) data, data_size)) < 0)
	    {
	      gnutls_assert ();
	      return ret;
	    }

	  /* the got_application data is only returned
	   * if expecting client hello (for rehandshake
	   * reasons). Otherwise it is an unexpected packet
	   */
	  if (type == GNUTLS_ALERT || (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO
				       && type == GNUTLS_HANDSHAKE))
	    return GNUTLS_E_GOT_APPLICATION_DATA;
	  else
	    {
	      gnutls_assert ();
	      return GNUTLS_E_UNEXPECTED_PACKET;
	    }

	  break;
	case GNUTLS_HANDSHAKE:
	  /* This is legal if HELLO_REQUEST is received - and we are a client.
	   * If we are a server, a client may initiate a renegotiation at any time.
	   */
	  if (session->security_parameters.entity == GNUTLS_SERVER)
	    {
	      gnutls_assert ();
	      return GNUTLS_E_REHANDSHAKE;
	    }

	  /* If we are already in a handshake then a Hello
	   * Request is illegal. But here we don't really care
	   * since this message will never make it up here.
	   */

	  /* So we accept it */
	  return _gnutls_recv_hello_request (session, data, data_size);

	  break;
	case GNUTLS_INNER_APPLICATION:
	  /* even if data is unexpected put it into the buffer */
	  if ((ret = _gnutls_record_buffer_put (recv_type, session,
						(void *) data,
						data_size)) < 0)
	    {
	      gnutls_assert ();
	      return ret;
	    }
	  gnutls_assert ();
	  return GNUTLS_E_UNEXPECTED_PACKET;
	  break;
	default:

	  _gnutls_record_log
	    ("REC[%x]: Received Unknown packet %d expecting %d\n",
	     session, recv_type, type);

	  gnutls_assert ();
	  return GNUTLS_E_INTERNAL_ERROR;
	}
    }

  return 0;

}