]> granicus.if.org Git - postgresql/commitdiff
Fix multiple breakages in our support for SSL certificates.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 26 Sep 2004 22:51:49 +0000 (22:51 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 26 Sep 2004 22:51:49 +0000 (22:51 +0000)
doc/src/sgml/libpq.sgml
doc/src/sgml/runtime.sgml
src/backend/libpq/be-secure.c
src/interfaces/libpq/fe-secure.c

index e39302e178c036d32260e924b0c8a72390593a88..4691abb78d616f39386ba04dd24ea241410a2208 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.163 2004/09/23 13:31:09 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.164 2004/09/26 22:51:49 tgl Exp $
 -->
 
  <chapter id="libpq">
@@ -233,22 +233,13 @@ PGconn *PQconnectdb(const char *conninfo);
 
       <para>
        If <productname>PostgreSQL</> is compiled without SSL support,
-       using option <literal>require</> will cause an error, and
+       using option <literal>require</> will cause an error, while
        options <literal>allow</> and <literal>prefer</> will be
        tolerated but <application>libpq</> will be unable to negotiate
        an <acronym>SSL</>
        connection.<indexterm><primary>SSL</><secondary
        sortas="libpq">with libpq</></indexterm>
       </para>
-
-      <para>
-       Please note that <acronym>SSL</> support in libpq covers
-       encryption only.  It will not verify the validity of the
-       certificate presented by the server that you are connecting to,
-       nor verify that the hostname matches that of the server's
-       certificate.  Additionally, there is no support for client
-       certificates.
-      </para>
      </listitem>
     </varlistentry>
 
@@ -3688,6 +3679,41 @@ If the permissions are less strict than this, the file will be ignored.
 </para>
 </sect1>
 
+
+<sect1 id="libpq-ssl">
+<title>SSL Support</title>
+
+<indexterm zone="libpq-ssl">
+ <primary>SSL</primary>
+</indexterm>
+
+  <para>
+   <productname>PostgreSQL</> has native support for using
+   <acronym>SSL</> connections to encrypt client/server communications
+   for increased security. See <xref linkend="ssl-tcp"> for details
+   about the server-side <acronym>SSL</> functionality.
+  </para>
+
+  <para>
+   If the server demands a client certificate, 
+   <application>libpq</application>
+   will send the certificate stored in file
+   <filename>.postgresql/postgresql.crt</> within the user's home directory.
+   A matching private key file <filename>.postgresql/postgresql.key</>
+   must also be present, and must not be world-readable.
+  </para>
+
+  <para>
+   If the file <filename>.postgresql/root.crt</> is present in the user's
+   home directory,
+   <application>libpq</application> will use the certificate list stored
+   therein to verify the server's certificate.  The SSL connection will
+   fail if the server does not present a certificate; therefore, to
+   use this feature the server must also have a <filename>root.crt</> file.
+  </para>
+</sect1>
+
+
 <sect1 id="libpq-threading">
 <title>Behavior in Threaded Programs</title>
 
index ff437bad35ad1ec7b59bfef9316a4245b1f94641..59c4e325b9001eb6174f72e0803e76761a5efa31 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.283 2004/09/23 13:15:57 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.284 2004/09/26 22:51:49 tgl Exp $
 -->
 
 <Chapter Id="runtime">
@@ -804,7 +804,7 @@ SET ENABLE_SEQSCAN TO OFF;
        <para>
         Enables <acronym>SSL</> connections. Please read
         <xref linkend="ssl-tcp"> before using this. The default
-        is off.
+        is off.  This parameter can only be set at server start.
        </para>
       </listitem>
      </varlistentry>
@@ -4324,8 +4324,8 @@ $ <userinput>kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`</userinput
    The server will listen for both standard and <acronym>SSL</>
    connections on the same TCP port, and will negotiate with any
    connecting client on whether to use <acronym>SSL</>. See <xref
-   linkend="auth-pg-hba-conf"> about how to force the server to
-   require use of <acronym>SSL</> for certain connections.
+   linkend="auth-pg-hba-conf"> about how to set up the server to
+   require use of <acronym>SSL</> for some or all connections.
   </para>
 
   <para>
@@ -4361,20 +4361,24 @@ chmod og-rwx server.key
 
   <para>
    If verification of client certificates is required, place the
-   certificates of the <acronym>CA</acronym> you wish to check for in
+   certificates of the <acronym>CA</acronym>(s) you wish to check for in
    the file <filename>root.crt</filename> in the data directory.  When
    present, a client certificate will be requested from the client
-   making the connection and it must have been signed by one of the
-   certificates present in <filename>root.crt</filename>.  If no
-   certificate is presented, the connection will be allowed to proceed
-   anway.
+   during SSL connection startup, and it must have been signed by one of the
+   certificates present in <filename>root.crt</filename>.
   </para>
 
   <para>
-   The <filename>root.crt</filename> file is always checked for, and
-   its absence will be noted through a message in the log.  This is
-   merely an informative message that client certificates will not be
-   requested.
+   When the <filename>root.crt</filename> file is not present, client
+   certificates will not be requested or checked.  In this mode, SSL
+   provides communication security but not authentication.
+  </para>
+
+  <para>
+   The files <filename>server.key</>, <filename>server.crt</>,
+   and <filename>root.crt</filename> are only examined during server
+   start; so you must restart the server to make changes in them take
+   effect.
   </para>
  </sect1>
 
index 43acba4473b7d0b8ee13697e231067ca33e7ee2c..eee9ad283674f6f1200244f435b280c6c8cd5f48 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.50 2004/09/23 20:27:50 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.51 2004/09/26 22:51:49 tgl Exp $
  *
  *       Since the server static private key ($DataDir/server.key)
  *       will normally be stored unencrypted so that the database
@@ -117,7 +117,6 @@ static const char *SSLerrmessage(void);
  *     (total in both directions) before we require renegotiation.
  */
 #define RENEGOTIATION_LIMIT (512 * 1024 * 1024)
-#define CA_PATH NULL
 
 static SSL_CTX *SSL_context = NULL;
 #endif
@@ -412,12 +411,12 @@ static DH  *
 load_dh_file(int keylength)
 {
        FILE       *fp;
-       char            fnbuf[2048];
+       char            fnbuf[MAXPGPATH];
        DH                 *dh = NULL;
        int                     codes;
 
        /* attempt to open file.  It's not an error if it doesn't exist. */
-       snprintf(fnbuf, sizeof fnbuf, "%s/dh%d.pem", DataDir, keylength);
+       snprintf(fnbuf, sizeof(fnbuf), "%s/dh%d.pem", DataDir, keylength);
        if ((fp = fopen(fnbuf, "r")) == NULL)
                return NULL;
 
@@ -694,20 +693,26 @@ initialize_SSL(void)
        if (SSL_CTX_set_cipher_list(SSL_context, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH") != 1)
                elog(FATAL, "could not set the cipher list (no valid ciphers available)");
 
-       /* accept client certificates, but don't require them. */
+       /*
+        * Require and check client certificates only if we have a root.crt file.
+        */
        snprintf(fnbuf, sizeof(fnbuf), "%s/root.crt", DataDir);
-       if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, CA_PATH))
+       if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL))
        {
                /* Not fatal - we do not require client certificates */
                ereport(LOG,
                                (errmsg("could not load root certificate file \"%s\": %s",
                                                fnbuf, SSLerrmessage()),
                                 errdetail("Will not verify client certificates.")));
-               return 0;
        }
-       SSL_CTX_set_verify(SSL_context,
-                                          SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE,
-                                          verify_cb);
+       else
+       {
+               SSL_CTX_set_verify(SSL_context,
+                                                  (SSL_VERIFY_PEER |
+                                                       SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
+                                                       SSL_VERIFY_CLIENT_ONCE),
+                                                  verify_cb);
+       }
 
        return 0;
 }
index 2fba98d88b86b98d2cebf28c76d62bf79d6c0907..3fbeffe01e6509fdd91c8e6ddcf567fb34bffeb8 100644 (file)
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.51 2004/09/23 20:27:43 tgl Exp $
+ *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.52 2004/09/26 22:51:49 tgl Exp $
  *
  * NOTES
+ *       [ Most of these notes are wrong/obsolete, but perhaps not all ]
+ *
  *       The client *requires* a valid server certificate.  Since
  *       SSH tunnels provide anonymous confidentiality, the presumption
  *       is that sites that want endpoint authentication will use the
@@ -527,7 +529,7 @@ verify_peer(PGconn *conn)
        if (h == NULL)
        {
                printfPQExpBuffer(&conn->errorMessage,
-               libpq_gettext("could not get information about host (%s): %s\n"),
+               libpq_gettext("could not get information about host \"%s\": %s\n"),
                                                  conn->peer_cn, hstrerror(h_errno));
                return -1;
        }
@@ -600,15 +602,15 @@ load_dh_file(int keylength)
        struct passwd pwdstr;
        struct passwd *pwd = NULL;
        FILE       *fp;
-       char            fnbuf[2048];
+       char            fnbuf[MAXPGPATH];
        DH                 *dh = NULL;
        int                     codes;
 
-       if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0)
+       if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0)
                return NULL;
 
        /* attempt to open file.  It's not an error if it doesn't exist. */
-       snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/dh%d.pem",
+       snprintf(fnbuf, sizeof(fnbuf), "%s/.postgresql/dh%d.pem",
                         pwd->pw_dir, keylength);
 
        if ((fp = fopen(fnbuf, "r")) == NULL)
@@ -735,7 +737,7 @@ tmp_dh_cb(SSL *s, int is_export, int keylength)
  *     This callback is only called when the server wants a
  *     client cert.
  *
- *     Returns 1 on success, 0 on no data, -1 on error.
+ *     Must return 1 on success, 0 on no data or error.
  */
 static int
 client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
@@ -748,52 +750,52 @@ client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
        struct passwd *pwd = NULL;
        struct stat buf,
                                buf2;
-       char            fnbuf[2048];
+       char            fnbuf[MAXPGPATH];
        FILE       *fp;
        PGconn     *conn = (PGconn *) SSL_get_app_data(ssl);
        int                     (*cb) () = NULL;        /* how to read user password */
        char            sebuf[256];
 
 
-       if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0)
+       if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0)
        {
                printfPQExpBuffer(&conn->errorMessage,
                                          libpq_gettext("could not get user information\n"));
-               return -1;
+               return 0;
        }
 
        /* read the user certificate */
-       snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.crt",
+       snprintf(fnbuf, sizeof(fnbuf), "%s/.postgresql/postgresql.crt",
                         pwd->pw_dir);
        if (stat(fnbuf, &buf) == -1)
                return 0;
        if ((fp = fopen(fnbuf, "r")) == NULL)
        {
                printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("could not open certificate (%s): %s\n"),
+                                 libpq_gettext("could not open certificate file \"%s\": %s\n"),
                                                  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-               return -1;
+               return 0;
        }
        if (PEM_read_X509(fp, x509, NULL, NULL) == NULL)
        {
                char       *err = SSLerrmessage();
 
                printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("could not read certificate (%s): %s\n"),
+                                 libpq_gettext("could not read certificate file \"%s\": %s\n"),
                                                  fnbuf, err);
                SSLerrfree(err);
                fclose(fp);
-               return -1;
+               return 0;
        }
        fclose(fp);
 
        /* read the user key */
-       snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.key",
+       snprintf(fnbuf, sizeof(fnbuf), "%s/.postgresql/postgresql.key",
                         pwd->pw_dir);
        if (stat(fnbuf, &buf) == -1)
        {
                printfPQExpBuffer(&conn->errorMessage,
-               libpq_gettext("certificate present, but not private key (%s)\n"),
+               libpq_gettext("certificate present, but not private key file \"%s\"\n"),
                                                  fnbuf);
                X509_free(*x509);
                return 0;
@@ -802,37 +804,38 @@ client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
                buf.st_uid != getuid())
        {
                printfPQExpBuffer(&conn->errorMessage,
-               libpq_gettext("private key (%s) has wrong permissions\n"), fnbuf);
+               libpq_gettext("private key file \"%s\" has wrong permissions\n"),
+                                                 fnbuf);
                X509_free(*x509);
-               return -1;
+               return 0;
        }
        if ((fp = fopen(fnbuf, "r")) == NULL)
        {
                printfPQExpBuffer(&conn->errorMessage,
-                        libpq_gettext("could not open private key file (%s): %s\n"),
+                        libpq_gettext("could not open private key file \"%s\": %s\n"),
                                                  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
                X509_free(*x509);
-               return -1;
+               return 0;
        }
        if (fstat(fileno(fp), &buf2) == -1 ||
                buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
        {
                printfPQExpBuffer(&conn->errorMessage,
-                                                 libpq_gettext("private key (%s) changed during execution\n"), fnbuf);
+                                                 libpq_gettext("private key file \"%s\" changed during execution\n"), fnbuf);
                X509_free(*x509);
-               return -1;
+               return 0;
        }
        if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL)
        {
                char       *err = SSLerrmessage();
 
                printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("could not read private key (%s): %s\n"),
+                                 libpq_gettext("could not read private key file \"%s\": %s\n"),
                                                  fnbuf, err);
                SSLerrfree(err);
                X509_free(*x509);
                fclose(fp);
-               return -1;
+               return 0;
        }
        fclose(fp);
 
@@ -842,12 +845,12 @@ client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
                char       *err = SSLerrmessage();
 
                printfPQExpBuffer(&conn->errorMessage,
-                       libpq_gettext("certificate/private key mismatch (%s): %s\n"),
+                       libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
                                                  fnbuf, err);
                SSLerrfree(err);
                X509_free(*x509);
                EVP_PKEY_free(*pkey);
-               return -1;
+               return 0;
        }
 
        return 1;
@@ -952,46 +955,35 @@ initialize_SSL(PGconn *conn)
        char            pwdbuf[BUFSIZ];
        struct passwd pwdstr;
        struct passwd *pwd = NULL;
-       char            fnbuf[2048];
+       char            fnbuf[MAXPGPATH];
 #endif
 
        if (init_ssl_system(conn))
                return -1;
 
 #ifndef WIN32
+       /* Set up to verify server cert, if root.crt is present */
        if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0)
        {
-               snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/root.crt",
+               snprintf(fnbuf, sizeof(fnbuf), "%s/.postgresql/root.crt",
                                 pwd->pw_dir);
-               if (stat(fnbuf, &buf) == -1)
+               if (stat(fnbuf, &buf) == 0)
                {
-                       return 0;
-#ifdef NOT_USED
-                       char            sebuf[256];
+                       if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL))
+                       {
+                               char       *err = SSLerrmessage();
 
-                       /* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
-                       printfPQExpBuffer(&conn->errorMessage,
-                                                         libpq_gettext("could not read root certificate list (%s): %s\n"),
-                                                fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-                       return -1;
-#endif
-               }
-               if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, 0))
-               {
-                       char       *err = SSLerrmessage();
+                               printfPQExpBuffer(&conn->errorMessage,
+                                                                 libpq_gettext("could not read root certificate file \"%s\": %s\n"),
+                                                                 fnbuf, err);
+                               SSLerrfree(err);
+                               return -1;
+                       }
 
-                       printfPQExpBuffer(&conn->errorMessage,
-                                                         libpq_gettext("could not read root certificate list (%s): %s\n"),
-                                                         fnbuf, err);
-                       SSLerrfree(err);
-                       return -1;
+                       SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER, verify_cb);
                }
        }
 
-       SSL_CTX_set_verify(SSL_context,
-                  SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
-       SSL_CTX_set_verify_depth(SSL_context, 1);
-
        /* set up empheral DH keys */
        SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
        SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);