diff options
author | Magnus Hagander <magnus@hagander.net> | 2008-12-15 10:28:22 +0000 |
---|---|---|
committer | Magnus Hagander <magnus@hagander.net> | 2008-12-15 10:28:22 +0000 |
commit | 5f3724dd7c6b8beb9be3030cb3262038755c88e4 (patch) | |
tree | 63cd88f1021be0230e31788e5dd428c9ac91efd2 /src | |
parent | a9d5f30be328fb83f0667b8a60df00a30d934fb2 (diff) | |
download | postgresql-5f3724dd7c6b8beb9be3030cb3262038755c88e4.tar.gz postgresql-5f3724dd7c6b8beb9be3030cb3262038755c88e4.zip |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 36 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-secure.c | 122 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-int.h | 7 |
3 files changed, 115 insertions, 50 deletions
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); - 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 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 |