Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/interfaces/libpq/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 7 kB image not shown  

Quelle  fe-secure-common.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * fe-secure-common.c
 *
 * common implementation-independent SSL support code
 *
 * While fe-secure.c contains the interfaces that the rest of libpq call, this
 * file contains support routines that are used by the library-specific
 * implementations such as fe-secure-openssl.c.
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *   src/interfaces/libpq/fe-secure-common.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres_fe.h"

#include <arpa/inet.h>

#include "fe-secure-common.h"

#include "libpq-int.h"
#include "pqexpbuffer.h"

/*
 * Check if a wildcard certificate matches the server hostname.
 *
 * The rule for this is:
 * 1. We only match the '*' character as wildcard
 * 2. We match only wildcards at the start of the string
 * 3. The '*' character does *not* match '.', meaning that we match only
 *    a single pathname component.
 * 4. We don't support more than one '*' in a single pattern.
 *
 * This is roughly in line with RFC2818, but contrary to what most browsers
 * appear to be implementing (point 3 being the difference)
 *
 * Matching is always case-insensitive, since DNS is case insensitive.
 */

static bool
wildcard_certificate_match(const char *pattern, const char *string)
{
 int   lenpat = strlen(pattern);
 int   lenstr = strlen(string);

 /* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
 if (lenpat < 3 ||
  pattern[0] != '*' ||
  pattern[1] != '.')
  return false;

 /* If pattern is longer than the string, we can never match */
 if (lenpat > lenstr)
  return false;

 /*
  * If string does not end in pattern (minus the wildcard), we don't match
 */

 if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
  return false;

 /*
  * If there is a dot left of where the pattern started to match, we don't
  * match (rule 3)
 */

 if (strchr(string, '.') < string + lenstr - lenpat)
  return false;

 /* String ended with pattern, and didn't have a dot before, so we match */
 return true;
}

/*
 * Check if a name from a server's certificate matches the peer's hostname.
 *
 * Returns 1 if the name matches, and 0 if it does not. On error, returns
 * -1, and sets the libpq error message.
 *
 * The name extracted from the certificate is returned in *store_name. The
 * caller is responsible for freeing it.
 */

int
pq_verify_peer_name_matches_certificate_name(PGconn *conn,
            const char *namedata, size_t namelen,
            char **store_name)
{
 char    *name;
 int   result;
 char    *host = conn->connhost[conn->whichhost].host;

 *store_name = NULL;

 if (!(host && host[0] != '\0'))
 {
  libpq_append_conn_error(conn, "host name must be specified");
  return -1;
 }

 /*
  * There is no guarantee the string returned from the certificate is
  * NULL-terminated, so make a copy that is.
 */

 name = malloc(namelen + 1);
 if (name == NULL)
 {
  libpq_append_conn_error(conn, "out of memory");
  return -1;
 }
 memcpy(name, namedata, namelen);
 name[namelen] = '\0';

 /*
  * Reject embedded NULLs in certificate common or alternative name to
  * prevent attacks like CVE-2009-4034.
 */

 if (namelen != strlen(name))
 {
  free(name);
  libpq_append_conn_error(conn, "SSL certificate's name contains embedded null");
  return -1;
 }

 if (pg_strcasecmp(name, host) == 0)
 {
  /* Exact name match */
  result = 1;
 }
 else if (wildcard_certificate_match(name, host))
 {
  /* Matched wildcard name */
  result = 1;
 }
 else
 {
  result = 0;
 }

 *store_name = name;
 return result;
}

/*
 * Check if an IP address from a server's certificate matches the peer's
 * hostname (which must itself be an IPv4/6 address).
 *
 * Returns 1 if the address matches, and 0 if it does not. On error, returns
 * -1, and sets the libpq error message.
 *
 * A string representation of the certificate's IP address is returned in
 * *store_name. The caller is responsible for freeing it.
 */

int
pq_verify_peer_name_matches_certificate_ip(PGconn *conn,
             const unsigned char *ipdata,
             size_t iplen,
             char **store_name)
{
 char    *addrstr;
 int   match = 0;
 char    *host = conn->connhost[conn->whichhost].host;
 int   family;
 char  tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
 char  sebuf[PG_STRERROR_R_BUFLEN];

 *store_name = NULL;

 if (!(host && host[0] != '\0'))
 {
  libpq_append_conn_error(conn, "host name must be specified");
  return -1;
 }

 /*
  * The data from the certificate is in network byte order. Convert our
  * host string to network-ordered bytes as well, for comparison. (The host
  * string isn't guaranteed to actually be an IP address, so if this
  * conversion fails we need to consider it a mismatch rather than an
  * error.)
 */

 if (iplen == 4)
 {
  /* IPv4 */
  struct in_addr addr;

  family = AF_INET;

  /*
   * The use of inet_aton() is deliberate; we accept alternative IPv4
   * address notations that are accepted by inet_aton() but not
   * inet_pton() as server addresses.
 */

  if (inet_aton(host, &addr))
  {
   if (memcmp(ipdata, &addr.s_addr, iplen) == 0)
    match = 1;
  }
 }

 /*
  * If they don't have inet_pton(), skip this.  Then, an IPv6 address in a
  * certificate will cause an error.
 */

#ifdef HAVE_INET_PTON
 else if (iplen == 16)
 {
  /* IPv6 */
  struct in6_addr addr;

  family = AF_INET6;

  if (inet_pton(AF_INET6, host, &addr) == 1)
  {
   if (memcmp(ipdata, &addr.s6_addr, iplen) == 0)
    match = 1;
  }
 }
#endif
 else
 {
  /*
   * Not IPv4 or IPv6. We could ignore the field, but leniency seems
   * wrong given the subject matter.
 */

  libpq_append_conn_error(conn, "certificate contains IP address with invalid length %zu",
        iplen);
  return -1;
 }

 /* Generate a human-readable representation of the certificate's IP. */
 addrstr = pg_inet_net_ntop(family, ipdata, 8 * iplen, tmp, sizeof(tmp));
 if (!addrstr)
 {
  libpq_append_conn_error(conn, "could not convert certificate's IP address to string: %s",
        strerror_r(errno, sebuf, sizeof(sebuf)));
  return -1;
 }

 *store_name = strdup(addrstr);
 return match;
}

/*
 * Verify that the server certificate matches the hostname we connected to.
 *
 * The certificate's Common Name and Subject Alternative Names are considered.
 */

bool
pq_verify_peer_name_matches_certificate(PGconn *conn)
{
 char    *host = conn->connhost[conn->whichhost].host;
 int   rc;
 int   names_examined = 0;
 char    *first_name = NULL;

 /*
  * If told not to verify the peer name, don't do it. Return true
  * indicating that the verification was successful.
 */

 if (strcmp(conn->sslmode, "verify-full") != 0)
  return true;

 /* Check that we have a hostname to compare with. */
 if (!(host && host[0] != '\0'))
 {
  libpq_append_conn_error(conn, "host name must be specified for a verified SSL connection");
  return false;
 }

 rc = pgtls_verify_peer_name_matches_certificate_guts(conn, &names_examined, &first_name);

 if (rc == 0)
 {
  /*
   * No match. Include the name from the server certificate in the error
   * message, to aid debugging broken configurations. If there are
   * multiple names, only print the first one to avoid an overly long
   * error message.
 */

  if (names_examined > 1)
  {
   appendPQExpBuffer(&conn->errorMessage,
         libpq_ngettext("server certificate for \"%s\" (and %d other name) does not match host name \"%s\"",
            "server certificate for \"%s\" (and %d other names) does not match host name \"%s\"",
            names_examined - 1),
         first_name, names_examined - 1, host);
   appendPQExpBufferChar(&conn->errorMessage, '\n');
  }
  else if (names_examined == 1)
  {
   libpq_append_conn_error(conn, "server certificate for \"%s\" does not match host name \"%s\"",
         first_name, host);
  }
  else
  {
   libpq_append_conn_error(conn, "could not get server's host name from server certificate");
  }
 }

 /* clean up */
 free(first_name);

 return (rc == 1);
}

Messung V0.5 in Prozent
C=89 H=95 G=91

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.