Support specifying filename for SSL certificate, key, root certificate store
authorMagnus Hagander <magnus@hagander.net>
Mon, 15 Dec 2008 10:28:22 +0000 (10:28 +0000)
committerMagnus Hagander <magnus@hagander.net>
Mon, 15 Dec 2008 10:28:22 +0000 (10:28 +0000)
and certificate revokation list by using connection parameters or environment
variables.

Original patch by Mark Woodward, heavily reworked by Alvaro Herrera and
Magnus Hagander.

doc/src/sgml/libpq.sgml
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-secure.c
src/interfaces/libpq/libpq-int.h

index 327eee1c2c95380ff73ab37251b893ba89df5edc..dc04b00629457de7c11227630f8e10ced9e98200 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.273 2008/12/07 23:46:39 alvherre Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.274 2008/12/15 10:28:21 mha Exp $ -->
 
 <chapter id="libpq">
  <title><application>libpq</application> - C Library</title>
          </listitem>
         </varlistentry>
 
+        <varlistentry>
+         <term><literal>sslcert</literal></term>
+         <listitem>
+          <para>
+           This parameter specifies the file name of the client SSL
+           certificate.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term><literal>sslkey</literal></term>
+         <listitem>
+          <para>
+           This parameter specifies the location for the secret key
+           used for the client certificate. It can either specify a filename
+           that will be used instead of the default
+           <filename>~/.postgresql/postgresql.key</>, or can specify an external
+           engine (engines are <productname>OpenSSL</> loadable modules). The
+           external engine specification should consist of a colon-separated
+           engine name and an engine-specific key identifier.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term><literal>sslrootcert</literal></term>
+         <listitem>
+          <para>
+           This parameter specifies the file name of the root SSL certificate.
+          </para>
+         </listitem>
+        </varlistentry>
+
+        <varlistentry>
+         <term><literal>sslcrl</literal></term>
+         <listitem>
+          <para>
+           This parameter specifies the file name of the SSL certificate
+           revocation list (CRL).
+          </para>
+         </listitem>
+        </varlistentry>
+
         <varlistentry>
          <term><literal>krbsrvname</literal></term>
          <listitem>
@@ -5761,17 +5805,48 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLCERT</envar></primary>
+      </indexterm>
+      <envar>PGSSLCERT</envar> specifies the location for the client
+      certificate to use if the server requests one.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
        <primary><envar>PGSSLKEY</envar></primary>
       </indexterm>
-      <envar>PGSSLKEY</envar> specifies the hardware token that stores the
-      secret key for the client certificate. The value of this variable
-      should consist of a colon-separated engine name (engines are
-      <productname>OpenSSL</> loadable modules) and an engine-specific key
-      identifier.  If this is not set, the secret key must be kept in a
-      file.
+      <envar>PGSSLKEY</envar> specifies the location for the secret key
+      used for the client certificate. It can either specify a filename
+      that will be used instead of the default
+      <filename>~/.postgresql/postgresql.key</>, or can specify an external
+      engine (engines are <productname>OpenSSL</> loadable modules). The
+      external engine specification should consist of a colon-separated
+      engine name and an engine-specific key identifier.
+     </para>
+    </listitem>
+
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLROOTCERT</envar></primary>
+      </indexterm>
+      <envar>PGSSLROOTCERT</envar> specifies the file name where the SSL
+      root certificate is stored.
+     </para>
+    </listitem>
+
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLCRL</envar></primary>
+      </indexterm>
+      <envar>PGSSLCRL</envar> specifies the file name where the SSL certificate
+      revocation list is stored.
      </para>
     </listitem>
 
@@ -6096,6 +6171,9 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
    if the file <filename>~/.postgresql/root.crl</filename> exists
    (<filename>%APPDATA%\postgresql\root.crl</filename> on Microsoft
    Windows).
+   The location of the root certificate store and the CRL can be overridden
+   by the connection parameters <literal>sslrootcert</> and <literal>sslcrl</>
+   or the environment variables <envar>PGSSLROOTCERT</> and <envar>PGSSLCRL</>.
   </para>
 
   <para>
@@ -6105,26 +6183,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
    directory.  The certificate must be signed by one of the certificate
    authorities (<acronym>CA</acronym>) trusted by the server.  A matching
    private key file <filename>~/.postgresql/postgresql.key</> must also
-   be present, unless the secret key for the certificate is stored in a
-   hardware token, as specified by <envar>PGSSLKEY</envar>.  The private
+   be present. The private
    key file must not allow any access to world or group; achieve this by the
    command <command>chmod 0600 ~/.postgresql/postgresql.key</command>.
    On Microsoft Windows these files are named
    <filename>%APPDATA%\postgresql\postgresql.crt</filename> and
    <filename>%APPDATA%\postgresql\postgresql.key</filename>, and there
    is no special permissions check since the directory is presumed secure.
-  </para>
-
-  <para>
-   If the environment variable <envar>PGSSLKEY</envar> is set, its value
-   should consist of a colon-separated engine name and key identifier. In
-   this case, <application>libpq</application> will load the specified
-   engine, i.e. the <productname>OpenSSL</> module which supports special
-   hardware, and reference the key with the specified identifier.
-   Identifiers are engine-specific. Typically, cryptography hardware tokens
-   do not reveal secret keys to the application. Instead, applications
-   delegate all cryptography operations which require the secret key to
-   the hardware token.
+   The location of the certificate and key files can be overridden by the
+   connection parameters <literal>sslcert</> and <literal>sslkey</> or the
+   environment variables <envar>PGSSLCERT</> and <envar>PGSSLKEY</>.
   </para>
 
   <para>
index ce6af2bcd328f4149dbd4409e351843a68a51dd8..c03de3c0be61d9c1de993a9003f8d6283b47e30e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.370 2008/11/26 00:26:23 tgl Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.371 2008/12/15 10:28:21 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -177,8 +177,10 @@ static const PQconninfoOption PQconninfoOptions[] = {
 #endif
 
    /*
-    * "sslmode" option is allowed even without client SSL support because the
-    * client can still handle SSL modes "disable" and "allow".
+    * ssl options are allowed even without client SSL support because the
+    * client can still handle SSL modes "disable" and "allow". Other parameters
+    * have no effect on non-SSL connections, so there is no reason to exclude them
+    * since none of them are mandatory.
     */
    {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
    "SSL-Mode", "", 8},         /* sizeof("disable") == 8 */
@@ -186,6 +188,18 @@ static const PQconninfoOption PQconninfoOptions[] = {
    {"sslverify", "PGSSLVERIFY", DefaultSSLVerify, NULL,
    "SSL-Verify", "", 5},       /* sizeof("chain") == 5 */
 
+   {"sslcert", "PGSSLCERT", NULL, NULL,
+   "SSL-Client-Cert", "", 64},
+
+   {"sslkey", "PGSSLKEY", NULL, NULL,
+   "SSL-Client-Key", "", 64},
+
+   {"sslrootcert", "PGSSLROOTCERT", NULL, NULL,
+   "SSL-Root-Certificate", "", 64},
+
+   {"sslcrl", "PGSSLCRL", NULL, NULL,
+   "SSL-Revocation-List", "", 64},
+
 #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
    /* Kerberos and GSSAPI authentication support specifying the service name */
    {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
@@ -419,6 +433,14 @@ connectOptions1(PGconn *conn, const char *conninfo)
    conn->sslmode = tmp ? strdup(tmp) : NULL;
    tmp = conninfo_getval(connOptions, "sslverify");
    conn->sslverify = tmp ? strdup(tmp) : NULL;
+   tmp = conninfo_getval(connOptions, "sslkey");
+   conn->sslkey = tmp ? strdup(tmp) : NULL;
+   tmp = conninfo_getval(connOptions, "sslcert");
+   conn->sslcert = tmp ? strdup(tmp) : NULL;
+   tmp = conninfo_getval(connOptions, "sslrootcert");
+   conn->sslrootcert = tmp ? strdup(tmp) : NULL;
+   tmp = conninfo_getval(connOptions, "sslcrl");
+   conn->sslcrl = tmp ? strdup(tmp) : NULL;
 #ifdef USE_SSL
    tmp = conninfo_getval(connOptions, "requiressl");
    if (tmp && tmp[0] == '1')
@@ -2032,6 +2054,14 @@ freePGconn(PGconn *conn)
        free(conn->sslmode);
    if (conn->sslverify)
        free(conn->sslverify);
+   if (conn->sslcert)
+       free(conn->sslcert);
+   if (conn->sslkey)
+       free(conn->sslkey);
+   if (conn->sslrootcert)
+       free(conn->sslrootcert);
+   if (conn->sslcrl)
+       free(conn->sslcrl);
 #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
    if (conn->krbsrvname)
        free(conn->krbsrvname);
index 7aa2ca508c1aedd7f6ebdc295062c232e617279d..14f71c74cf486e9aa252eaeae6fc9fb35acb04ea 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.113 2008/12/04 14:07:42 mha Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.114 2008/12/15 10:28:22 mha Exp $
  *
  * NOTES
  *
@@ -568,7 +568,10 @@ client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
    }
 
    /* read the user certificate */
-   snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+   if (conn->sslcert)
+       strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+   else
+       snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
 
    /*
     * OpenSSL <= 0.9.8 lacks error stack handling, which means it's likely to
@@ -618,60 +621,78 @@ client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
 
    BIO_free(bio);
 
-#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
-   if (getenv("PGSSLKEY"))
+   /*
+    * Read the SSL key. If a key is specified, treat it as an engine:key combination
+    * if there is colon present - we don't support files with colon in the name. The
+    * exception is if the second character is a colon, in which case it can be a Windows
+    * filename with drive specification.
+    */
+   if (conn->sslkey && strlen(conn->sslkey) > 0)
    {
-       /* read the user key from engine */
-       char       *engine_env = getenv("PGSSLKEY");
-       char       *engine_colon = strchr(engine_env, ':');
-       char       *engine_str;
-       ENGINE     *engine_ptr;
-
-       if (!engine_colon)
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
+       if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+           && conn->sslkey[1] != ':'
+#endif
+          )
        {
-           printfPQExpBuffer(&conn->errorMessage,
-                             libpq_gettext("invalid value of PGSSLKEY environment variable\n"));
-           ERR_pop_to_mark();
-           return 0;
-       }
+           /* Colon, but not in second character, treat as engine:key */
+           ENGINE     *engine_ptr;
+           char       *engine_str = strdup(conn->sslkey);
+           char       *engine_colon = strchr(engine_str, ':');
 
-       engine_str = malloc(engine_colon - engine_env + 1);
-       strlcpy(engine_str, engine_env, engine_colon - engine_env + 1);
-       engine_ptr = ENGINE_by_id(engine_str);
-       if (engine_ptr == NULL)
-       {
-           char       *err = SSLerrmessage();
+           *engine_colon = '\0';   /* engine_str now has engine name */
+           engine_colon++;         /* engine_colon now has key name */
 
-           printfPQExpBuffer(&conn->errorMessage,
-                    libpq_gettext("could not load SSL engine \"%s\": %s\n"),
-                             engine_str, err);
-           SSLerrfree(err);
-           free(engine_str);
-           ERR_pop_to_mark();
-           return 0;
-       }
+           engine_ptr = ENGINE_by_id(engine_str);
+           if (engine_ptr == NULL)
+           {
+               char       *err = SSLerrmessage();
 
-       *pkey = ENGINE_load_private_key(engine_ptr, engine_colon + 1,
-                                       NULL, NULL);
-       if (*pkey == NULL)
-       {
-           char       *err = SSLerrmessage();
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
+                                 engine_str, err);
+               SSLerrfree(err);
+               free(engine_str);
+               ERR_pop_to_mark();
+               return 0;
+           }
 
-           printfPQExpBuffer(&conn->errorMessage,
-                             libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
-                             engine_colon + 1, engine_str, err);
-           SSLerrfree(err);
+           *pkey = ENGINE_load_private_key(engine_ptr, engine_colon,
+                                           NULL, NULL);
+           if (*pkey == NULL)
+           {
+               char       *err = SSLerrmessage();
+
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+                                 engine_colon, engine_str, err);
+               SSLerrfree(err);
+               free(engine_str);
+               ERR_pop_to_mark();
+               return 0;
+           }
            free(engine_str);
-           ERR_pop_to_mark();
-           return 0;
+
+           fnbuf[0] = '\0'; /* indicate we're not going to load from a file */
+       }
+       else
+#endif /* support for SSL engines */
+       {
+           /* PGSSLKEY is not an engine, treat it as a filename */
+           strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
        }
-       free(engine_str);
    }
    else
-#endif   /* use PGSSLKEY */
    {
-       /* read the user key from file */
+       /* No PGSSLKEY specified, load default file */
        snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+   }
+
+   if (fnbuf[0] != '\0')
+   {
+       /* read the user key from file */
+
        if (stat(fnbuf, &buf) != 0)
        {
            printfPQExpBuffer(&conn->errorMessage,
@@ -948,7 +969,11 @@ initialize_SSL(PGconn *conn)
    /* Set up to verify server cert, if root.crt is present */
    if (pqGetHomeDirectory(homedir, sizeof(homedir)))
    {
-       snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+       if (conn->sslrootcert)
+           strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+       else
+           snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+
        if (stat(fnbuf, &buf) == 0)
        {
            X509_STORE *cvstore;
@@ -966,8 +991,13 @@ initialize_SSL(PGconn *conn)
 
            if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
            {
+               if (conn->sslcrl)
+                   strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+               else
+                   snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+
                /* setting the flags to check against the complete CRL chain */
-               if (X509_STORE_load_locations(cvstore, ROOT_CRL_FILE, NULL) != 0)
+               if (X509_STORE_load_locations(cvstore, fnbuf, NULL) != 0)
 /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
 #ifdef X509_V_FLAG_CRL_CHECK
                    X509_STORE_set_flags(cvstore,
index adeaa35d0bf46304667fb3d9f5f71b7b373e1ab5..7544174dc21164493ab35ead2fb6b8158d64028b 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.137 2008/11/13 09:45:25 mha Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.138 2008/12/15 10:28:22 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -292,6 +292,11 @@ struct pg_conn
    char       *pgpass;
    char       *sslmode;        /* SSL mode (require,prefer,allow,disable) */
    char       *sslverify;      /* Verify server SSL certificate (none,chain,cn) */
+   char       *sslkey;         /* client key filename */
+   char       *sslcert;        /* client certificate filename */
+   char       *sslrootcert;    /* root certificate filename */
+   char       *sslcrl;         /* certificate revocation list filename */
+
 #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
    char       *krbsrvname;     /* Kerberos service name */
 #endif