diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 327eee1c2c9..dc04b006294 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,4 +1,4 @@ - + <application>libpq</application> - C Library @@ -317,6 +317,50 @@ + + sslcert + + + This parameter specifies the file name of the client SSL + certificate. + + + + + + sslkey + + + 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 + ~/.postgresql/postgresql.key, or can specify an external + engine (engines are OpenSSL loadable modules). The + external engine specification should consist of a colon-separated + engine name and an engine-specific key identifier. + + + + + + sslrootcert + + + This parameter specifies the file name of the root SSL certificate. + + + + + + sslcrl + + + This parameter specifies the file name of the SSL certificate + revocation list (CRL). + + + + krbsrvname @@ -5761,17 +5805,48 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) + + + + PGSSLCERT + + PGSSLCERT specifies the location for the client + certificate to use if the server requests one. + + + PGSSLKEY - PGSSLKEY 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 - OpenSSL loadable modules) and an engine-specific key - identifier. If this is not set, the secret key must be kept in a - file. + PGSSLKEY 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 + ~/.postgresql/postgresql.key, or can specify an external + engine (engines are OpenSSL loadable modules). The + external engine specification should consist of a colon-separated + engine name and an engine-specific key identifier. + + + + + + + PGSSLROOTCERT + + PGSSLROOTCERT specifies the file name where the SSL + root certificate is stored. + + + + + + + PGSSLCRL + + PGSSLCRL specifies the file name where the SSL certificate + revocation list is stored. @@ -6096,6 +6171,9 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) if the file ~/.postgresql/root.crl exists (%APPDATA%\postgresql\root.crl on Microsoft Windows). + The location of the root certificate store and the CRL can be overridden + by the connection parameters sslrootcert and sslcrl + or the environment variables PGSSLROOTCERT and PGSSLCRL. @@ -6105,26 +6183,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) directory. The certificate must be signed by one of the certificate authorities (CA) trusted by the server. A matching private key file ~/.postgresql/postgresql.key must also - be present, unless the secret key for the certificate is stored in a - hardware token, as specified by PGSSLKEY. The private + be present. The private key file must not allow any access to world or group; achieve this by the command chmod 0600 ~/.postgresql/postgresql.key. On Microsoft Windows these files are named %APPDATA%\postgresql\postgresql.crt and %APPDATA%\postgresql\postgresql.key, and there is no special permissions check since the directory is presumed secure. - - - - If the environment variable PGSSLKEY is set, its value - should consist of a colon-separated engine name and key identifier. In - this case, libpq will load the specified - engine, i.e. the 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 sslcert and sslkey or the + environment variables PGSSLCERT and PGSSLKEY. diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index ce6af2bcd32..c03de3c0be6 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -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); diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c index 7aa2ca508c1..14f71c74cf4 100644 --- a/src/interfaces/libpq/fe-secure.c +++ b/src/interfaces/libpq/fe-secure.c @@ -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); + engine_ptr = ENGINE_by_id(engine_str); + if (engine_ptr == 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; + } + + *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; - } - *pkey = ENGINE_load_private_key(engine_ptr, engine_colon + 1, - NULL, NULL); - if (*pkey == NULL) + fnbuf[0] = '\0'; /* indicate we're not going to load from a file */ + } + else +#endif /* support for SSL engines */ { - char *err = SSLerrmessage(); - - 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); - free(engine_str); - ERR_pop_to_mark(); - return 0; + /* PGSSLKEY is not an engine, treat it as a filename */ + strncpy(fnbuf, conn->sslkey, sizeof(fnbuf)); } - free(engine_str); } else -#endif /* use PGSSLKEY */ + { + /* 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 */ - snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_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 adeaa35d0bf..7544174dc21 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -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