summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMagnus Hagander2008-12-15 10:28:22 +0000
committerMagnus Hagander2008-12-15 10:28:22 +0000
commit0bbede61a4e9cc0851b9865dc5367c407fb5afca (patch)
treedac4a66d28989dd3f3c9f84a5e718d8f7a74c77c
parent7b589c3d2d26ef07628cc936c7bc6aca854d40ab (diff)
Support specifying filename for SSL certificate, key, root certificate store
and certificate revokation list by using connection parameters or environment variables. Original patch by Mark Woodward, heavily reworked by Alvaro Herrera and Magnus Hagander.
-rw-r--r--doc/src/sgml/libpq.sgml108
-rw-r--r--src/interfaces/libpq/fe-connect.c34
-rw-r--r--src/interfaces/libpq/fe-secure.c120
-rw-r--r--src/interfaces/libpq/libpq-int.h5
4 files changed, 200 insertions, 67 deletions
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 7d67221a54..e83c5ac80b 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -318,6 +318,50 @@
</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>
<para>
@@ -5764,14 +5808,45 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
<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>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 1b6561886b..9784ee89d4 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -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);
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index bca3938b2e..91f9035091 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -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,
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index e45f0b8a38..21c081284b 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -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