aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces')
-rw-r--r--src/interfaces/libpq/fe-secure.c118
1 files changed, 116 insertions, 2 deletions
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 632c451366e..3240be892e0 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v 1.2 2002/06/14 04:31:49 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v 1.3 2002/06/14 04:36:58 momjian Exp $
*
* NOTES
* The client *requires* a valid server certificate. Since
@@ -52,6 +52,20 @@
* should normally be stored encrypted. However we still
* support EPH since it's useful for other reasons.
*
+ * ...
+ *
+ * Client certificates are supported, if the server requests
+ * or requires them. Client certificates can be used for
+ * authentication, to prevent sessions from being hijacked,
+ * or to allow "road warriors" to access the database while
+ * keeping it closed to everyone else.
+ *
+ * The user's certificate and private key are located in
+ * $HOME/.postgresql/postgresql.crt
+ * and
+ * $HOME/.postgresql/postgresql.key
+ * respectively.
+ *
* OS DEPENDENCIES
* The code currently assumes a POSIX password entry. How should
* Windows and Mac users be handled?
@@ -71,7 +85,7 @@
* [*] emphermal DH keys, default values
*
* milestone 4: provide endpoint authentication (client)
- * [ ] server verifies client certificates
+ * [*] server verifies client certificates
*
* milestone 5: provide informational callbacks
* [ ] provide informational callbacks
@@ -135,6 +149,7 @@ static int verify_peer(PGconn *);
static DH *load_dh_file(int keylength);
static DH *load_dh_buffer(const char *, size_t);
static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
+static int client_cert_cb(SSL *, X509 **, EVP_PKEY **);
static int initialize_SSL(PGconn *);
static void destroy_SSL(void);
static int open_client_SSL(PGconn *);
@@ -615,6 +630,101 @@ tmp_dh_cb (SSL *s, int is_export, int keylength)
}
/*
+ * Callback used by SSL to load client cert and key.
+ * This callback is only called when the server wants a
+ * client cert.
+ *
+ * Returns 1 on success, 0 on no data, -1 on error.
+ */
+static int
+client_cert_cb (SSL *ssl, X509 **x509, EVP_PKEY **pkey)
+{
+ struct passwd *pwd;
+ struct stat buf, buf2;
+ char fnbuf[2048];
+ FILE *fp;
+ PGconn *conn = (PGconn *) SSL_get_app_data(ssl);
+ int (*cb)() = NULL; /* how to read user password */
+
+ if ((pwd = getpwuid(getuid())) == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unable to get user information\n"));
+ return -1;
+ }
+
+ /* read the user certificate */
+ snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.crt",
+ pwd->pw_dir);
+ if (stat(fnbuf, &buf) == -1)
+ return 0;
+ if ((fp = fopen(fnbuf, "r")) == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unable to open certificate (%s): %s\n"),
+ fnbuf, strerror(errno));
+ return -1;
+ }
+ if (PEM_read_X509(fp, x509, NULL, NULL) == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unable to read certificate (%s): %s\n"),
+ fnbuf, SSLerrmessage());
+ fclose(fp);
+ return -1;
+ }
+ fclose(fp);
+
+ /* read the user key */
+ snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.key",
+ pwd->pw_dir);
+ if (stat(fnbuf, &buf) == -1)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("certificate present, but not private key (%s)\n"),
+ fnbuf);
+ X509_free(*x509);
+ return 0;
+ }
+ if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) ||
+ buf.st_uid != getuid())
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("private key has bad permissions (%s)\n"), fnbuf);
+ X509_free(*x509);
+ return -1;
+ }
+ if ((fp = fopen(fnbuf, "r")) == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unable to open private key file (%s): %s\n"),
+ fnbuf, strerror(errno));
+ X509_free(*x509);
+ return -1;
+ }
+ if (fstat(fileno(fp), &buf2) == -1 ||
+ buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("private key changed under us (%s)\n"), fnbuf);
+ X509_free(*x509);
+ return -1;
+ }
+ if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unable to read private key (%s): %s\n"),
+ fnbuf, SSLerrmessage());
+ X509_free(*x509);
+ fclose(fp);
+ return -1;
+ }
+ fclose(fp);
+
+ return 1;
+}
+
+/*
* Initialize global SSL context.
*/
static int
@@ -666,6 +776,9 @@ initialize_SSL (PGconn *conn)
SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);
+ /* set up mechanism to provide client certificate, if available */
+ SSL_CTX_set_client_cert_cb(SSL_context, client_cert_cb);
+
return 0;
}
@@ -691,6 +804,7 @@ open_client_SSL (PGconn *conn)
int r;
if (!(conn->ssl = SSL_new(SSL_context)) ||
+ !SSL_set_app_data(conn->ssl, conn) ||
!SSL_set_fd(conn->ssl, conn->sock) ||
SSL_connect(conn->ssl) <= 0)
{