Пример #1
0
/**
 * gnutls_ia_verify_endphase:
 * @session: is a #gnutls_session_t structure.
 * @checksum: 12-byte checksum data, received from gnutls_ia_recv().
 *
 * Verify TLS/IA end phase checksum data.  If verification fails, the
 * %GNUTLS_A_INNER_APPLICATION_VERIFICATION alert is sent to the other
 * sie.
 *
 * This function is called when gnutls_ia_recv() return
 * %GNUTLS_E_WARNING_IA_IPHF_RECEIVED or
 * %GNUTLS_E_WARNING_IA_FPHF_RECEIVED.
 *
 * Return value: Return 0 on successful verification, or an error
 * code.  If the checksum verification of the end phase message fails,
 * %GNUTLS_E_IA_VERIFY_FAILED is returned.
 **/
int
gnutls_ia_verify_endphase (gnutls_session_t session, const char *checksum)
{
  char local_checksum[CHECKSUM_SIZE];
  int client = session->security_parameters.entity == GNUTLS_CLIENT;
  const char *label = client ? server_finished_label : client_finished_label;
  int size_of_label = client ? sizeof (server_finished_label) :
    sizeof (client_finished_label);
  int ret;

  ret = _gnutls_PRF (session, session->security_parameters.inner_secret,
		     GNUTLS_MASTER_SIZE,
		     label, size_of_label - 1,
		     "", 0, CHECKSUM_SIZE, local_checksum);
  if (ret < 0)
    {
      gnutls_assert ();
      return ret;
    }

  if (memcmp (local_checksum, checksum, CHECKSUM_SIZE) != 0)
    {
      ret = gnutls_alert_send (session, GNUTLS_AL_FATAL,
			       GNUTLS_A_INNER_APPLICATION_VERIFICATION);
      if (ret < 0)
	{
	  gnutls_assert ();
	  return ret;
	}

      return GNUTLS_E_IA_VERIFY_FAILED;
    }

  return 0;
}
Пример #2
0
/**
 * gnutls_bye:
 * @session: is a #gnutls_session_t structure.
 * @how: is an integer
 *
 * Terminates the current TLS/SSL connection. The connection should
 * have been initiated using gnutls_handshake().  @how should be one
 * of %GNUTLS_SHUT_RDWR, %GNUTLS_SHUT_WR.
 *
 * In case of %GNUTLS_SHUT_RDWR the TLS session gets
 * terminated and further receives and sends will be disallowed.  If
 * the return value is zero you may continue using the underlying
 * transport layer. %GNUTLS_SHUT_RDWR sends an alert containing a close
 * request and waits for the peer to reply with the same message.
 *
 * In case of %GNUTLS_SHUT_WR the TLS session gets terminated
 * and further sends will be disallowed. In order to reuse the
 * connection you should wait for an EOF from the peer.
 * %GNUTLS_SHUT_WR sends an alert containing a close request.
 *
 * Note that not all implementations will properly terminate a TLS
 * connection.  Some of them, usually for performance reasons, will
 * terminate only the underlying transport layer, and thus not
 * distinguishing between a malicious party prematurely terminating 
 * the connection and normal termination. 
 *
 * This function may also return %GNUTLS_E_AGAIN or
 * %GNUTLS_E_INTERRUPTED; cf.  gnutls_record_get_direction().
 *
 * Returns: %GNUTLS_E_SUCCESS on success, or an error code, see
 *   function documentation for entire semantics.
 **/
int
gnutls_bye (gnutls_session_t session, gnutls_close_request_t how)
{
  int ret = 0;

  switch (STATE)
    {
    case STATE0:
    case STATE60:
      ret = _gnutls_io_write_flush (session);
      STATE = STATE60;
      if (ret < 0)
        {
          gnutls_assert ();
          return ret;
        }

    case STATE61:
      ret =
        gnutls_alert_send (session, GNUTLS_AL_WARNING, GNUTLS_A_CLOSE_NOTIFY);
      STATE = STATE61;
      if (ret < 0)
        {
          gnutls_assert ();
          return ret;
        }

    case STATE62:
      STATE = STATE62;
      if (how == GNUTLS_SHUT_RDWR)
        {
          do
            {
              ret = _gnutls_recv_int (session, GNUTLS_ALERT, -1, NULL, 0, NULL, 
                                      session->internals.record_timeout_ms);
            }
          while (ret == GNUTLS_E_GOT_APPLICATION_DATA);

          if (ret >= 0)
            session->internals.may_not_read = 1;

          if (ret < 0)
            {
              gnutls_assert ();
              return ret;
            }
        }
      STATE = STATE62;

      break;
    default:
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  STATE = STATE0;

  session->internals.may_not_write = 1;
  return 0;
}
Пример #3
0
/* This is called when we want send our certificate
 */
int
_gnutls_send_client_certificate (gnutls_session_t session, int again)
{
  gnutls_buffer_st data;
  int ret = 0;


  if (session->key->certificate_requested == 0)
    return 0;

  if (session->internals.auth_struct->gnutls_generate_client_certificate ==
      NULL)
    return 0;

  _gnutls_buffer_init( &data);

  if (again == 0)
    {
      if (gnutls_protocol_get_version (session) != GNUTLS_SSL3 ||
          session->internals.selected_cert_list_length > 0)
        {
          /* TLS 1.0 or SSL 3.0 with a valid certificate 
           */
          ret =
            session->internals.
            auth_struct->gnutls_generate_client_certificate (session, &data);

          if (ret < 0)
            {
              gnutls_assert();
              goto cleanup;
            }
        }
    }

  /* In the SSL 3.0 protocol we need to send a
   * no certificate alert instead of an
   * empty certificate.
   */
  if (gnutls_protocol_get_version (session) == GNUTLS_SSL3 &&
      session->internals.selected_cert_list_length == 0)
    {
      ret =
        gnutls_alert_send (session, GNUTLS_AL_WARNING,
                           GNUTLS_A_SSL3_NO_CERTIFICATE);

    }
  else
    {                           /* TLS 1.0 or SSL 3.0 with a valid certificate 
                                 */
      ret = send_handshake (session, data.data, data.length,
                            GNUTLS_HANDSHAKE_CERTIFICATE_PKT);
    }

cleanup:
  _gnutls_buffer_clear (&data);
  return ret;
}
Пример #4
0
void cstp_fatal_close(worker_st *ws,
			    gnutls_alert_description_t a)
{
	if (ws->session) {
		gnutls_alert_send(ws->session, GNUTLS_AL_FATAL, a);
		gnutls_deinit(ws->session);
	} else {
		close(ws->conn_fd);
	}
}
Пример #5
0
/**
 * gnutls_alert_send_appropriate:
 * @session: is a #gnutls_session_t type.
 * @err: is an integer
 *
 * Sends an alert to the peer depending on the error code returned by
 * a gnutls function. This function will call gnutls_error_to_alert()
 * to determine the appropriate alert to send.
 *
 * This function may also return %GNUTLS_E_AGAIN, or
 * %GNUTLS_E_INTERRUPTED.
 *
 * If the return value is %GNUTLS_E_INVALID_REQUEST, then no alert has
 * been sent to the peer.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
 *   an error code is returned.
 */
int gnutls_alert_send_appropriate(gnutls_session_t session, int err)
{
	int alert;
	int level;

	alert = gnutls_error_to_alert(err, &level);
	if (alert < 0) {
		return alert;
	}

	return gnutls_alert_send(session, level, alert);
}
Пример #6
0
static int check_req(const char *srcinfo, requiem_io_t *fd, gnutls_x509_crq crq, uint64_t *analyzerid)
{
        int ret;
        size_t size;
        char buf[128], c;
        requiem_string_t *out;
        requiem_connection_permission_t perms;

        size = sizeof(buf);
        ret = gnutls_x509_crq_get_dn_by_oid(crq, GNUTLS_OID_X520_DN_QUALIFIER, 0, 0, buf, &size);
        if ( ret < 0 ) {
                fprintf(stderr, "could not retrieve dn qualifier: %s.\n", gnutls_strerror(ret));
                return -1;
        }
        *analyzerid = strtoull(buf, NULL, 10);

        size = sizeof(buf);
        ret = gnutls_x509_crq_get_dn_by_oid(crq, GNUTLS_OID_X520_COMMON_NAME, 0, 0, buf, &size);
        if ( ret < 0 ) {
                fprintf(stderr, "could not retrieve common name: %s.\n", gnutls_strerror(ret));
                return -1;
        }
        perms = atoi(buf);

        ret = requiem_string_new(&out);
        if ( ret < 0 ) {
                requiem_perror(ret, "error creating requiem-string");
                return -1;
        }

        ret = requiem_connection_permission_to_string(perms, out);
        if ( ret < 0 ) {
                requiem_perror(ret, "could not convert permission to string");
                return -1;
        }

        fprintf(stderr, "Registration request for analyzerID=\"%" REQUIEM_PRIu64 "\" permission=\"%s\".\n",
                *analyzerid, requiem_string_get_string(out));


        if ( server_confirm ) {
                c = ask_req_confirmation();
                if ( c != 'y' ) {
                        gnutls_alert_send(requiem_io_get_fdptr(fd), GNUTLS_AL_FATAL, GNUTLS_A_USER_CANCELED);
                        return -1;
                }
        }

        requiem_string_destroy(out);

        return 0;
}
Пример #7
0
static int send_queued_alert(server_generic_client_t *client)
{
        int ret;

        do {
                ret = gnutls_alert_send(prelude_io_get_fdptr(client->fd), GNUTLS_AL_FATAL, client->alert);
        } while ( ret < 0 && ret == GNUTLS_E_INTERRUPTED );

        if ( ret == GNUTLS_E_AGAIN ) {
                server_generic_notify_write_enable(client);
                return 0;
        }

        client->alert = 0;
        gnutls_deinit(prelude_io_get_fdptr(client->fd));
        prelude_io_set_sys_io(client->fd, prelude_io_get_fd(client->fd));

        return 1;
}
Пример #8
0
static int
_gnutls_ia_server_handshake (gnutls_session_t session)
{
  gnutls_ia_apptype_t msg_type;
  ssize_t len;
  char buf[1024];
  int ret;
  const struct gnutls_ia_server_credentials_st *cred =
    _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL);

  if (cred == NULL)
    return GNUTLS_E_INTERNAL_ERROR;

  do
    {
      char *avp;
      size_t avplen;

      len = gnutls_ia_recv (session, buf, sizeof (buf));
      if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
	  len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
	{
	  ret = gnutls_ia_verify_endphase (session, buf);
	  if (ret < 0)
	    return ret;
	}

      if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED)
	continue;
      else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
	break;

      if (len < 0)
	return len;

      avp = NULL;
      avplen = 0;

      ret = cred->avp_func (session, cred->avp_ptr, buf, len, &avp, &avplen);
      if (ret < 0)
	{
	  int tmpret;
	  tmpret = gnutls_alert_send (session, GNUTLS_AL_FATAL,
				      GNUTLS_A_INNER_APPLICATION_FAILURE);
	  if (tmpret < 0)
	    gnutls_assert ();
	  return ret;
	}

      msg_type = ret;

      if (msg_type != GNUTLS_IA_APPLICATION_PAYLOAD)
	{
	  ret = gnutls_ia_endphase_send (session, msg_type ==
					 GNUTLS_IA_FINAL_PHASE_FINISHED);
	  if (ret < 0)
	    return ret;
	}
      else
	{
	  len = gnutls_ia_send (session, avp, avplen);
	  gnutls_free (avp);
	  if (len < 0)
	    return len;
	}
    }
  while (1);

  return 0;
}
Пример #9
0
static int
_gnutls_ia_client_handshake (gnutls_session_t session)
{
  char *buf = NULL;
  size_t buflen = 0;
  char tmp[1024];		/* XXX */
  ssize_t len;
  int ret;
  const struct gnutls_ia_client_credentials_st *cred =
    _gnutls_get_cred (session->key, GNUTLS_CRD_IA, NULL);

  if (cred == NULL)
    return GNUTLS_E_INTERNAL_ERROR;

  while (1)
    {
      char *avp;
      size_t avplen;

      ret = cred->avp_func (session, cred->avp_ptr,
			    buf, buflen, &avp, &avplen);
      if (ret)
	{
	  int tmpret;
	  tmpret = gnutls_alert_send (session, GNUTLS_AL_FATAL,
				      GNUTLS_A_INNER_APPLICATION_FAILURE);
	  if (tmpret < 0)
	    gnutls_assert ();
	  return ret;
	}

      len = gnutls_ia_send (session, avp, avplen);
      gnutls_free (avp);
      if (len < 0)
	return len;

      len = gnutls_ia_recv (session, tmp, sizeof (tmp));
      if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
	  len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
	{
	  ret = gnutls_ia_verify_endphase (session, tmp);
	  if (ret < 0)
	    return ret;

	  ret = gnutls_ia_endphase_send
	    (session, len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED);
	  if (ret < 0)
	    return ret;
	}

      if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED)
	{
	  buf = NULL;
	  buflen = 0;
	  continue;
	}
      else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
	break;

      if (len < 0)
	return len;

      buflen = len;
      buf = tmp;
    }

  return 0;
}
Пример #10
0
/** Handles one client.
 * This one connects to the remote server, and proxies every traffic
 * between our client and the server.
 *
 * @param config is the main CryWrap configuration structure.
 * @param insock is the socket through which the client sends input.
 * @param outsock is the socket through which we send output.
 *
 * @note Exits on error.
 */
static int
_crywrap_do_one(const crywrap_config_t * config, int insock, int outsock)
{
	int sock, ret, tls_pending;
	gnutls_session_t session;
	char buffer[_CRYWRAP_MAXBUF + 2];
	fd_set fdset;
	unsigned int status = 0;
	struct sockaddr_storage faddr;
	socklen_t socklen = sizeof(struct sockaddr_storage);
	char peer_name[NI_MAXHOST];

	/* Log the connection */
	if (getpeername(insock, (struct sockaddr *) &faddr, &socklen) != 0)
		cry_error("getpeername(): %s", strerror(errno));
	else {
		getnameinfo((struct sockaddr *) &faddr,
			    sizeof(struct sockaddr_storage), peer_name,
			    sizeof(peer_name), NULL, 0, NI_NUMERICHOST);
		cry_log("Accepted connection from %s on %d to %s/%d",
			peer_name, insock, config->dest.host,
			config->dest.port);
	}

	/* Do the handshake with our peer */
	session = _crywrap_tls_session_create(config);
	gnutls_transport_set_ptr2(session,
				  (gnutls_transport_ptr_t) insock,
				  (gnutls_transport_ptr_t) outsock);

	do {
		ret = gnutls_handshake(session);
	}
	while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);

	if (ret < 0) {
		cry_error("Handshake failed: %s", gnutls_strerror(ret));
		gnutls_alert_send_appropriate(session, ret);
		goto error;
	}

	/* Verify the client's certificate, if any. */
	if (config->verify) {
		ret = gnutls_certificate_verify_peers2(session, &status);
		if (ret < 0)
			cry_log
			    ("Error getting certificate from client: %s",
			     gnutls_strerror(ret));

		if (ret == 0 && status != 0) {
			if (status & GNUTLS_CERT_INVALID)
				cry_log("%s",
					"Client certificate not trusted or invalid");
		}

		if (config->verify > 0 && status != 0) {
			ret = -1;
			gnutls_alert_send(session, GNUTLS_AL_FATAL,
					  GNUTLS_A_INSUFFICIENT_SECURITY);
			goto error;
		}
	}

	/* Connect to the remote host */
	sock = _crywrap_remote_connect(config->dest.addr,
				       htons(config->dest.port));

	for (;;) {
		FD_ZERO(&fdset);
		FD_SET(insock, &fdset);
		FD_SET(sock, &fdset);

		memset(buffer, 0, _CRYWRAP_MAXBUF + 1);

		tls_pending = 0;

		if (gnutls_record_check_pending(session) > 0)
			tls_pending = 1;
		else {
			select(sock + 1, &fdset, NULL, NULL, NULL);
			if (FD_ISSET(insock, &fdset))
				tls_pending = 1;
		}
		/* TLS client */
		if (tls_pending != 0) {
			ret =
			    gnutls_record_recv(session, buffer,
					       _CRYWRAP_MAXBUF);
			if (ret == 0) {
				cry_log("%s",
					"Peer has closed the GNUTLS connection");
				break;
			} else if (ret < 0) {
				cry_log("Received corrupted data: %s.",
					gnutls_strerror(ret));
				break;
			} else
				send(sock, buffer, ret, 0);
		}

		/* Remote server */
		if (FD_ISSET(sock, &fdset)) {
			ret = recv(sock, buffer, _CRYWRAP_MAXBUF, 0);
			if (ret == 0) {
				cry_log("%s",
					"Server has closed the connection");
				break;
			} else if (ret < 0) {
				cry_log("Received corrupted data: %s.",
					strerror(errno));
				break;
			} else {
				int r, o = 0;

				do {
					r = gnutls_record_send(session,
							       &buffer[o],
							       ret - o);
					o += r;
				} while (r > 0 && ret > o);

				if (r < 0)
					cry_log
					    ("Received corrupt data: %s",
					     gnutls_strerror(r));
			}
		}
	}

      error:
	gnutls_bye(session, GNUTLS_SHUT_WR);
	gnutls_deinit(session);
	close(insock);
	close(outsock);

	return (ret == 0) ? 0 : 1;
}
u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
                              const u8 *in_data, size_t in_len,
                              size_t *out_len, u8 **appl_data,
                              size_t *appl_data_len)
{
        struct tls_global *global = ssl_ctx;
        u8 *out_data;
        int ret;

        if (appl_data)
                *appl_data = NULL;

        if (in_data && in_len) {
                if (conn->pull_buf) {
                        wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
                                   "pull_buf", __func__,
                                   (unsigned long) conn->pull_buf_len);
                        os_free(conn->pull_buf);
                }
                conn->pull_buf = os_malloc(in_len);
                if (conn->pull_buf == NULL)
                        return NULL;
                os_memcpy(conn->pull_buf, in_data, in_len);
                conn->pull_buf_offset = conn->pull_buf;
                conn->pull_buf_len = in_len;
        }

        ret = gnutls_handshake(conn->session);
        if (ret < 0) {
                switch (ret) {
                case GNUTLS_E_AGAIN:
                        if (global->server && conn->established &&
                            conn->push_buf == NULL) {
                                /* Need to return something to trigger
                                 * completion of EAP-TLS. */
                                conn->push_buf = os_malloc(1);
                        }
                        break;
                case GNUTLS_E_FATAL_ALERT_RECEIVED:
                        wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
                                   __func__, gnutls_alert_get_name(
                                           gnutls_alert_get(conn->session)));
                        conn->read_alerts++;
                        /* continue */
                default:
                        wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
                                   "-> %s", __func__, gnutls_strerror(ret));
                        conn->failed++;
                }
        } else {
                size_t size;
                gnutls_alert_description_t err;

                if (conn->verify_peer &&
                    tls_connection_verify_peer(conn, &err)) {
                        wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
                                   "failed validation");
                        conn->failed++;
                        gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err);
                        goto out;
                }

#ifdef CONFIG_GNUTLS_EXTRA
                if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) {
                        wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation");
                        conn->failed++;
                        return NULL;
                }
#endif /* CONFIG_GNUTLS_EXTRA */

                if (conn->tls_ia)
                        wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake");
                else {
                        wpa_printf(MSG_DEBUG, "TLS: Handshake completed "
                                   "successfully");
                }
                conn->established = 1;
                if (conn->push_buf == NULL) {
                        /* Need to return something to get final TLS ACK. */
                        conn->push_buf = os_malloc(1);
                }

                gnutls_session_get_data(conn->session, NULL, &size);
                if (global->session_data == NULL ||
                    global->session_data_size < size) {
                        os_free(global->session_data);
                        global->session_data = os_malloc(size);
                }
                if (global->session_data) {
                        global->session_data_size = size;
                        gnutls_session_get_data(conn->session,
                                                global->session_data,
                                                &global->session_data_size);
                }
        }

out:
        out_data = conn->push_buf;
        *out_len = conn->push_buf_len;
        conn->push_buf = NULL;
        conn->push_buf_len = 0;
        return out_data;
}
Пример #12
0
struct wpabuf * tls_connection_handshake(void *tls_ctx,
					 struct tls_connection *conn,
					 const struct wpabuf *in_data,
					 struct wpabuf **appl_data)
{
	struct tls_global *global = tls_ctx;
	struct wpabuf *out_data;
	int ret;

	if (appl_data)
		*appl_data = NULL;

	if (in_data && wpabuf_len(in_data) > 0) {
		if (conn->pull_buf) {
			wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
				   "pull_buf", __func__,
				   (unsigned long) wpabuf_len(conn->pull_buf));
			wpabuf_free(conn->pull_buf);
		}
		conn->pull_buf = wpabuf_dup(in_data);
		if (conn->pull_buf == NULL)
			return NULL;
		conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
	}

	ret = gnutls_handshake(conn->session);
	if (ret < 0) {
		switch (ret) {
		case GNUTLS_E_AGAIN:
			if (global->server && conn->established &&
			    conn->push_buf == NULL) {
				/* Need to return something to trigger
				 * completion of EAP-TLS. */
				conn->push_buf = wpabuf_alloc(0);
			}
			break;
		case GNUTLS_E_FATAL_ALERT_RECEIVED:
			wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
				   __func__, gnutls_alert_get_name(
					   gnutls_alert_get(conn->session)));
			conn->read_alerts++;
			/* continue */
		default:
			wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
				   "-> %s", __func__, gnutls_strerror(ret));
			conn->failed++;
		}
	} else {
		size_t size;
		gnutls_alert_description_t err;

		if (conn->verify_peer &&
		    tls_connection_verify_peer(conn, &err)) {
			wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
				   "failed validation");
			conn->failed++;
			gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err);
			goto out;
		}

		wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
		conn->established = 1;
		if (conn->push_buf == NULL) {
			/* Need to return something to get final TLS ACK. */
			conn->push_buf = wpabuf_alloc(0);
		}

		gnutls_session_get_data(conn->session, NULL, &size);
		if (global->session_data == NULL ||
		    global->session_data_size < size) {
			os_free(global->session_data);
			global->session_data = os_malloc(size);
		}
		if (global->session_data) {
			global->session_data_size = size;
			gnutls_session_get_data(conn->session,
						global->session_data,
						&global->session_data_size);
		}

		if (conn->pull_buf && appl_data)
			*appl_data = gnutls_get_appl_data(conn);
	}

out:
	out_data = conn->push_buf;
	conn->push_buf = NULL;
	return out_data;
}
Пример #13
0
static BOOL
verify_certificate(gnutls_session session, const char **error)
{
int verify;
uschar *dn_string = US"";
const gnutls_datum *cert;
unsigned int cert_size = 0;

*error = NULL;

/* Get the peer's certificate. If it sent one, extract it's DN, and then
attempt to verify the certificate. If no certificate is supplied, verification
is forced to fail. */

cert = gnutls_certificate_get_peers(session, &cert_size);
if (cert != NULL)
  {
  uschar buff[1024];
  gnutls_x509_crt gcert;

  gnutls_x509_crt_init(&gcert);
  dn_string = US"unknown";

  if (gnutls_x509_crt_import(gcert, cert, GNUTLS_X509_FMT_DER) == 0)
    {
    size_t bufsize = sizeof(buff);
    if (gnutls_x509_crt_get_dn(gcert, CS buff, &bufsize) >= 0)
      dn_string = string_copy_malloc(buff);
    }

  verify = gnutls_certificate_verify_peers(session);
  }
else
  {
  DEBUG(D_tls) debug_printf("no peer certificate supplied\n");
  verify = GNUTLS_CERT_INVALID;
  *error = "not supplied";
  }

/* Handle the result of verification. INVALID seems to be set as well
as REVOKED, but leave the test for both. */

if ((verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0)
  {
  tls_certificate_verified = FALSE;
  if (*error == NULL) *error = ((verify & GNUTLS_CERT_REVOKED) != 0)?
    "revoked" : "invalid";
  if (verify_requirement == VERIFY_REQUIRED)
    {
    DEBUG(D_tls) debug_printf("TLS certificate verification failed (%s): "
      "peerdn=%s\n", *error, dn_string);
    gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE);
    return FALSE;                       /* reject */
    }
  DEBUG(D_tls) debug_printf("TLS certificate verify failure (%s) overridden "
      "(host in tls_try_verify_hosts): peerdn=%s\n", *error, dn_string);
  }
else
  {
  tls_certificate_verified = TRUE;
  DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=%s\n",
    dn_string);
  }

tls_peerdn = dn_string;
return TRUE;                            /* accept */
}
Пример #14
0
static int tls_connection_verify_peer(gnutls_session_t session)
{
	struct tls_connection *conn;
	unsigned int status, num_certs, i;
	struct os_time now;
	const gnutls_datum_t *certs;
	gnutls_x509_crt_t cert;
	gnutls_alert_description_t err;
	int res;

	conn = gnutls_session_get_ptr(session);
	if (!conn->verify_peer) {
		wpa_printf(MSG_DEBUG,
			   "GnuTLS: No peer certificate verification enabled");
		return 0;
	}

	wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate");

#if GNUTLS_VERSION_NUMBER >= 0x030300
	{
		gnutls_typed_vdata_st data[1];
		unsigned int elements = 0;

		os_memset(data, 0, sizeof(data));
		if (!conn->global->server) {
			data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID;
			data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER;
			elements++;
		}
		res = gnutls_certificate_verify_peers(session, data, 1,
						      &status);
	}
#else /* < 3.3.0 */
	res = gnutls_certificate_verify_peers2(session, &status);
#endif
	if (res < 0) {
		wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
			   "certificate chain");
		err = GNUTLS_A_INTERNAL_ERROR;
		goto out;
	}

#if GNUTLS_VERSION_NUMBER >= 0x030104
	{
		gnutls_datum_t info;
		int ret, type;

		type = gnutls_certificate_type_get(session);
		ret = gnutls_certificate_verification_status_print(status, type,
								   &info, 0);
		if (ret < 0) {
			wpa_printf(MSG_DEBUG,
				   "GnuTLS: Failed to print verification status");
			err = GNUTLS_A_INTERNAL_ERROR;
			goto out;
		}
		wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data);
		gnutls_free(info.data);
	}
#endif /* GnuTLS 3.1.4 or newer */

	certs = gnutls_certificate_get_peers(session, &num_certs);
	if (certs == NULL || num_certs == 0) {
		wpa_printf(MSG_INFO, "TLS: No peer certificate chain received");
		err = GNUTLS_A_UNKNOWN_CA;
		goto out;
	}

	if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
		wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
		if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
			wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
				   "algorithm");
			gnutls_tls_fail_event(conn, NULL, 0, NULL,
					      "certificate uses insecure algorithm",
					      TLS_FAIL_BAD_CERTIFICATE);
			err = GNUTLS_A_INSUFFICIENT_SECURITY;
			goto out;
		}
		if (status & GNUTLS_CERT_NOT_ACTIVATED) {
			wpa_printf(MSG_INFO, "TLS: Certificate not yet "
				   "activated");
			gnutls_tls_fail_event(conn, NULL, 0, NULL,
					      "certificate not yet valid",
					      TLS_FAIL_NOT_YET_VALID);
			err = GNUTLS_A_CERTIFICATE_EXPIRED;
			goto out;
		}
		if (status & GNUTLS_CERT_EXPIRED) {
			wpa_printf(MSG_INFO, "TLS: Certificate expired");
			gnutls_tls_fail_event(conn, NULL, 0, NULL,
					      "certificate has expired",
					      TLS_FAIL_EXPIRED);
			err = GNUTLS_A_CERTIFICATE_EXPIRED;
			goto out;
		}
		gnutls_tls_fail_event(conn, NULL, 0, NULL,
				      "untrusted certificate",
				      TLS_FAIL_UNTRUSTED);
		err = GNUTLS_A_INTERNAL_ERROR;
		goto out;
	}

	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
		wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
			   "known issuer");
		gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found",
				      TLS_FAIL_UNTRUSTED);
		err = GNUTLS_A_UNKNOWN_CA;
		goto out;
	}

	if (status & GNUTLS_CERT_REVOKED) {
		wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
		gnutls_tls_fail_event(conn, NULL, 0, NULL,
				      "certificate revoked",
				      TLS_FAIL_REVOKED);
		err = GNUTLS_A_CERTIFICATE_REVOKED;
		goto out;
	}

	if (status != 0) {
		wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d",
			   status);
		err = GNUTLS_A_INTERNAL_ERROR;
		goto out;
	}

	if (check_ocsp(conn, session, &err))
		goto out;

	os_get_time(&now);

	for (i = 0; i < num_certs; i++) {
		char *buf;
		size_t len;
		if (gnutls_x509_crt_init(&cert) < 0) {
			wpa_printf(MSG_INFO, "TLS: Certificate initialization "
				   "failed");
			err = GNUTLS_A_BAD_CERTIFICATE;
			goto out;
		}

		if (gnutls_x509_crt_import(cert, &certs[i],
					   GNUTLS_X509_FMT_DER) < 0) {
			wpa_printf(MSG_INFO, "TLS: Could not parse peer "
				   "certificate %d/%d", i + 1, num_certs);
			gnutls_x509_crt_deinit(cert);
			err = GNUTLS_A_BAD_CERTIFICATE;
			goto out;
		}

		gnutls_x509_crt_get_dn(cert, NULL, &len);
		len++;
		buf = os_malloc(len + 1);
		if (buf) {
			buf[0] = buf[len] = '\0';
			gnutls_x509_crt_get_dn(cert, buf, &len);
		}
		wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
			   i + 1, num_certs, buf);

		if (conn->global->event_cb) {
			struct wpabuf *cert_buf = NULL;
			union tls_event_data ev;
#ifdef CONFIG_SHA256
			u8 hash[32];
			const u8 *_addr[1];
			size_t _len[1];
#endif /* CONFIG_SHA256 */

			os_memset(&ev, 0, sizeof(ev));
			if (conn->global->cert_in_cb) {
				cert_buf = wpabuf_alloc_copy(certs[i].data,
							     certs[i].size);
				ev.peer_cert.cert = cert_buf;
			}
#ifdef CONFIG_SHA256
			_addr[0] = certs[i].data;
			_len[0] = certs[i].size;
			if (sha256_vector(1, _addr, _len, hash) == 0) {
				ev.peer_cert.hash = hash;
				ev.peer_cert.hash_len = sizeof(hash);
			}
#endif /* CONFIG_SHA256 */
			ev.peer_cert.depth = i;
			ev.peer_cert.subject = buf;
			conn->global->event_cb(conn->global->cb_ctx,
					       TLS_PEER_CERTIFICATE, &ev);
			wpabuf_free(cert_buf);
		}

		if (i == 0) {
			if (conn->suffix_match &&
			    !gnutls_x509_crt_check_hostname(
				    cert, conn->suffix_match)) {
				wpa_printf(MSG_WARNING,
					   "TLS: Domain suffix match '%s' not found",
					   conn->suffix_match);
				gnutls_tls_fail_event(
					conn, &certs[i], i, buf,
					"Domain suffix mismatch",
					TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
				err = GNUTLS_A_BAD_CERTIFICATE;
				gnutls_x509_crt_deinit(cert);
				os_free(buf);
				goto out;
			}

#if GNUTLS_VERSION_NUMBER >= 0x030300
			if (conn->domain_match &&
			    !gnutls_x509_crt_check_hostname2(
				    cert, conn->domain_match,
				    GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) {
				wpa_printf(MSG_WARNING,
					   "TLS: Domain match '%s' not found",
					   conn->domain_match);
				gnutls_tls_fail_event(
					conn, &certs[i], i, buf,
					"Domain mismatch",
					TLS_FAIL_DOMAIN_MISMATCH);
				err = GNUTLS_A_BAD_CERTIFICATE;
				gnutls_x509_crt_deinit(cert);
				os_free(buf);
				goto out;
			}
#endif /* >= 3.3.0 */

			/* TODO: validate altsubject_match.
			 * For now, any such configuration is rejected in
			 * tls_connection_set_params() */

#if GNUTLS_VERSION_NUMBER < 0x030300
			/*
			 * gnutls_certificate_verify_peers() not available, so
			 * need to check EKU separately.
			 */
			if (!conn->global->server &&
			    !server_eku_purpose(cert)) {
				wpa_printf(MSG_WARNING,
					   "GnuTLS: No server EKU");
				gnutls_tls_fail_event(
					conn, &certs[i], i, buf,
					"No server EKU",
					TLS_FAIL_BAD_CERTIFICATE);
				err = GNUTLS_A_BAD_CERTIFICATE;
				gnutls_x509_crt_deinit(cert);
				os_free(buf);
				goto out;
			}
#endif /* < 3.3.0 */
		}

		if (!conn->disable_time_checks &&
		    (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
		     gnutls_x509_crt_get_activation_time(cert) > now.sec)) {
			wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
				   "not valid at this time",
				   i + 1, num_certs);
			gnutls_tls_fail_event(
				conn, &certs[i], i, buf,
				"Certificate is not valid at this time",
				TLS_FAIL_EXPIRED);
			gnutls_x509_crt_deinit(cert);
			os_free(buf);
			err = GNUTLS_A_CERTIFICATE_EXPIRED;
			goto out;
		}

		os_free(buf);

		gnutls_x509_crt_deinit(cert);
	}

	if (conn->global->event_cb != NULL)
		conn->global->event_cb(conn->global->cb_ctx,
				       TLS_CERT_CHAIN_SUCCESS, NULL);

	return 0;

out:
	conn->failed++;
	gnutls_alert_send(session, GNUTLS_AL_FATAL, err);
	return GNUTLS_E_CERTIFICATE_ERROR;
}
Пример #15
0
static int gnutls_do_handshake(mgs_handle_t * ctxt) {
    int ret;
    int errcode;
    int maxtries = HANDSHAKE_MAX_TRIES;

    if (ctxt->status != 0 || ctxt->session == NULL) {
        return -1;
    }

tryagain:
    do {
        ret = gnutls_handshake(ctxt->session);
        maxtries--;
    } while ((ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
            && maxtries > 0);

    if (maxtries < 1) {
        ctxt->status = -1;
#if USING_2_1_RECENT
        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c,
                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
#else
        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
                ctxt->c->base_server,
                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
#endif
        if (ctxt->session) {
            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL,
                    gnutls_error_to_alert
                    (GNUTLS_E_INTERNAL_ERROR, NULL));
            gnutls_deinit(ctxt->session);
        }
        ctxt->session = NULL;
        return -1;
    }

    if (ret < 0) {
        if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
                || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
            errcode = gnutls_alert_get(ctxt->session);
            ap_log_error(APLOG_MARK, APLOG_INFO, 0,
                    ctxt->c->base_server,
                    "GnuTLS: Hanshake Alert (%d) '%s'.",
                    errcode,
                    gnutls_alert_get_name(errcode));
        }

        if (!gnutls_error_is_fatal(ret)) {
            ap_log_error(APLOG_MARK, APLOG_INFO, 0,
                    ctxt->c->base_server,
                    "GnuTLS: Non-Fatal Handshake Error: (%d) '%s'",
                    ret, gnutls_strerror(ret));
            goto tryagain;
        }
#if USING_2_1_RECENT
        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
                "GnuTLS: Handshake Failed (%d) '%s'", ret,
                gnutls_strerror(ret));
#else
        ap_log_error(APLOG_MARK, APLOG_INFO, 0,
                ctxt->c->base_server,
                "GnuTLS: Handshake Failed (%d) '%s'", ret,
                gnutls_strerror(ret));
#endif
        ctxt->status = -1;
        if (ctxt->session) {
            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL,
                    gnutls_error_to_alert(ret,
                    NULL));
            gnutls_deinit(ctxt->session);
        }
        ctxt->session = NULL;
        return ret;
    } else {
        /* all done with the handshake */
        ctxt->status = 1;
        /* If the session was resumed, we did not set the correct
         * server_rec in ctxt->sc.  Go Find it. (ick!)
         */
        if (gnutls_session_is_resumed(ctxt->session)) {
            mgs_srvconf_rec *sc;
            sc = mgs_find_sni_server(ctxt->session);
            if (sc) {
                ctxt->sc = sc;
            }
        }
        return 0;
    }
}
Пример #16
0
int session::send_alert (gnutls_alert_level_t level,
                         gnutls_alert_description_t desc)
{
    return RETWRAP (gnutls_alert_send (s, level, desc));
}