LCOV - differential code coverage report
Current view: top level - src/interfaces/libpq - fe-secure-common.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 79.8 % 84 67 7 3 5 2 5 31 5 26 7 32 3 6
Current Date: 2023-04-08 17:13:01 Functions: 100.0 % 4 4 2 1 1 2
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (120,180] days: 36.4 % 11 4 7 4
Legend: Lines: hit not hit (240..) days: 86.3 % 73 63 3 5 2 5 31 1 26 7 32
Function coverage date bins:
(240..) days: 66.7 % 6 4 2 1 1 2

 Age         Owner                  TLA  Line data    Source code
                                  1                 : /*-------------------------------------------------------------------------
                                  2                 :  *
                                  3                 :  * fe-secure-common.c
                                  4                 :  *
                                  5                 :  * common implementation-independent SSL support code
                                  6                 :  *
                                  7                 :  * While fe-secure.c contains the interfaces that the rest of libpq call, this
                                  8                 :  * file contains support routines that are used by the library-specific
                                  9                 :  * implementations such as fe-secure-openssl.c.
                                 10                 :  *
                                 11                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
                                 12                 :  * Portions Copyright (c) 1994, Regents of the University of California
                                 13                 :  *
                                 14                 :  * IDENTIFICATION
                                 15                 :  *    src/interfaces/libpq/fe-secure-common.c
                                 16                 :  *
                                 17                 :  *-------------------------------------------------------------------------
                                 18                 :  */
                                 19                 : 
                                 20                 : #include "postgres_fe.h"
                                 21                 : 
                                 22                 : #include <arpa/inet.h>
                                 23                 : 
                                 24                 : #include "fe-secure-common.h"
                                 25                 : 
                                 26                 : #include "libpq-int.h"
                                 27                 : #include "pqexpbuffer.h"
                                 28                 : 
                                 29                 : /*
                                 30                 :  * Check if a wildcard certificate matches the server hostname.
                                 31                 :  *
                                 32                 :  * The rule for this is:
                                 33                 :  *  1. We only match the '*' character as wildcard
                                 34                 :  *  2. We match only wildcards at the start of the string
                                 35                 :  *  3. The '*' character does *not* match '.', meaning that we match only
                                 36                 :  *     a single pathname component.
                                 37                 :  *  4. We don't support more than one '*' in a single pattern.
                                 38                 :  *
                                 39                 :  * This is roughly in line with RFC2818, but contrary to what most browsers
                                 40                 :  * appear to be implementing (point 3 being the difference)
                                 41                 :  *
                                 42                 :  * Matching is always case-insensitive, since DNS is case insensitive.
                                 43                 :  */
                                 44                 : static bool
 1898 peter_e                    45 CBC          21 : wildcard_certificate_match(const char *pattern, const char *string)
                                 46                 : {
                                 47              21 :     int         lenpat = strlen(pattern);
                                 48              21 :     int         lenstr = strlen(string);
                                 49                 : 
                                 50                 :     /* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
                                 51              21 :     if (lenpat < 3 ||
                                 52              21 :         pattern[0] != '*' ||
                                 53               3 :         pattern[1] != '.')
                                 54              18 :         return false;
                                 55                 : 
                                 56                 :     /* If pattern is longer than the string, we can never match */
                                 57               3 :     if (lenpat > lenstr)
 1898 peter_e                    58 UBC           0 :         return false;
                                 59                 : 
                                 60                 :     /*
                                 61                 :      * If string does not end in pattern (minus the wildcard), we don't match
                                 62                 :      */
 1898 peter_e                    63 CBC           3 :     if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
                                 64               1 :         return false;
                                 65                 : 
                                 66                 :     /*
                                 67                 :      * If there is a dot left of where the pattern started to match, we don't
                                 68                 :      * match (rule 3)
                                 69                 :      */
                                 70               2 :     if (strchr(string, '.') < string + lenstr - lenpat)
                                 71               1 :         return false;
                                 72                 : 
                                 73                 :     /* String ended with pattern, and didn't have a dot before, so we match */
                                 74               1 :     return true;
                                 75                 : }
                                 76                 : 
                                 77                 : /*
                                 78                 :  * Check if a name from a server's certificate matches the peer's hostname.
                                 79                 :  *
                                 80                 :  * Returns 1 if the name matches, and 0 if it does not. On error, returns
                                 81                 :  * -1, and sets the libpq error message.
                                 82                 :  *
                                 83                 :  * The name extracted from the certificate is returned in *store_name. The
                                 84                 :  * caller is responsible for freeing it.
                                 85                 :  */
                                 86                 : int
                                 87              34 : pq_verify_peer_name_matches_certificate_name(PGconn *conn,
                                 88                 :                                              const char *namedata, size_t namelen,
                                 89                 :                                              char **store_name)
                                 90                 : {
                                 91                 :     char       *name;
                                 92                 :     int         result;
 1710 tgl                        93              34 :     char       *host = conn->connhost[conn->whichhost].host;
                                 94                 : 
 1898 peter_e                    95              34 :     *store_name = NULL;
                                 96                 : 
 1710 tgl                        97              34 :     if (!(host && host[0] != '\0'))
                                 98                 :     {
  145 peter                      99 UNC           0 :         libpq_append_conn_error(conn, "host name must be specified");
 1710 tgl                       100 UIC           0 :         return -1;
                                101                 :     }
                                102                 : 
                                103                 :     /*
                                104                 :      * There is no guarantee the string returned from the certificate is
                                105                 :      * NULL-terminated, so make a copy that is.
 1898 peter_e                   106 ECB             :      */
 1898 peter_e                   107 CBC          34 :     name = malloc(namelen + 1);
 1898 peter_e                   108 GIC          34 :     if (name == NULL)
 1898 peter_e                   109 EUB             :     {
  145 peter                     110 UNC           0 :         libpq_append_conn_error(conn, "out of memory");
 1898 peter_e                   111 LBC           0 :         return -1;
 1898 peter_e                   112 ECB             :     }
 1898 peter_e                   113 GIC          34 :     memcpy(name, namedata, namelen);
                                114              34 :     name[namelen] = '\0';
                                115                 : 
                                116                 :     /*
                                117                 :      * Reject embedded NULLs in certificate common or alternative name to
 1898 peter_e                   118 ECB             :      * prevent attacks like CVE-2009-4034.
                                119                 :      */
 1898 peter_e                   120 GBC          34 :     if (namelen != strlen(name))
 1898 peter_e                   121 EUB             :     {
 1898 peter_e                   122 UBC           0 :         free(name);
  145 peter                     123 UNC           0 :         libpq_append_conn_error(conn, "SSL certificate's name contains embedded null");
 1898 peter_e                   124 LBC           0 :         return -1;
                                125                 :     }
                                126                 : 
 1898 peter_e                   127 CBC          34 :     if (pg_strcasecmp(name, host) == 0)
                                128                 :     {
 1898 peter_e                   129 ECB             :         /* Exact name match */
 1898 peter_e                   130 GIC          13 :         result = 1;
                                131                 :     }
 1898 peter_e                   132 CBC          21 :     else if (wildcard_certificate_match(name, host))
                                133                 :     {
                                134                 :         /* Matched wildcard name */
 1898 peter_e                   135 GIC           1 :         result = 1;
 1898 peter_e                   136 ECB             :     }
                                137                 :     else
                                138                 :     {
 1898 peter_e                   139 CBC          20 :         result = 0;
 1898 peter_e                   140 ECB             :     }
                                141                 : 
 1898 peter_e                   142 GIC          34 :     *store_name = name;
                                143              34 :     return result;
                                144                 : }
                                145                 : 
                                146                 : /*
                                147                 :  * Check if an IP address from a server's certificate matches the peer's
                                148                 :  * hostname (which must itself be an IPv4/6 address).
                                149                 :  *
                                150                 :  * Returns 1 if the address matches, and 0 if it does not. On error, returns
                                151                 :  * -1, and sets the libpq error message.
                                152                 :  *
                                153                 :  * A string representation of the certificate's IP address is returned in
  373 peter                     154 ECB             :  * *store_name. The caller is responsible for freeing it.
                                155                 :  */
                                156                 : int
  373 peter                     157 GIC          24 : pq_verify_peer_name_matches_certificate_ip(PGconn *conn,
                                158                 :                                            const unsigned char *ipdata,
                                159                 :                                            size_t iplen,
  373 peter                     160 ECB             :                                            char **store_name)
                                161                 : {
                                162                 :     char       *addrstr;
  373 peter                     163 GIC          24 :     int         match = 0;
                                164              24 :     char       *host = conn->connhost[conn->whichhost].host;
                                165                 :     int         family;
  373 peter                     166 ECB             :     char        tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
                                167                 :     char        sebuf[PG_STRERROR_R_BUFLEN];
                                168                 : 
  373 peter                     169 GIC          24 :     *store_name = NULL;
  373 peter                     170 EUB             : 
  373 peter                     171 GBC          24 :     if (!(host && host[0] != '\0'))
                                172                 :     {
  145 peter                     173 UNC           0 :         libpq_append_conn_error(conn, "host name must be specified");
  373 peter                     174 UIC           0 :         return -1;
                                175                 :     }
                                176                 : 
                                177                 :     /*
                                178                 :      * The data from the certificate is in network byte order. Convert our
                                179                 :      * host string to network-ordered bytes as well, for comparison. (The host
  373 peter                     180 ECB             :      * string isn't guaranteed to actually be an IP address, so if this
                                181                 :      * conversion fails we need to consider it a mismatch rather than an
                                182                 :      * error.)
                                183                 :      */
  373 peter                     184 GIC          24 :     if (iplen == 4)
  373 peter                     185 ECB             :     {
                                186                 :         /* IPv4 */
                                187                 :         struct in_addr addr;
                                188                 : 
  373 peter                     189 GIC          14 :         family = AF_INET;
                                190                 : 
                                191                 :         /*
  373 peter                     192 ECB             :          * The use of inet_aton() is deliberate; we accept alternative IPv4
                                193                 :          * address notations that are accepted by inet_aton() but not
                                194                 :          * inet_pton() as server addresses.
                                195                 :          */
  373 peter                     196 GIC          14 :         if (inet_aton(host, &addr))
                                197                 :         {
                                198               6 :             if (memcmp(ipdata, &addr.s_addr, iplen) == 0)
                                199               4 :                 match = 1;
                                200                 :         }
                                201                 :     }
                                202                 : 
                                203                 :     /*
  373 peter                     204 ECB             :      * If they don't have inet_pton(), skip this.  Then, an IPv6 address in a
                                205                 :      * certificate will cause an error.
                                206                 :      */
                                207                 : #ifdef HAVE_INET_PTON
  373 peter                     208 GIC          10 :     else if (iplen == 16)
  373 peter                     209 ECB             :     {
                                210                 :         /* IPv6 */
                                211                 :         struct in6_addr addr;
                                212                 : 
  373 peter                     213 CBC          10 :         family = AF_INET6;
  373 peter                     214 ECB             : 
  373 peter                     215 GIC          10 :         if (inet_pton(AF_INET6, host, &addr) == 1)
                                216                 :         {
                                217               6 :             if (memcmp(ipdata, &addr.s6_addr, iplen) == 0)
                                218               5 :                 match = 1;
                                219                 :         }
                                220                 :     }
                                221                 : #endif
                                222                 :     else
                                223                 :     {
  373 peter                     224 EUB             :         /*
                                225                 :          * Not IPv4 or IPv6. We could ignore the field, but leniency seems
                                226                 :          * wrong given the subject matter.
                                227                 :          */
  145 peter                     228 UNC           0 :         libpq_append_conn_error(conn, "certificate contains IP address with invalid length %zu",
                                229                 :                           iplen);
  373 peter                     230 LBC           0 :         return -1;
                                231                 :     }
  373 peter                     232 EUB             : 
                                233                 :     /* Generate a human-readable representation of the certificate's IP. */
  373 peter                     234 GBC          24 :     addrstr = pg_inet_net_ntop(family, ipdata, 8 * iplen, tmp, sizeof(tmp));
  373 peter                     235 GIC          24 :     if (!addrstr)
                                236                 :     {
  145 peter                     237 UNC           0 :         libpq_append_conn_error(conn, "could not convert certificate's IP address to string: %s",
  373 peter                     238 UIC           0 :                           strerror_r(errno, sebuf, sizeof(sebuf)));
                                239               0 :         return -1;
                                240                 :     }
                                241                 : 
  373 peter                     242 GIC          24 :     *store_name = strdup(addrstr);
                                243              24 :     return match;
                                244                 : }
                                245                 : 
 1898 peter_e                   246 ECB             : /*
                                247                 :  * Verify that the server certificate matches the hostname we connected to.
                                248                 :  *
                                249                 :  * The certificate's Common Name and Subject Alternative Names are considered.
                                250                 :  */
                                251                 : bool
 1898 peter_e                   252 GIC         103 : pq_verify_peer_name_matches_certificate(PGconn *conn)
                                253                 : {
 1710 tgl                       254             103 :     char       *host = conn->connhost[conn->whichhost].host;
                                255                 :     int         rc;
 1898 peter_e                   256             103 :     int         names_examined = 0;
 1898 peter_e                   257 CBC         103 :     char       *first_name = NULL;
 1898 peter_e                   258 ECB             : 
                                259                 :     /*
                                260                 :      * If told not to verify the peer name, don't do it. Return true
                                261                 :      * indicating that the verification was successful.
                                262                 :      */
 1898 peter_e                   263 GBC         103 :     if (strcmp(conn->sslmode, "verify-full") != 0)
                                264              67 :         return true;
                                265                 : 
                                266                 :     /* Check that we have a hostname to compare with. */
 1898 peter_e                   267 CBC          36 :     if (!(host && host[0] != '\0'))
                                268                 :     {
  145 peter                     269 UNC           0 :         libpq_append_conn_error(conn, "host name must be specified for a verified SSL connection");
 1898 peter_e                   270 UIC           0 :         return false;
                                271                 :     }
                                272                 : 
 1898 peter_e                   273 GIC          36 :     rc = pgtls_verify_peer_name_matches_certificate_guts(conn, &names_examined, &first_name);
                                274                 : 
                                275              36 :     if (rc == 0)
 1898 peter_e                   276 ECB             :     {
                                277                 :         /*
                                278                 :          * No match. Include the name from the server certificate in the error
                                279                 :          * message, to aid debugging broken configurations. If there are
                                280                 :          * multiple names, only print the first one to avoid an overly long
                                281                 :          * error message.
                                282                 :          */
 1898 peter_e                   283 CBC          13 :         if (names_examined > 1)
                                284                 :         {
  818 tgl                       285               7 :             appendPQExpBuffer(&conn->errorMessage,
  145 peter                     286 GNC           7 :                               libpq_ngettext("server certificate for \"%s\" (and %d other name) does not match host name \"%s\"",
                                287                 :                                              "server certificate for \"%s\" (and %d other names) does not match host name \"%s\"",
 1898 peter_e                   288 GIC           7 :                                              names_examined - 1),
                                289                 :                               first_name, names_examined - 1, host);
  145 peter                     290 GNC           7 :             appendPQExpBufferChar(&conn->errorMessage, '\n');
                                291                 :         }
 1898 peter_e                   292 GIC           6 :         else if (names_examined == 1)
 1898 peter_e                   293 ECB             :         {
  145 peter                     294 GNC           5 :             libpq_append_conn_error(conn, "server certificate for \"%s\" does not match host name \"%s\"",
                                295                 :                               first_name, host);
                                296                 :         }
 1898 peter_e                   297 ECB             :         else
                                298                 :         {
  145 peter                     299 GNC           1 :             libpq_append_conn_error(conn, "could not get server's host name from server certificate");
                                300                 :         }
                                301                 :     }
                                302                 : 
                                303                 :     /* clean up */
  297                           304              36 :     free(first_name);
                                305                 : 
 1898 peter_e                   306 GIC          36 :     return (rc == 1);
                                307                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a