[gnutls-devel] GnuTLS | Segfault during handshake on server connection if no private key is supplied (#1412)

Read-only notification of GnuTLS library development activities gnutls-devel at lists.gnutls.org
Tue Oct 4 12:05:49 CEST 2022



Sebastian Dröge created an issue: https://gitlab.com/gnutls/gnutls/-/issues/1412



When providing credentials without a private key then `gnutls_handshake()` will segfault. This is obviously an application bug but it would be great if GnuTLS could also catch this in a better way, either with an actual assertion or with a handshake error.

valgrind reports the following with the attached server application (based on the echo server example from the docs):

```
==427258== Invalid read of size 4
==427258==    at 0x490617D: _gnutls_privkey_compatible_with_sig (privkey.c:1966)
==427258==    by 0x499BD22: _gnutls_session_get_sign_algo (signature.c:381)
==427258==    by 0x49A94BC: cert_select_sign_algorithm (cert.c:1591)
==427258==    by 0x49AC308: _gnutls_select_server_cert (cert.c:1643)
==427258==    by 0x49B778D: _gnutls_figure_common_ciphersuite (ciphersuites.c:1526)
==427258==    by 0x48D01C1: _gnutls_server_select_suite (handshake.c:1158)
==427258==    by 0x48D2FBC: read_client_hello (handshake.c:862)
==427258==    by 0x48D2FBC: _gnutls_recv_handshake (handshake.c:1641)
==427258==    by 0x48D6368: handshake_server (handshake.c:3496)
==427258==    by 0x48D6368: gnutls_handshake (handshake.c:2886)
==427258==    by 0x1097B0: main (test.c:127)
==427258==  Address 0x4 is not stack'd, malloc'd or (recently) free'd
```

Testcase application

```cpp
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <gnutls/gnutls.h>
#include <gnutls/urls.h>
#include <gnutls/x509.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define CHECK(x) assert((x) >= 0)
#define LOOP_CHECK(rval, cmd)                                                  \
  do {                                                                         \
    rval = cmd;                                                                \
  } while (rval == GNUTLS_E_AGAIN || rval == GNUTLS_E_INTERRUPTED)

#define MAX_BUF 1024
#define PORT 5556

const unsigned char cert_data[] = "-----BEGIN CERTIFICATE----- \
MIIEQDCCAqigAwIBAgIRAO5XT7tkYlNt77CBj2H5HiEwDQYJKoZIhvcNAQELBQAw \
czEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSQwIgYDVQQLDBtqbWFs \
dmVzQHBvcC1vcyAoSm9hbyBBbHZlcykxKzApBgNVBAMMIm1rY2VydCBqbWFsdmVz \
QHBvcC1vcyAoSm9hbyBBbHZlcykwHhcNMjIwOTIyMTIyNTMzWhcNMjQxMjIyMTMy \
NTMzWjBPMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUx \
JDAiBgNVBAsMG2ptYWx2ZXNAcG9wLW9zIChKb2FvIEFsdmVzKTCCASIwDQYJKoZI \
hvcNAQEBBQADggEPADCCAQoCggEBAJPz8CCFHtNsSzbc64tR/7B1Kf1xuxDz0cBS \
vAlEYzfaBrPmgV3zoaIgKnW9u8EWwvRXzhVLfMnO/x8jfIRmSDDK1M+fXmriIDkx \
5cbWnCN2GQ81P/GdvsGpx2XWBpKTCYQm/6EvdvAsg0+GWrgSxo4hFg59YLaWeWHj \
PTSKABM9C63X9UnQKvP25rYkJ42znnkmqmGXCe1iPk1xZvDfqcbZ1sZXlMV2dS9M \
CRu6Dwqo7N3hVbwM/vZ4X7vWH6JRT8Soz6CijWDcAVvusrxx0QyNQg4nuQznZrnW \
nQbjmEQf4MCO4OCd5uh5rXcxvdYZbpMx25EujMbh0OWfecC+GH8CAwEAAaNzMHEw \
DgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaA \
FIwNqPZz6OxXFjHo/y0pAHyU3Q2TMCkGA1UdEQQiMCCCCWxvY2FsaG9zdIINZHMu \
YWl2ZXJvLmxhbocEfwAAATANBgkqhkiG9w0BAQsFAAOCAYEAiPa2QzuPK9FckgW+ \
FBu0fwsjBY+DOhhLzj7Su2eEg0hQDuV8A8hwbj6lfvY0UGOh6Tb6fjs/daUaQLcK \
k5och7JbHiauch5hVx2xmXCcJu55sLW9UcJHOSvP+jalUm1BqlMWHIAcbFwajOWV \
tXENgRleSkFmw2uoi66lQq8QBHDosUKJukZQnweXihWXoZ2pWPkaOY3cBc2DYpBM \
8ioyW6mdyG1Ot4vT5En+WauGJCFKAzvPtM74HNp9kw+ddLqPamTTbMtm78gi8B7E \
eHPYBedjEvaroY5lw1iU6n5VNiF61Ygc3vC4QSCIBNOOMUEXwK0c3gzF9DrpCBXY \
ezVYCmgnM5FfeJ/l25DhpPdUTD+MJJ3dZd+AB4Kx7a0dI5xWelaZCExa+yr8UWYd \
hsrOnFMTWHMjlx+kzWxC4U+bHAxX4buQpq37l9GyASldbLGdqA86Dnaxit3nhZPb \
GO+ToIks+22GnP8wLvWWTZc3wV90RhRDy5GkZzDX7K8J1/C0 \
-----END CERTIFICATE-----";

static const gnutls_datum_t cert_pem = {
    .data = (unsigned char *)cert_data,
    .size = sizeof(cert_data),
};

static gnutls_x509_crt_t cert;
static gnutls_privkey_t privkey;
static gnutls_certificate_credentials_t creds;

static int retrieve_func(gnutls_session_t session,
                         const gnutls_datum_t *req_ca_rdn, int nreqs,
                         const gnutls_pk_algorithm_t *pk_algos,
                         int pk_algos_length, gnutls_pcert_st **pcert,
                         unsigned int *pcert_length, gnutls_privkey_t *pkey) {

  *pcert = malloc(sizeof(gnutls_pcert_st));
  gnutls_pcert_import_x509(*pcert, cert, 0);
  *pcert_length = 1;
  *pkey = *pkey;
  return 0;
}

int main(void) {
  int listen_sd;
  int sd, ret;
  gnutls_priority_t priority_cache;
  struct sockaddr_in sa_serv;
  struct sockaddr_in sa_cli;
  socklen_t client_len;
  char topbuf[512];
  gnutls_session_t session;
  char buffer[MAX_BUF + 1];
  int optval = 1;

  CHECK(gnutls_global_init());

  CHECK(gnutls_x509_crt_init(&cert));
  CHECK(gnutls_x509_crt_import(cert, &cert_pem, GNUTLS_X509_FMT_PEM));
  CHECK(gnutls_privkey_init(&privkey));

  CHECK(gnutls_certificate_allocate_credentials(&creds));
  gnutls_certificate_set_retrieve_function2(creds, retrieve_func);

  CHECK(gnutls_priority_init(&priority_cache, NULL, NULL));

  listen_sd = socket(AF_INET, SOCK_STREAM, 0);

  memset(&sa_serv, '\0', sizeof(sa_serv));
  sa_serv.sin_family = AF_INET;
  sa_serv.sin_addr.s_addr = INADDR_ANY;
  sa_serv.sin_port = htons(PORT); /* Server Port number */

  setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int));

  bind(listen_sd, (struct sockaddr *)&sa_serv, sizeof(sa_serv));

  listen(listen_sd, 1024);

  printf("Server ready. Listening to port '%d'.\n\n", PORT);

  client_len = sizeof(sa_cli);
  for (;;) {
    CHECK(gnutls_init(&session, GNUTLS_SERVER));
    CHECK(gnutls_priority_set(session, priority_cache));
    CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, creds));

    gnutls_certificate_server_set_request(session, GNUTLS_CERT_IGNORE);
    gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);

    sd = accept(listen_sd, (struct sockaddr *)&sa_cli, &client_len);

    printf("- connection from %s, port %d\n",
           inet_ntop(AF_INET, &sa_cli.sin_addr, topbuf, sizeof(topbuf)),
           ntohs(sa_cli.sin_port));

    gnutls_transport_set_int(session, sd);

    LOOP_CHECK(ret, gnutls_handshake(session));
    if (ret < 0) {
      close(sd);
      gnutls_deinit(session);
      fprintf(stderr, "*** Handshake has failed (%s)\n\n",
              gnutls_strerror(ret));
      continue;
    }
    printf("- Handshake was completed\n");

    for (;;) {
      LOOP_CHECK(ret, gnutls_record_recv(session, buffer, MAX_BUF));

      if (ret == 0) {
        printf("\n- Peer has closed the GnuTLS connection\n");
        break;
      } else if (ret < 0 && gnutls_error_is_fatal(ret) == 0) {
        fprintf(stderr, "*** Warning: %s\n", gnutls_strerror(ret));
      } else if (ret < 0) {
        fprintf(stderr,
                "\n*** Received corrupted "
                "data(%d). Closing the connection.\n\n",
                ret);
        break;
      } else if (ret > 0) {
        CHECK(gnutls_record_send(session, buffer, ret));
      }
    }
    printf("\n");
    LOOP_CHECK(ret, gnutls_bye(session, GNUTLS_SHUT_WR));

    close(sd);
    gnutls_deinit(session);
  }
  close(listen_sd);

  gnutls_x509_crt_deinit(cert);
  gnutls_privkey_deinit(privkey);
  gnutls_certificate_free_credentials(creds);
  gnutls_priority_deinit(priority_cache);

  gnutls_global_deinit();

  return 0;
}
```

-- 
Reply to this email directly or view it on GitLab: https://gitlab.com/gnutls/gnutls/-/issues/1412
You're receiving this email because of your account on gitlab.com.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.gnupg.org/pipermail/gnutls-devel/attachments/20221004/1452a021/attachment-0001.html>


More information about the Gnutls-devel mailing list