Your message dated Sat, 29 Apr 2023 10:54:14 +0100
with message-id 
<502b8fb37ece620c9723446611a9287974ba5a0c.ca...@adam-barratt.org.uk>
and subject line Closing p-u requests for fixes included in 11.7
has caused the Debian Bug report #1031948,
regarding bullseye-pu: package libgit2/1.1.0+dfsg.1-4+deb11u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1031948: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1031948
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bullseye
User: [email protected]
Usertags: pu
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:libgit2

After fixing CVE-2023-22742 for LTS and ELTS, I'd like to see
this CVE also fixed in stable, for consistency.

The CVE is an inproper ssh certificate validation vulnerabilty,
which allows man-in-the-middle attacks.

[ Tests ]
I've manually tested the patch (checking if git poperations still
work using the ssh transport.

Autopkgtests are green too.

[ Risks ]
Manual tests and test suite works ok.

[ Checklist ]
  [X] *all* changes are documented in the d/changelog
  [X] I reviewed all changes and I approve them
  [X] attach debdiff against the package in (old)stable
  [X] the issue is verified as fixed in unstable

[ Changes ]
The change is a backport of the upstream fix:
https://github.com/libgit2/libgit2/commit/cd6f679af401eda1f172402006ef8265f8bd58ea
diff -Nru libgit2-1.1.0+dfsg.1/debian/changelog 
libgit2-1.1.0+dfsg.1/debian/changelog
--- libgit2-1.1.0+dfsg.1/debian/changelog       2021-01-06 18:28:58.000000000 
+0100
+++ libgit2-1.1.0+dfsg.1/debian/changelog       2023-02-24 16:11:52.000000000 
+0100
@@ -1,3 +1,10 @@
+libgit2 (1.1.0+dfsg.1-4+deb11u1) bullseye-security; urgency=high
+
+  * Non-maintainer upload by the Security Team.
+  * Backport patch for CVE-2023-22742 (Closes: #1029368)
+
+ -- Tobias Frost <[email protected]>  Fri, 24 Feb 2023 16:11:52 +0100
+
 libgit2 (1.1.0+dfsg.1-4) unstable; urgency=medium
 
   * Source-only upload for migration.
diff -Nru libgit2-1.1.0+dfsg.1/debian/patches/CVE-2023-22742.patch 
libgit2-1.1.0+dfsg.1/debian/patches/CVE-2023-22742.patch
--- libgit2-1.1.0+dfsg.1/debian/patches/CVE-2023-22742.patch    1970-01-01 
01:00:00.000000000 +0100
+++ libgit2-1.1.0+dfsg.1/debian/patches/CVE-2023-22742.patch    2023-02-24 
16:11:52.000000000 +0100
@@ -0,0 +1,523 @@
+Description: CVE-2023-22742 - fails to verify SSH keys by default
+ CVE-Description: libgit2 is a cross-platform, linkable library
+ implementation of Git. When using an SSH remote with the optional libssh2
+ backend, libgit2 does not perform certificate checking by default. Prior
+ versions of libgit2 require the caller to set the `certificate_check` field of
+ libgit2's `git_remote_callbacks` structure - if a certificate check callback 
is
+ not set, libgit2 does not perform any certificate checking. This means that by
+ default - without configuring a certificate check callback, clients will not
+ perform validation on the server SSH keys and may be subject to a
+ man-in-the-middle attack. 
+ This is a backport of the upstream fix to the Debian stretch version.
+Origin: https://github.com/libgit2/libgit2/pull/6449
+Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1029368
+Forwarded: <URL|no|not-needed, useless if you have a Bug field, optional>
+Last-Update: 2023-02-21 <YYYY-MM-DD, last update of the meta-information, 
optional>
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+--- a/src/transports/ssh.c
++++ b/src/transports/ssh.c
+@@ -475,15 +475,122 @@
+       return 0;
+ }
+ 
++
++#define KNOWN_HOSTS_FILE ".ssh/known_hosts"
++
++/*
++ * Load the known_hosts file.
++ *
++ * Returns success but leaves the output NULL if we couldn't find the file.
++ */
++static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION 
*session)
++{
++      git_buf path = GIT_BUF_INIT;
++      LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
++      int error;
++      char *home = getenv("HOME");
++
++      assert(hosts);
++
++      if (!home)
++              return GIT_ENOTFOUND;
++
++
++
++      if ((error = git_buf_join(&path, '/', home, KNOWN_HOSTS_FILE)) < 0)
++              goto out;
++
++      if ((known_hosts = libssh2_knownhost_init(session)) == NULL) {
++              ssh_error(session, "error initializing known hosts");
++              error = -1;
++              goto out;
++      }
++
++      /*
++       * Try to read the file and consider not finding it as not trusting the
++       * host rather than an error.
++       */
++      error = libssh2_knownhost_readfile(known_hosts, path.ptr, 
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
++      if (error == LIBSSH2_ERROR_FILE)
++              error = 0;
++      if (error < 0)
++              ssh_error(session, "error reading known_hosts");
++
++out:
++      *hosts = known_hosts;
++
++      git_buf_free(&path);
++
++      return error;
++}
++
++static const char *hostkey_type_to_string(int type)
++{
++      switch (type) {
++      case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
++              return "ssh-rsa";
++      case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
++              return "ssh-dss";
++#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
++      case LIBSSH2_KNOWNHOST_KEY_ECDSA_256:
++              return "ecdsa-sha2-nistp256";
++      case LIBSSH2_KNOWNHOST_KEY_ECDSA_384:
++              return "ecdsa-sha2-nistp384";
++      case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
++              return "ecdsa-sha2-nistp521";
++#endif
++#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
++      case LIBSSH2_KNOWNHOST_KEY_ED25519:
++              return "ssh-ed25519";
++#endif
++      }
++
++      return NULL;
++}
++
++/*
++ * We figure out what kind of key we want to ask the remote for by trying to
++ * look it up with a nonsense key and using that mismatch to figure out what 
key
++ * we do have stored for the host.
++ *
++ * Returns the string to pass to libssh2_session_method_pref or NULL if we 
were
++ * unable to find anything or an error happened.
++ */
++static const char *find_hostkey_preference(LIBSSH2_KNOWNHOSTS *known_hosts, 
const char *hostname, int port)
++{
++      struct libssh2_knownhost *host = NULL;
++      /* Specify no key type so we don't filter on that */
++      int type = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW;
++      const char key = '\0';
++      int error;
++
++      /*
++       * In case of mismatch, we can find the type of key from known_hosts in
++       * the returned host's information as it means that an entry was found
++       * but our nonsense key obviously didn't match.
++       */
++      error = libssh2_knownhost_checkp(known_hosts, hostname, port, &key, 1, 
type, &host);
++      if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH)
++              return hostkey_type_to_string(host->typemask & 
LIBSSH2_KNOWNHOST_KEY_MASK);
++
++      return NULL;
++}
++
+ static int _git_ssh_session_create(
+       LIBSSH2_SESSION** session,
++      LIBSSH2_KNOWNHOSTS **hosts,
++      const char *hostname,
++      int port,
+       git_stream *io)
+ {
+       int rc = 0;
+       LIBSSH2_SESSION* s;
++      LIBSSH2_KNOWNHOSTS *known_hosts;
+       git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, 
parent);
++      const char *keytype = NULL;
+ 
+       assert(session);
++      assert(hosts);
+ 
+       s = libssh2_session_init();
+       if (!s) {
+@@ -491,21 +598,224 @@
+               return -1;
+       }
+ 
++      if ((rc = load_known_hosts(&known_hosts, s)) < 0) {
++              ssh_error(s, "error loading known_hosts");
++              libssh2_session_free(s);
++              return -1;
++      }
++
++      if ((keytype = find_hostkey_preference(known_hosts, hostname, port)) != 
NULL) {
++              do {
++                      rc = libssh2_session_method_pref(s, 
LIBSSH2_METHOD_HOSTKEY, keytype);
++              } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == 
rc);
++              if (rc != LIBSSH2_ERROR_NONE) {
++                      ssh_error(s, "failed to set hostkey preference");
++                      goto on_error;
++              }
++      }
++
+       do {
+               rc = libssh2_session_handshake(s, socket->s);
+       } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+ 
+       if (rc != LIBSSH2_ERROR_NONE) {
+               ssh_error(s, "failed to start SSH session");
+-              libssh2_session_free(s);
+-              return -1;
++              goto on_error;
+       }
+ 
+       libssh2_session_set_blocking(s, 1);
+ 
+       *session = s;
++      *hosts = known_hosts;
+ 
+       return 0;
++
++on_error:
++      libssh2_knownhost_free(known_hosts);
++      libssh2_session_free(s);
++      return -1;
++}
++
++
++/*
++ * Returns the typemask argument to pass to libssh2_knownhost_check{,p} based 
on
++ * the type of key that libssh2_session_hostkey returns.
++ */
++static int fingerprint_type_mask(int keytype)
++{
++      int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW;
++      return mask;
++
++      switch (keytype) {
++      case LIBSSH2_HOSTKEY_TYPE_RSA:
++              mask |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
++              break;
++      case LIBSSH2_HOSTKEY_TYPE_DSS:
++              mask |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
++              break;
++#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
++      case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
++              mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
++              break;
++      case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
++              mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
++              break;
++      case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
++              mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
++              break;
++#endif
++#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
++      case LIBSSH2_HOSTKEY_TYPE_ED25519:
++              mask |= LIBSSH2_KNOWNHOST_KEY_ED25519;
++              break;
++#endif
++      }
++
++      return mask;
++}
++
++/*
++ * Check the host against the user's known_hosts file.
++ *
++ * Returns 1/0 for valid/''not-valid or <0 for an error
++ */
++static int check_against_known_hosts(
++      LIBSSH2_SESSION *session,
++      LIBSSH2_KNOWNHOSTS *known_hosts,
++      const char *hostname,
++      int port,
++      const char *key,
++      size_t key_len,
++      int key_type)
++{
++      int check, typemask, ret = 0;
++      struct libssh2_knownhost *host = NULL;
++
++      if (known_hosts == NULL)
++              return 0;
++
++      typemask = fingerprint_type_mask(key_type);
++      check = libssh2_knownhost_checkp(known_hosts, hostname, port, key, 
key_len, typemask, &host);
++      if (check == LIBSSH2_KNOWNHOST_CHECK_FAILURE) {
++              ssh_error(session, "error checking for known host");
++              return -1;
++      }
++
++      ret = check == LIBSSH2_KNOWNHOST_CHECK_MATCH ? 1 : 0;
++
++      return ret;
++}
++
++/*
++ * Perform the check for the session's certificate against known hosts if
++ * possible and then ask the user if they have a callback.
++ *
++ * Returns 1/0 for valid/not-valid or <0 for an error
++ */
++static int check_certificate(
++      LIBSSH2_SESSION *session,
++      LIBSSH2_KNOWNHOSTS *known_hosts,
++      git_transport_certificate_check_cb check_cb,
++      void *check_cb_payload,
++      const char *host,
++      int port)
++{
++      git_cert_hostkey cert = {{ 0 }};
++      const char *key;
++      size_t cert_len;
++      int cert_type, cert_valid = 0, error = 0;
++
++      if ((key = libssh2_session_hostkey(session, &cert_len, &cert_type)) == 
NULL) {
++              ssh_error(session, "failed to retrieve hostkey");
++              return -1;
++      }
++
++      if ((cert_valid = check_against_known_hosts(session, known_hosts, host, 
port, key, cert_len, cert_type)) < 0)
++              return -1;
++
++      cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
++      if (key != NULL) {
++              cert.type |= GIT_CERT_SSH_RAW;
++              cert.hostkey = key;
++              cert.hostkey_len = cert_len;
++              switch (cert_type) {
++              case LIBSSH2_HOSTKEY_TYPE_RSA:
++                      cert.raw_type = GIT_CERT_SSH_RAW_TYPE_RSA;
++                      break;
++              case LIBSSH2_HOSTKEY_TYPE_DSS:
++                      cert.raw_type = GIT_CERT_SSH_RAW_TYPE_DSS;
++                      break;
++
++#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
++              case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
++                      cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256;
++                      break;
++              case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
++                      cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384;
++                      break;
++              case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
++                      cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521;
++                      break;
++#endif
++
++#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
++              case LIBSSH2_HOSTKEY_TYPE_ED25519:
++                      cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ED25519;
++                      break;
++#endif
++              default:
++                      cert.raw_type = GIT_CERT_SSH_RAW_TYPE_UNKNOWN;
++              }
++      }
++
++#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
++      key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
++      if (key != NULL) {
++              cert.type |= GIT_CERT_SSH_SHA256;
++              memcpy(&cert.hash_sha256, key, 32);
++      }
++#endif
++
++      key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
++      if (key != NULL) {
++              cert.type |= GIT_CERT_SSH_SHA1;
++              memcpy(&cert.hash_sha1, key, 20);
++      }
++
++      key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
++      if (key != NULL) {
++              cert.type |= GIT_CERT_SSH_MD5;
++              memcpy(&cert.hash_md5, key, 16);
++      }
++
++      if (cert.type == 0) {
++              git_error_set(GITERR_SSH, "unable to get the host key");
++              return -1;
++      }
++
++      git_error_clear();
++      error = 0;
++      if (!cert_valid) {
++              git_error_set(GITERR_SSH, "invalid or unknown remote ssh 
hostkey");
++              error = GIT_ECERTIFICATE;
++      }
++
++      if (check_cb != NULL) {
++              git_cert_hostkey *cert_ptr = &cert;
++              git_error_state previous_error = {0};
++
++              git_error_state_capture(&previous_error, error);
++              error = check_cb((git_cert *) cert_ptr, cert_valid, host, 
check_cb_payload);
++              if (error == GIT_PASSTHROUGH) {
++                      error = git_error_state_restore(&previous_error);
++              } else if (error < 0 && !git_error_last()) {
++                      git_error_set(GITERR_NET, "user canceled hostkey 
check");
++              }
++
++              git_error_state_free(&previous_error);
++      }
++
++      return error;
+ }
+ 
+ #define SSH_DEFAULT_PORT "22"
+@@ -517,12 +827,13 @@
+       git_smart_subtransport_stream **stream)
+ {
+       git_net_url urldata = GIT_NET_URL_INIT;
+-      int auth_methods, error = 0;
++      int auth_methods, error = 0, iport;
+       size_t i;
+       ssh_stream *s;
+       git_credential *cred = NULL;
+       LIBSSH2_SESSION* session=NULL;
+       LIBSSH2_CHANNEL* channel=NULL;
++      LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
+ 
+       t->current_stream = NULL;
+ 
+@@ -557,55 +868,20 @@
+           (error = git_stream_connect(s->io)) < 0)
+               goto done;
+ 
+-      if ((error = _git_ssh_session_create(&session, s->io)) < 0)
+-              goto done;
+-
+-      if (t->owner->certificate_check_cb != NULL) {
+-              git_cert_hostkey cert = {{ 0 }}, *cert_ptr;
+-              const char *key;
+-
+-              cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
+-
+-#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
+-              key = libssh2_hostkey_hash(session, 
LIBSSH2_HOSTKEY_HASH_SHA256);
+-              if (key != NULL) {
+-                      cert.type |= GIT_CERT_SSH_SHA256;
+-                      memcpy(&cert.hash_sha256, key, 32);
+-              }
+-#endif
+-
+-              key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
+-              if (key != NULL) {
+-                      cert.type |= GIT_CERT_SSH_SHA1;
+-                      memcpy(&cert.hash_sha1, key, 20);
+-              }
+-
+-              key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
+-              if (key != NULL) {
+-                      cert.type |= GIT_CERT_SSH_MD5;
+-                      memcpy(&cert.hash_md5, key, 16);
+-              }
+-
+-              if (cert.type == 0) {
+-                      git_error_set(GIT_ERROR_SSH, "unable to get the host 
key");
+-                      error = -1;
+-                      goto done;
+-              }
+-
+-              /* We don't currently trust any hostkeys */
+-              git_error_clear();
+-
+-              cert_ptr = &cert;
++      /*
++       * Try to parse the port as a number, if we can't then fall back to
++       * default. It would be nice if we could get the port that was resolved
++       * as part of the stream connection, but that's not something that's
++       * exposed.
++       */
++      iport = atol(urldata.port);
++      if(iport == 0) iport = -1;
+ 
+-              error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 
0, urldata.host, t->owner->message_cb_payload);
+-
+-              if (error < 0 && error != GIT_PASSTHROUGH) {
+-                      if (!git_error_last())
+-                              git_error_set(GIT_ERROR_NET, "user cancelled 
hostkey check");
++      if ((error = _git_ssh_session_create(&session, &known_hosts, 
urldata.host, iport, s->io)) < 0)
++              goto done;
+ 
+-                      goto done;
+-              }
+-      }
++      if ((error = check_certificate(session, known_hosts, 
t->owner->certificate_check_cb, t->owner->message_cb_payload, urldata.host, 
iport)) < 0)
++              goto done;
+ 
+       /* we need the username to ask for auth methods */
+       if (!urldata.username) {
+@@ -677,6 +953,9 @@
+       if (error < 0) {
+               ssh_stream_free(*stream);
+ 
++              if (known_hosts)
++                      libssh2_knownhost_free(known_hosts);
++
+               if (session)
+                       libssh2_session_free(session);
+       }
+--- a/include/git2/cert.h
++++ b/include/git2/cert.h
+@@ -80,8 +80,29 @@
+       GIT_CERT_SSH_SHA1 = (1 << 1),
+       /** SHA-256 is available */
+       GIT_CERT_SSH_SHA256 = (1 << 2),
++      /** Raw hostkey is available */
++      GIT_CERT_SSH_RAW = (1 << 3)
+ } git_cert_ssh_t;
+ 
++
++typedef enum {
++      /** The raw key is of an unknown type. */
++      GIT_CERT_SSH_RAW_TYPE_UNKNOWN = 0,
++      /** The raw key is an RSA key. */
++      GIT_CERT_SSH_RAW_TYPE_RSA = 1,
++      /** The raw key is a DSS key. */
++      GIT_CERT_SSH_RAW_TYPE_DSS = 2,
++      /** The raw key is a ECDSA 256 key. */
++      GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256 = 3,
++      /** The raw key is a ECDSA 384 key. */
++      GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384 = 4,
++      /** The raw key is a ECDSA 521 key. */
++      GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521 = 5,
++      /** The raw key is a ED25519 key. */
++      GIT_CERT_SSH_RAW_TYPE_KEY_ED25519 = 6
++} git_cert_ssh_raw_type_t;
++
++
+ /**
+  * Hostkey information taken from libssh2
+  */
+@@ -111,6 +132,28 @@
+        * have the SHA-256 hash of the hostkey.
+        */
+       unsigned char hash_sha256[32];
++
++
++      /**
++       * Raw hostkey type. If `type` has `GIT_CERT_SSH_RAW` set, this will
++       * have the type of the raw hostkey.
++       */
++      git_cert_ssh_raw_type_t raw_type;
++
++      /**
++       * Pointer to the raw hostkey. If `type` has `GIT_CERT_SSH_RAW` set,
++       * this will have the raw contents of the hostkey.
++       */
++      const char *hostkey;
++
++      /**
++       * Raw hostkey length. If `type` has `GIT_CERT_SSH_RAW` set, this will
++       * have the length of the raw contents of the hostkey.
++       */
++      size_t hostkey_len;
++
++
++
+ } git_cert_hostkey;
+ 
+ /**
diff -Nru libgit2-1.1.0+dfsg.1/debian/patches/series 
libgit2-1.1.0+dfsg.1/debian/patches/series
--- libgit2-1.1.0+dfsg.1/debian/patches/series  2020-12-06 23:46:54.000000000 
+0100
+++ libgit2-1.1.0+dfsg.1/debian/patches/series  2023-02-24 13:05:26.000000000 
+0100
@@ -1,2 +1,3 @@
 disable-online-tests.patch
 enable-repro-builds.patch
+CVE-2023-22742.patch

--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 11.7

Hi,

Each of the updates referred to in these requests was included in this
morning's 11.7 point release.

Regards,

Adam

--- End Message ---

Reply via email to