SSL libpq patch applied - Mailing list pgsql-patches

From Bruce Momjian
Subject SSL libpq patch applied
Date
Msg-id 200802140349.m1E3n7l20922@momjian.us
Whole thread Raw
List pgsql-patches
I have applied the attached patch which renames a libpq NOT_USED SSL
function to verify_peer_name_matches_certificate(), clarifies some of
the function's variables and logic, and updates a comment.  This should
make SSL improvements easier in the future.

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://postgres.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: src/interfaces/libpq/fe-secure.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v
retrieving revision 1.102
diff -c -c -r1.102 fe-secure.c
*** src/interfaces/libpq/fe-secure.c    29 Jan 2008 02:03:39 -0000    1.102
--- src/interfaces/libpq/fe-secure.c    14 Feb 2008 03:46:30 -0000
***************
*** 143,149 ****
  #endif

  #ifdef NOT_USED
! static int    verify_peer(PGconn *);
  #endif
  static int    verify_cb(int ok, X509_STORE_CTX *ctx);
  static int    client_cert_cb(SSL *, X509 **, EVP_PKEY **);
--- 143,149 ----
  #endif

  #ifdef NOT_USED
! static int    verify_peer_name_matches_certificate(PGconn *);
  #endif
  static int    verify_cb(int ok, X509_STORE_CTX *ctx);
  static int    client_cert_cb(SSL *, X509 **, EVP_PKEY **);
***************
*** 498,515 ****
   *    Verify that common name resolves to peer.
   */
  static int
! verify_peer(PGconn *conn)
  {
!     struct hostent *h = NULL;
!     struct sockaddr addr;
!     struct sockaddr_in *sin;
      ACCEPT_TYPE_ARG3 len;
      char      **s;
      unsigned long l;

!     /* get the address on the other side of the socket */
!     len = sizeof(addr);
!     if (getpeername(conn->sock, &addr, &len) == -1)
      {
          char        sebuf[256];

--- 498,515 ----
   *    Verify that common name resolves to peer.
   */
  static int
! verify_peer_name_matches_certificate(PGconn *conn)
  {
!     struct hostent *cn_hostentry = NULL;
!     struct sockaddr server_addr;
!     struct sockaddr_in *sin (struct sockaddr_in *) &server_addr;
      ACCEPT_TYPE_ARG3 len;
      char      **s;
      unsigned long l;

!     /* Get the address on the other side of the socket. */
!     len = sizeof(server_addr);
!     if (getpeername(conn->sock, &server_addr, &len) == -1)
      {
          char        sebuf[256];

***************
*** 519,528 ****
          return -1;
      }

!     /* weird, but legal case */
!     if (addr.sa_family == AF_UNIX)
!         return 0;

      {
          struct hostent hpstr;
          char        buf[BUFSIZ];
--- 519,532 ----
          return -1;
      }

!     if (server_addr.sa_family != AF_INET)
!     {
!         printfPQExpBuffer(&conn->errorMessage,
!                           libpq_gettext("unsupported protocol\n"));
!         return -1;
!     }

+     /* Get the IP addresses of the certificate's common name (CN) */
      {
          struct hostent hpstr;
          char        buf[BUFSIZ];
***************
*** 534,544 ****
           * convert the pqGethostbyname() function call to use getaddrinfo().
           */
          pqGethostbyname(conn->peer_cn, &hpstr, buf, sizeof(buf),
!                         &h, &herrno);
      }

!     /* what do we know about the peer's common name? */
!     if (h == NULL)
      {
          printfPQExpBuffer(&conn->errorMessage,
            libpq_gettext("could not get information about host \"%s\": %s\n"),
--- 538,548 ----
           * convert the pqGethostbyname() function call to use getaddrinfo().
           */
          pqGethostbyname(conn->peer_cn, &hpstr, buf, sizeof(buf),
!                         &cn_hostentry, &herrno);
      }

!     /* Did we get an IP address? */
!     if (cn_hostentry == NULL)
      {
          printfPQExpBuffer(&conn->errorMessage,
            libpq_gettext("could not get information about host \"%s\": %s\n"),
***************
*** 546,598 ****
          return -1;
      }

!     /* does the address match? */
!     switch (addr.sa_family)
!     {
!         case AF_INET:
!             sin = (struct sockaddr_in *) & addr;
!             for (s = h->h_addr_list; *s != NULL; s++)
!             {
!                 if (!memcmp(&sin->sin_addr.s_addr, *s, h->h_length))
!                     return 0;
!             }
!             break;
!
!         default:
!             printfPQExpBuffer(&conn->errorMessage,
!                               libpq_gettext("unsupported protocol\n"));
!             return -1;
!     }
!
!     /*
!      * the prior test should be definitive, but in practice it sometimes
!      * fails.  So we also check the aliases.
!      */
!     for (s = h->h_aliases; *s != NULL; s++)
!     {
!         if (pg_strcasecmp(conn->peer_cn, *s) == 0)
              return 0;
-     }
-
-     /* generate protocol-aware error message */
-     switch (addr.sa_family)
-     {
-         case AF_INET:
-             sin = (struct sockaddr_in *) & addr;
-             l = ntohl(sin->sin_addr.s_addr);
-             printfPQExpBuffer(&conn->errorMessage,
-                               libpq_gettext(
-                                             "server common name \"%s\" does not resolve to %ld.%ld.%ld.%ld\n"),
-                          conn->peer_cn, (l >> 24) % 0x100, (l >> 16) % 0x100,
-                               (l >> 8) % 0x100, l % 0x100);
-             break;
-         default:
-             printfPQExpBuffer(&conn->errorMessage,
-                               libpq_gettext(
-              "server common name \"%s\" does not resolve to peer address\n"),
-                               conn->peer_cn);
-     }

      return -1;
  }
  #endif   /* NOT_USED */
--- 550,566 ----
          return -1;
      }

!     /* Does one of the CN's IP addresses match the server's IP address? */
!     for (s = cn_hostentry->h_addr_list; *s != NULL; s++)
!         if (!memcmp(&sin->sin_addr.s_addr, *s, cn_hostentry->h_length))
              return 0;

+     l = ntohl(sin->sin_addr.s_addr);
+     printfPQExpBuffer(&conn->errorMessage,
+                       libpq_gettext(
+                         "server common name \"%s\" does not resolve to %ld.%ld.%ld.%ld\n"),
+                       conn->peer_cn, (l >> 24) % 0x100, (l >> 16) % 0x100,
+                       (l >> 8) % 0x100, l % 0x100);
      return -1;
  }
  #endif   /* NOT_USED */
***************
*** 1049,1073 ****
          }
      }

-     /* check the certificate chain of the server */
-
- #ifdef NOT_USED
-     /* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
-
      /*
!      * this eliminates simple man-in-the-middle attacks and simple
!      * impersonations
       */
-     r = SSL_get_verify_result(conn->ssl);
-     if (r != X509_V_OK)
-     {
-         printfPQExpBuffer(&conn->errorMessage,
-                    libpq_gettext("certificate could not be validated: %s\n"),
-                           X509_verify_cert_error_string(r));
-         close_SSL(conn);
-         return PGRES_POLLING_FAILED;
-     }
- #endif

      /* pull out server distinguished and common names */
      conn->peer = SSL_get_peer_certificate(conn->ssl);
--- 1017,1026 ----
          }
      }

      /*
!      * We already checked the server certificate in initialize_SSL()
!      * using SSL_CTX_set_verify() if root.crt exists.
       */

      /* pull out server distinguished and common names */
      conn->peer = SSL_get_peer_certificate(conn->ssl);
***************
*** 1091,1107 ****
                                NID_commonName, conn->peer_cn, SM_USER);
      conn->peer_cn[SM_USER] = '\0';

-     /* verify that the common name resolves to peer */
-
  #ifdef NOT_USED
!     /* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
!
!     /*
!      * this is necessary to eliminate man-in-the-middle attacks and
!      * impersonations where the attacker somehow learned the server's private
!      * key
!      */
!     if (verify_peer(conn) == -1)
      {
          close_SSL(conn);
          return PGRES_POLLING_FAILED;
--- 1044,1051 ----
                                NID_commonName, conn->peer_cn, SM_USER);
      conn->peer_cn[SM_USER] = '\0';

  #ifdef NOT_USED
!     if (verify_peer_name_matches_certificate(conn) == -1)
      {
          close_SSL(conn);
          return PGRES_POLLING_FAILED;

pgsql-patches by date:

Previous
From: Tom Lane
Date:
Subject: Re: tzcode update
Next
From: "Heikki Linnakangas"
Date:
Subject: Re: tzcode update