Reject certificates with embedded NULLs in the commonName field. This stops
authorMagnus Hagander <magnus@hagander.net>
Wed, 9 Dec 2009 06:37:21 +0000 (06:37 +0000)
committerMagnus Hagander <magnus@hagander.net>
Wed, 9 Dec 2009 06:37:21 +0000 (06:37 +0000)
attacks where an attacker would put <attack>\0<propername> in the field and
trick the validation code that the certificate was for <attack>.

This is a very low risk attack since it reuqires the attacker to trick the
CA into issuing a certificate with an incorrect field, and the common
PostgreSQL deployments are with private CAs, and not external ones. Also,
default mode in 8.4 does not do any name validation, and is thus also not
vulnerable - but the higher security modes are.

Backpatch all the way. Even though versions 8.3.x and before didn't have
certificate name validation support, they still exposed this field for
the user to perform the validation in the application code, and there
is no way to detect this problem through that API.

Security: CVE-2009-4034

src/backend/libpq/be-secure.c
src/interfaces/libpq/fe-secure.c

index 78aba180d9f5cecc09ea5b9ea909ebdc304de6ee..142b7276426ae73846aa91e1e95e0bab7280bb6d 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.74.2.3 2009/01/28 15:06:54 mha Exp $
+ *       $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.74.2.4 2009/12/09 06:37:21 mha Exp $
  *
  *       Since the server static private key ($DataDir/server.key)
  *       will normally be stored unencrypted so that the database
@@ -929,9 +929,29 @@ aloop:
                X509_NAME_oneline(X509_get_subject_name(port->peer),
                                                  port->peer_dn, sizeof(port->peer_dn));
                port->peer_dn[sizeof(port->peer_dn) - 1] = '\0';
-               X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
+               r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
                                           NID_commonName, port->peer_cn, sizeof(port->peer_cn));
                port->peer_cn[sizeof(port->peer_cn) - 1] = '\0';
+               if (r == -1)
+               {
+                       /* Unable to get the CN, set it to blank so it can't be used */
+                       port->peer_cn[0] = '\0';
+               }
+               else
+               {
+                       /*
+                        * Reject embedded NULLs in certificate common name to prevent attacks like
+                        * CVE-2009-4034.
+                        */
+                       if (r != strlen(port->peer_cn))
+                       {
+                               ereport(COMMERROR,
+                                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                                errmsg("SSL certificate's common name contains embedded null")));
+                               close_SSL(port);
+                               return -1;
+                       }
+               }
        }
        ereport(DEBUG2,
                        (errmsg("SSL connection from \"%s\"", port->peer_cn)));
index b4022d778bbfbc96a49c1f7a38aca2d4c4f749c9..16cf832b6f563ea34f929ccd16b6d2a3ec95428e 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.89.2.1 2009/01/28 15:06:55 mha Exp $
+ *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.89.2.2 2009/12/09 06:37:21 mha Exp $
  *
  * NOTES
  *       [ Most of these notes are wrong/obsolete, but perhaps not all ]
@@ -944,9 +944,28 @@ open_client_SSL(PGconn *conn)
                                          conn->peer_dn, sizeof(conn->peer_dn));
        conn->peer_dn[sizeof(conn->peer_dn) - 1] = '\0';
 
-       X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+       r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
                                                          NID_commonName, conn->peer_cn, SM_USER);
-       conn->peer_cn[SM_USER] = '\0';
+       conn->peer_cn[SM_USER] = '\0'; /* buffer is SM_USER+1 chars! */
+       if (r == -1)
+       {
+               /* Unable to get the CN, set it to blank so it can't be used */
+               conn->peer_cn[0] = '\0';
+       }
+       else
+       {
+               /*
+                * Reject embedded NULLs in certificate common name to prevent attacks like
+                * CVE-2009-4034.
+                */
+               if (r != strlen(conn->peer_cn))
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("SSL certificate's common name contains embedded null\n"));
+                       close_SSL(conn);
+                       return PGRES_POLLING_FAILED;
+               }
+       }
 
        /* verify that the common name resolves to peer */