LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-secure-common.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 50 64 78.1 %
Date: 2019-11-21 14:06:36 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          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-2019, 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 "fe-secure-common.h"
      23             : 
      24             : #include "libpq-int.h"
      25             : #include "pqexpbuffer.h"
      26             : 
      27             : /*
      28             :  * Check if a wildcard certificate matches the server hostname.
      29             :  *
      30             :  * The rule for this is:
      31             :  *  1. We only match the '*' character as wildcard
      32             :  *  2. We match only wildcards at the start of the string
      33             :  *  3. The '*' character does *not* match '.', meaning that we match only
      34             :  *     a single pathname component.
      35             :  *  4. We don't support more than one '*' in a single pattern.
      36             :  *
      37             :  * This is roughly in line with RFC2818, but contrary to what most browsers
      38             :  * appear to be implementing (point 3 being the difference)
      39             :  *
      40             :  * Matching is always case-insensitive, since DNS is case insensitive.
      41             :  */
      42             : static bool
      43          32 : wildcard_certificate_match(const char *pattern, const char *string)
      44             : {
      45          32 :     int         lenpat = strlen(pattern);
      46          32 :     int         lenstr = strlen(string);
      47             : 
      48             :     /* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
      49          64 :     if (lenpat < 3 ||
      50          38 :         pattern[0] != '*' ||
      51           6 :         pattern[1] != '.')
      52          26 :         return false;
      53             : 
      54             :     /* If pattern is longer than the string, we can never match */
      55           6 :     if (lenpat > lenstr)
      56           0 :         return false;
      57             : 
      58             :     /*
      59             :      * If string does not end in pattern (minus the wildcard), we don't match
      60             :      */
      61           6 :     if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
      62           2 :         return false;
      63             : 
      64             :     /*
      65             :      * If there is a dot left of where the pattern started to match, we don't
      66             :      * match (rule 3)
      67             :      */
      68           4 :     if (strchr(string, '.') < string + lenstr - lenpat)
      69           2 :         return false;
      70             : 
      71             :     /* String ended with pattern, and didn't have a dot before, so we match */
      72           2 :     return true;
      73             : }
      74             : 
      75             : /*
      76             :  * Check if a name from a server's certificate matches the peer's hostname.
      77             :  *
      78             :  * Returns 1 if the name matches, and 0 if it does not. On error, returns
      79             :  * -1, and sets the libpq error message.
      80             :  *
      81             :  * The name extracted from the certificate is returned in *store_name. The
      82             :  * caller is responsible for freeing it.
      83             :  */
      84             : int
      85          44 : pq_verify_peer_name_matches_certificate_name(PGconn *conn,
      86             :                                              const char *namedata, size_t namelen,
      87             :                                              char **store_name)
      88             : {
      89             :     char       *name;
      90             :     int         result;
      91          44 :     char       *host = conn->connhost[conn->whichhost].host;
      92             : 
      93          44 :     *store_name = NULL;
      94             : 
      95          44 :     if (!(host && host[0] != '\0'))
      96             :     {
      97           0 :         printfPQExpBuffer(&conn->errorMessage,
      98           0 :                           libpq_gettext("host name must be specified\n"));
      99           0 :         return -1;
     100             :     }
     101             : 
     102             :     /*
     103             :      * There is no guarantee the string returned from the certificate is
     104             :      * NULL-terminated, so make a copy that is.
     105             :      */
     106          44 :     name = malloc(namelen + 1);
     107          44 :     if (name == NULL)
     108             :     {
     109           0 :         printfPQExpBuffer(&conn->errorMessage,
     110           0 :                           libpq_gettext("out of memory\n"));
     111           0 :         return -1;
     112             :     }
     113          44 :     memcpy(name, namedata, namelen);
     114          44 :     name[namelen] = '\0';
     115             : 
     116             :     /*
     117             :      * Reject embedded NULLs in certificate common or alternative name to
     118             :      * prevent attacks like CVE-2009-4034.
     119             :      */
     120          44 :     if (namelen != strlen(name))
     121             :     {
     122           0 :         free(name);
     123           0 :         printfPQExpBuffer(&conn->errorMessage,
     124           0 :                           libpq_gettext("SSL certificate's name contains embedded null\n"));
     125           0 :         return -1;
     126             :     }
     127             : 
     128          44 :     if (pg_strcasecmp(name, host) == 0)
     129             :     {
     130             :         /* Exact name match */
     131          12 :         result = 1;
     132             :     }
     133          32 :     else if (wildcard_certificate_match(name, host))
     134             :     {
     135             :         /* Matched wildcard name */
     136           2 :         result = 1;
     137             :     }
     138             :     else
     139             :     {
     140          30 :         result = 0;
     141             :     }
     142             : 
     143          44 :     *store_name = name;
     144          44 :     return result;
     145             : }
     146             : 
     147             : /*
     148             :  * Verify that the server certificate matches the hostname we connected to.
     149             :  *
     150             :  * The certificate's Common Name and Subject Alternative Names are considered.
     151             :  */
     152             : bool
     153          78 : pq_verify_peer_name_matches_certificate(PGconn *conn)
     154             : {
     155          78 :     char       *host = conn->connhost[conn->whichhost].host;
     156             :     int         rc;
     157          78 :     int         names_examined = 0;
     158          78 :     char       *first_name = NULL;
     159             : 
     160             :     /*
     161             :      * If told not to verify the peer name, don't do it. Return true
     162             :      * indicating that the verification was successful.
     163             :      */
     164          78 :     if (strcmp(conn->sslmode, "verify-full") != 0)
     165          50 :         return true;
     166             : 
     167             :     /* Check that we have a hostname to compare with. */
     168          28 :     if (!(host && host[0] != '\0'))
     169             :     {
     170           0 :         printfPQExpBuffer(&conn->errorMessage,
     171           0 :                           libpq_gettext("host name must be specified for a verified SSL connection\n"));
     172           0 :         return false;
     173             :     }
     174             : 
     175          28 :     rc = pgtls_verify_peer_name_matches_certificate_guts(conn, &names_examined, &first_name);
     176             : 
     177          28 :     if (rc == 0)
     178             :     {
     179             :         /*
     180             :          * No match. Include the name from the server certificate in the error
     181             :          * message, to aid debugging broken configurations. If there are
     182             :          * multiple names, only print the first one to avoid an overly long
     183             :          * error message.
     184             :          */
     185          14 :         if (names_examined > 1)
     186             :         {
     187          12 :             printfPQExpBuffer(&conn->errorMessage,
     188           6 :                               libpq_ngettext("server certificate for \"%s\" (and %d other name) does not match host name \"%s\"\n",
     189             :                                              "server certificate for \"%s\" (and %d other names) does not match host name \"%s\"\n",
     190           6 :                                              names_examined - 1),
     191             :                               first_name, names_examined - 1, host);
     192             :         }
     193           8 :         else if (names_examined == 1)
     194             :         {
     195          12 :             printfPQExpBuffer(&conn->errorMessage,
     196           6 :                               libpq_gettext("server certificate for \"%s\" does not match host name \"%s\"\n"),
     197             :                               first_name, host);
     198             :         }
     199             :         else
     200             :         {
     201           2 :             printfPQExpBuffer(&conn->errorMessage,
     202           2 :                               libpq_gettext("could not get server's host name from server certificate\n"));
     203             :         }
     204             :     }
     205             : 
     206             :     /* clean up */
     207          28 :     if (first_name)
     208          26 :         free(first_name);
     209             : 
     210          28 :     return (rc == 1);
     211             : }

Generated by: LCOV version 1.13