diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/libpq/auth.c | 36 | ||||
-rw-r--r-- | src/backend/libpq/be-secure.c | 64 | ||||
-rw-r--r-- | src/backend/libpq/hba.c | 34 | ||||
-rw-r--r-- | src/include/libpq/hba.h | 3 | ||||
-rw-r--r-- | src/include/libpq/libpq.h | 3 |
5 files changed, 123 insertions, 17 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index bccb0a516f2..dfa3ff2e9a9 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.171 2008/11/18 13:10:20 petere Exp $ + * $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.172 2008/11/20 09:29:36 mha Exp $ * *------------------------------------------------------------------------- */ @@ -275,6 +275,40 @@ ClientAuthentication(Port *port) errmsg("missing or erroneous pg_hba.conf file"), errhint("See server log for details."))); + /* + * This is the first point where we have access to the hba record for + * the current connection, so perform any verifications based on the + * hba options field that should be done *before* the authentication + * here. + */ + if (port->hba->clientcert) + { + /* + * When we parse pg_hba.conf, we have already made sure that we have + * been able to load a certificate store. Thus, if a certificate is + * present on the client, it has been verified against our root + * certificate store, and the connection would have been aborted + * already if it didn't verify ok. + */ +#ifdef USE_SSL + if (!port->peer) + { + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("connection requires a valid client certificate"))); + } +#else + /* + * hba.c makes sure hba->clientcert can't be set unless OpenSSL + * is present. + */ + Assert(false); +#endif + } + + /* + * Now proceed to do the actual authentication check + */ switch (port->hba->auth_method) { case uaReject: diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c index bae9fe8464f..fd7e703c526 100644 --- a/src/backend/libpq/be-secure.c +++ b/src/backend/libpq/be-secure.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.85 2008/10/24 12:24:35 mha Exp $ + * $PostgreSQL: pgsql/src/backend/libpq/be-secure.c,v 1.86 2008/11/20 09:29:36 mha Exp $ * * Since the server static private key ($DataDir/server.key) * will normally be stored unencrypted so that the database @@ -102,6 +102,7 @@ static const char *SSLerrmessage(void); #define RENEGOTIATION_LIMIT (512 * 1024 * 1024) static SSL_CTX *SSL_context = NULL; +static bool ssl_loaded_verify_locations = false; /* GUC variable controlling SSL cipher list */ char *SSLCipherSuites = NULL; @@ -203,6 +204,19 @@ secure_destroy(void) } /* + * Indicate if we have loaded the root CA store to verify certificates + */ +bool +secure_loaded_verify_locations(void) +{ +#ifdef USE_SSL + return ssl_loaded_verify_locations; +#endif + + return false; +} + +/* * Attempt to negotiate secure session. */ int @@ -754,15 +768,34 @@ initialize_SSL(void) elog(FATAL, "could not set the cipher list (no valid ciphers available)"); /* - * Require and check client certificates only if we have a root.crt file. + * Attempt to load CA store, so we can verify client certificates if needed. */ - if (!SSL_CTX_load_verify_locations(SSL_context, ROOT_CERT_FILE, NULL)) + if (access(ROOT_CERT_FILE, R_OK)) { - /* Not fatal - we do not require client certificates */ - ereport(LOG, + ssl_loaded_verify_locations = false; + + /* + * If root certificate file simply not found. Don't log an error here, because + * it's quite likely the user isn't planning on using client certificates. + * If we can't access it for other reasons, it is an error. + */ + if (errno != ENOENT) + { + ereport(FATAL, + (errmsg("could not access root certificate file \"%s\": %m", + ROOT_CERT_FILE))); + } + } + else if (!SSL_CTX_load_verify_locations(SSL_context, ROOT_CERT_FILE, NULL)) + { + /* + * File was there, but we could not load it. This means the file is somehow + * broken, and we cannot do verification at all - so abort here. + */ + ssl_loaded_verify_locations = false; + ereport(FATAL, (errmsg("could not load root certificate file \"%s\": %s", - ROOT_CERT_FILE, SSLerrmessage()), - errdetail("Will not verify client certificates."))); + ROOT_CERT_FILE, SSLerrmessage()))); } else { @@ -795,13 +828,18 @@ initialize_SSL(void) ROOT_CRL_FILE, SSLerrmessage()), errdetail("Certificates will not be checked against revocation list."))); } - } - SSL_CTX_set_verify(SSL_context, - (SSL_VERIFY_PEER | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT | - SSL_VERIFY_CLIENT_ONCE), - verify_cb); + /* + * Always ask for SSL client cert, but don't fail if it's not presented. We'll fail later in this case, + * based on what we find in pg_hba.conf. + */ + SSL_CTX_set_verify(SSL_context, + (SSL_VERIFY_PEER | + SSL_VERIFY_CLIENT_ONCE), + verify_cb); + + ssl_loaded_verify_locations = true; + } } } diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index d5e56bda453..64f67818c93 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.172 2008/10/28 12:10:43 mha Exp $ + * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.173 2008/11/20 09:29:36 mha Exp $ * *------------------------------------------------------------------------- */ @@ -927,6 +927,38 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) INVALID_AUTH_OPTION("map", "ident, krb5, gssapi and sspi"); parsedline->usermap = pstrdup(c); } + else if (strcmp(token, "clientcert") == 0) + { + /* + * Since we require ctHostSSL, this really can never happen on non-SSL-enabled + * builds, so don't bother checking for USE_SSL. + */ + if (parsedline->conntype != ctHostSSL) + { + ereport(LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("clientcert can only be configured for \"hostssl\" rows"), + errcontext("line %d of configuration file \"%s\"", + line_num, HbaFileName))); + return false; + } + if (strcmp(c, "1") == 0) + { + if (!secure_loaded_verify_locations()) + { + ereport(LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("client certificates can only be checked if a root certificate store is available"), + errdetail("make sure the root certificate store is present and readable"), + errcontext("line %d of configuration file \"%s\"", + line_num, HbaFileName))); + return false; + } + parsedline->clientcert = true; + } + else + parsedline->clientcert = false; + } else if (strcmp(token, "pamservice") == 0) { REQUIRE_AUTH_OPTION(uaPAM, "pamservice", "pam"); diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index 79b5a51c6d0..cf5942a2668 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -4,7 +4,7 @@ * Interface to hba.c * * - * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.51 2008/10/28 12:10:44 mha Exp $ + * $PostgreSQL: pgsql/src/include/libpq/hba.h,v 1.52 2008/11/20 09:29:36 mha Exp $ * *------------------------------------------------------------------------- */ @@ -54,6 +54,7 @@ typedef struct int ldapport; char *ldapprefix; char *ldapsuffix; + bool clientcert; } HbaLine; typedef struct Port hbaPort; diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h index 53c6437cea1..cc4d1bbd7da 100644 --- a/src/include/libpq/libpq.h +++ b/src/include/libpq/libpq.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/libpq/libpq.h,v 1.69 2008/01/01 19:45:58 momjian Exp $ + * $PostgreSQL: pgsql/src/include/libpq/libpq.h,v 1.70 2008/11/20 09:29:36 mha Exp $ * *------------------------------------------------------------------------- */ @@ -67,6 +67,7 @@ extern void pq_endcopyout(bool errorAbort); * prototypes for functions in be-secure.c */ extern int secure_initialize(void); +extern bool secure_loaded_verify_locations(void); extern void secure_destroy(void); extern int secure_open_server(Port *port); extern void secure_close(Port *port); |