From a9bd17616e687115a824e0459133d25b536cd1d6 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Fri, 14 Jun 2002 03:56:47 +0000 Subject: [PATCH] Attached are a revised set of SSL patches. Many of these patches are motivated by security concerns, it's not just bug fixes. The key differences (from stock 7.2.1) are: *) almost all code that directly uses the OpenSSL library is in two new files, src/interfaces/libpq/fe-ssl.c src/backend/postmaster/be-ssl.c in the long run, it would be nice to merge these two files. *) the legacy code to read and write network data have been encapsulated into read_SSL() and write_SSL(). These functions should probably be renamed - they handle both SSL and non-SSL cases. the remaining code should eliminate the problems identified earlier, albeit not very cleanly. *) both front- and back-ends will send a SSL shutdown via the new close_SSL() function. This is necessary for sessions to work properly. (Sessions are not yet fully supported, but by cleanly closing the SSL connection instead of just sending a TCP FIN packet other SSL tools will be much happier.) *) The client certificate and key are now expected in a subdirectory of the user's home directory. Specifically, - the directory .postgresql must be owned by the user, and allow no access by 'group' or 'other.' - the file .postgresql/postgresql.crt must be a regular file owned by the user. - the file .postgresql/postgresql.key must be a regular file owned by the user, and allow no access by 'group' or 'other'. At the current time encrypted private keys are not supported. There should also be a way to support multiple client certs/keys. *) the front-end performs minimal validation of the back-end cert. Self-signed certs are permitted, but the common name *must* match the hostname used by the front-end. (The cert itself should always use a fully qualified domain name (FDQN) in its common name field.) This means that psql -h eris db will fail, but psql -h eris.example.com db will succeed. At the current time this must be an exact match; future patches may support any FQDN that resolves to the address returned by getpeername(2). Another common "problem" is expiring certs. For now, it may be a good idea to use a very-long-lived self-signed cert. As a compile-time option, the front-end can specify a file containing valid root certificates, but it is not yet required. *) the back-end performs minimal validation of the client cert. It allows self-signed certs. It checks for expiration. It supports a compile-time option specifying a file containing valid root certificates. *) both front- and back-ends default to TLSv1, not SSLv3/SSLv2. *) both front- and back-ends support DSA keys. DSA keys are moderately more expensive on startup, but many people consider them preferable than RSA keys. (E.g., SSH2 prefers DSA keys.) *) if /dev/urandom exists, both client and server will read 16k of randomization data from it. *) the server can read empheral DH parameters from the files $DataDir/dh512.pem $DataDir/dh1024.pem $DataDir/dh2048.pem $DataDir/dh4096.pem if none are provided, the server will default to hardcoded parameter files provided by the OpenSSL project. Remaining tasks: *) the select() clauses need to be revisited - the SSL abstraction layer may need to absorb more of the current code to avoid rare deadlock conditions. This also touches on a true solution to the pg_eof() problem. *) the SIGPIPE signal handler may need to be revisited. *) support encrypted private keys. *) sessions are not yet fully supported. (SSL sessions can span multiple "connections," and allow the client and server to avoid costly renegotiations.) *) makecert - a script that creates back-end certs. *) pgkeygen - a tool that creates front-end certs. *) the whole protocol issue, SASL, etc. *) certs are fully validated - valid root certs must be available. This is a hassle, but it means that you *can* trust the identity of the server. *) the client library can handle hardcoded root certificates, to avoid the need to copy these files. *) host name of server cert must resolve to IP address, or be a recognized alias. This is more liberal than the previous iteration. *) the number of bytes transferred is tracked, and the session key is periodically renegotiated. *) basic cert generation scripts (mkcert.sh, pgkeygen.sh). The configuration files have reasonable defaults for each type of use. Bear Giles --- src/backend/libpq/Makefile | 5 +- src/backend/libpq/pqcomm.c | 44 +- src/backend/postmaster/postmaster.c | 96 +--- src/bin/psql/startup.c | 23 +- src/include/libpq/libpq-be.h | 3 +- src/interfaces/libpq/Makefile | 4 +- src/interfaces/libpq/fe-connect.c | 81 +-- src/interfaces/libpq/fe-misc.c | 50 +- src/interfaces/libpq/fe-ssl.c | 785 ++++++++++++++++++++++++++++ src/interfaces/libpq/libpq-int.h | 3 +- src/interfaces/ssl/client.conf | 120 +++++ src/interfaces/ssl/mkcert.sh | 114 ++++ src/interfaces/ssl/pgkeygen.sh | 54 ++ src/interfaces/ssl/root.conf | 270 ++++++++++ src/interfaces/ssl/server.conf | 118 +++++ 15 files changed, 1562 insertions(+), 208 deletions(-) create mode 100644 src/interfaces/libpq/fe-ssl.c create mode 100644 src/interfaces/ssl/client.conf create mode 100755 src/interfaces/ssl/mkcert.sh create mode 100644 src/interfaces/ssl/pgkeygen.sh create mode 100644 src/interfaces/ssl/root.conf create mode 100644 src/interfaces/ssl/server.conf diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile index d0f05e802d..55d2b8c421 100644 --- a/src/backend/libpq/Makefile +++ b/src/backend/libpq/Makefile @@ -4,7 +4,7 @@ # Makefile for libpq subsystem (backend half of libpq interface) # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.30 2002/04/04 04:25:46 momjian Exp $ +# $Header: /cvsroot/pgsql/src/backend/libpq/Makefile,v 1.31 2002/06/14 03:56:46 momjian Exp $ # #------------------------------------------------------------------------- @@ -14,7 +14,8 @@ include $(top_builddir)/src/Makefile.global # be-fsstubs is here for historical reasons, probably belongs elsewhere -OBJS = be-fsstubs.o auth.o crypt.o hba.o md5.o pqcomm.o pqformat.o pqsignal.o +OBJS = be-fsstubs.o be-ssl.o auth.o crypt.o hba.o md5.o pqcomm.o \ + pqformat.o pqsignal.o all: SUBSYS.o diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index a82771dcf3..5ece972b56 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -29,7 +29,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pqcomm.c,v 1.133 2002/05/05 00:03:28 tgl Exp $ + * $Id: pqcomm.c,v 1.134 2002/06/14 03:56:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -81,6 +81,14 @@ #include "miscadmin.h" #include "storage/ipc.h" +/* these functions are misnamed - they handle both SSL and non-SSL case */ +extern ssize_t read_SSL(Port *, void *ptr, size_t len); +extern ssize_t write_SSL(Port *, const void *ptr, size_t len); + +#ifdef USE_SSL +extern void close_SSL(Port *); +#endif /* USE_SSL */ + static void pq_close(void); @@ -138,6 +146,9 @@ pq_close(void) { if (MyProcPort != NULL) { +#ifdef USE_SSL + close_SSL(MyProcPort); +#endif /* USE_SSL */ close(MyProcPort->sock); /* make sure any subsequent attempts to do I/O fail cleanly */ MyProcPort->sock = -1; @@ -416,6 +427,7 @@ StreamConnection(int server_fd, Port *port) void StreamClose(int sock) { + /* FIXME - what about closing SSL connections? */ close(sock); } @@ -457,14 +469,8 @@ pq_recvbuf(void) { int r; -#ifdef USE_SSL - if (MyProcPort->ssl) - r = SSL_read(MyProcPort->ssl, PqRecvBuffer + PqRecvLength, - PQ_BUFFER_SIZE - PqRecvLength); - else -#endif - r = recv(MyProcPort->sock, PqRecvBuffer + PqRecvLength, - PQ_BUFFER_SIZE - PqRecvLength, 0); + r = read_SSL(MyProcPort, PqRecvBuffer + PqRecvLength, + PQ_BUFFER_SIZE - PqRecvLength); if (r < 0) { @@ -480,7 +486,11 @@ pq_recvbuf(void) elog(COMMERROR, "pq_recvbuf: recv() failed: %m"); return EOF; } +#ifdef USE_SSL + if (r == 0 && !MyProcPort->ssl) +#else /* USE_SSL */ if (r == 0) +#endif /* USE_SSL */ { /* as above, only write to postmaster log */ elog(COMMERROR, "pq_recvbuf: unexpected EOF on client connection"); @@ -651,14 +661,13 @@ pq_flush(void) { int r; -#ifdef USE_SSL - if (MyProcPort->ssl) - r = SSL_write(MyProcPort->ssl, bufptr, bufend - bufptr); - else -#endif - r = send(MyProcPort->sock, bufptr, bufend - bufptr, 0); + r = write_SSL(MyProcPort, bufptr, bufend - bufptr); +#ifdef USE_SSL + if (r < 0 || (r == 0 && !MyProcPort->ssl)) +#else /* USE_SSL */ if (r <= 0) +#endif /* USE_SSL */ { if (errno == EINTR) continue; /* Ok if we were interrupted */ @@ -703,8 +712,9 @@ int pq_eof(void) { char x; - int res; + int res = 1; +#ifndef USE_SSL /* not a good solution, but better than nothing */ res = recv(MyProcPort->sock, &x, 1, MSG_PEEK); if (res < 0) @@ -713,6 +723,8 @@ pq_eof(void) elog(COMMERROR, "pq_eof: recv() failed: %m"); return EOF; } +#endif /* USE_SSL */ + if (res == 0) return EOF; else diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index ca96a4482b..5b56ab37ca 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.276 2002/06/11 13:40:51 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.277 2002/06/14 03:56:47 momjian Exp $ * * NOTES * @@ -165,10 +165,6 @@ static int ServerSock_INET = INVALID_SOCK; /* stream socket server */ static int ServerSock_UNIX = INVALID_SOCK; /* stream socket server */ #endif -#ifdef USE_SSL -static SSL_CTX *SSL_context = NULL; /* Global SSL context */ -#endif - /* * Set by the -o option */ @@ -274,8 +270,10 @@ __attribute__((format(printf, 1, 2))); #define ShutdownDataBase() SSDataBase(BS_XLOG_SHUTDOWN) #ifdef USE_SSL -static void InitSSL(void); -static const char *SSLerrmessage(void); +extern int initialize_ctx(const char *, void (*err)(const char *fmt,...)); +extern void destroy_ctx(void); +extern int open_SSL_server(Port *); +extern void close_SSL(Port *); #endif @@ -609,7 +607,10 @@ PostmasterMain(int argc, char *argv[]) ExitPostmaster(1); } if (EnableSSL) - InitSSL(); + { + if (initialize_ctx(NULL, postmaster_error) == -1) + ExitPostmaster(1); + } #endif /* @@ -1114,13 +1115,9 @@ ProcessStartupPacket(Port *port, bool SSLdone) #ifdef USE_SSL if (SSLok == 'S') - { - if (!(port->ssl = SSL_new(SSL_context)) || - !SSL_set_fd(port->ssl, port->sock) || - SSL_accept(port->ssl) <= 0) + { + if (open_SSL_server(port) != STATUS_OK) { - elog(LOG, "failed to initialize SSL connection: %s (%m)", - SSLerrmessage()); return STATUS_ERROR; } } @@ -1322,9 +1319,10 @@ static void ConnFree(Port *conn) { #ifdef USE_SSL - if (conn->ssl) - SSL_free(conn->ssl); + close_SSL(conn); #endif + if (conn->sock != -1) + close(conn->sock); free(conn); } @@ -2424,72 +2422,6 @@ CountChildren(void) return cnt; } -#ifdef USE_SSL - -/* - * Initialize SSL library and structures - */ -static void -InitSSL(void) -{ - char fnbuf[2048]; - - SSL_load_error_strings(); - SSL_library_init(); - SSL_context = SSL_CTX_new(SSLv23_method()); - if (!SSL_context) - { - postmaster_error("failed to create SSL context: %s", - SSLerrmessage()); - ExitPostmaster(1); - } - snprintf(fnbuf, sizeof(fnbuf), "%s/server.crt", DataDir); - if (!SSL_CTX_use_certificate_file(SSL_context, fnbuf, SSL_FILETYPE_PEM)) - { - postmaster_error("failed to load server certificate (%s): %s", - fnbuf, SSLerrmessage()); - ExitPostmaster(1); - } - snprintf(fnbuf, sizeof(fnbuf), "%s/server.key", DataDir); - if (!SSL_CTX_use_PrivateKey_file(SSL_context, fnbuf, SSL_FILETYPE_PEM)) - { - postmaster_error("failed to load private key file (%s): %s", - fnbuf, SSLerrmessage()); - ExitPostmaster(1); - } - if (!SSL_CTX_check_private_key(SSL_context)) - { - postmaster_error("check of private key failed: %s", - SSLerrmessage()); - ExitPostmaster(1); - } -} - -/* - * Obtain reason string for last SSL error - * - * Some caution is needed here since ERR_reason_error_string will - * return NULL if it doesn't recognize the error code. We don't - * want to return NULL ever. - */ -static const char * -SSLerrmessage(void) -{ - unsigned long errcode; - const char *errreason; - static char errbuf[32]; - - errcode = ERR_get_error(); - if (errcode == 0) - return "No SSL error reported"; - errreason = ERR_reason_error_string(errcode); - if (errreason != NULL) - return errreason; - snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode); - return errbuf; -} - -#endif /* USE_SSL */ /* * Fire off a subprocess for startup/shutdown/checkpoint. diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index fee8c98a5f..1d8e3aa965 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/startup.c,v 1.57 2002/05/14 04:20:15 ishii Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/startup.c,v 1.58 2002/06/14 03:56:47 momjian Exp $ */ #include "postgres_fe.h" @@ -678,14 +678,33 @@ printSSLInfo(void) { int sslbits = -1; SSL *ssl; + X509 *peer; + char sn[256]; + long l; ssl = PQgetssl(pset.db); if (!ssl) return; /* no SSL */ +/* peer = pset.db.peer; */ + if ((peer = SSL_get_peer_certificate(ssl)) != NULL) + { + X509_NAME_oneline(X509_get_subject_name(peer), sn, sizeof sn); + } + else + { + strncpy(sn, "(anonymous)", sizeof sn); + } + printf(gettext("SSL connection\n")); + printf(gettext("(host: %s)\n"), sn); + SSL_get_cipher_bits(ssl, &sslbits); - printf(gettext("SSL connection (cipher: %s, bits: %i)\n\n"), + printf(gettext("(protocol: %s)\n"), SSL_get_version(ssl)), + printf(gettext("(cipher: %s, bits: %i)\n"), SSL_get_cipher(ssl), sslbits); + l = SSL_get_default_timeout(ssl); + printf(gettext("(timeout: %ld:%02ld:%02ld)\n\n"), + l / 3600L, (l / 60L) % 60L, l % 60L); } #endif diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index ed8b4e939f..d0e6a15162 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-be.h,v 1.27 2001/11/12 05:43:25 tgl Exp $ + * $Id: libpq-be.h,v 1.28 2002/06/14 03:56:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -70,6 +70,7 @@ typedef struct Port */ #ifdef USE_SSL SSL *ssl; + X509 *peer; #endif } Port; diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile index 8b8f3c91d1..a9679e631d 100644 --- a/src/interfaces/libpq/Makefile +++ b/src/interfaces/libpq/Makefile @@ -4,7 +4,7 @@ # # Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.59 2001/09/22 22:54:32 petere Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.60 2002/06/14 03:56:47 momjian Exp $ # #------------------------------------------------------------------------- @@ -20,7 +20,7 @@ SO_MINOR_VERSION= 2 override CPPFLAGS := -I$(srcdir) $(CPPFLAGS) -DFRONTEND -DSYSCONFDIR='"$(sysconfdir)"' OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \ - pqexpbuffer.o dllist.o md5.o pqsignal.o \ + pqexpbuffer.o dllist.o md5.o pqsignal.o fe-ssl.o \ $(INET_ATON) $(SNPRINTF) $(STRERROR) ifdef MULTIBYTE diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 9864aad9bf..ac4a42052a 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.183 2002/04/15 23:34:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.184 2002/06/14 03:56:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -62,10 +62,6 @@ inet_aton(const char *cp, struct in_addr * inp) #endif -#ifdef USE_SSL -static SSL_CTX *SSL_context = NULL; -#endif - #define NOTIFYLIST_INITIAL_SIZE 10 #define NOTIFYLIST_GROWBY 10 @@ -186,8 +182,13 @@ static char *conninfo_getval(PQconninfoOption *connOptions, static void defaultNoticeProcessor(void *arg, const char *message); static int parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage); + #ifdef USE_SSL -static const char *SSLerrmessage(void); +extern int initialize_ctx(const char *passwd, void (*err)(const char *fmt,...), PGconn *); +extern void destroy_ctx(PGconn *); +extern int open_SSL_client(PGconn *); +extern void close_SSL(PGconn *); +extern SSL *PQgetssl(PGconn *); #endif @@ -969,28 +970,10 @@ retry2: } if (SSLok == 'S') { - if (!SSL_context) - { - SSL_load_error_strings(); - SSL_library_init(); - SSL_context = SSL_CTX_new(SSLv23_method()); - if (!SSL_context) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not create SSL context: %s\n"), - SSLerrmessage()); - goto connect_errReturn; - } - } - if (!(conn->ssl = SSL_new(SSL_context)) || - !SSL_set_fd(conn->ssl, conn->sock) || - SSL_connect(conn->ssl) <= 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not establish SSL connection: %s\n"), - SSLerrmessage()); + if (initialize_ctx(NULL, NULL, conn) == -1) + goto connect_errReturn; + if (open_SSL_client(conn) == -1) goto connect_errReturn; - } /* SSL connection finished. Continue to send startup packet */ } else if (SSLok == 'E') @@ -1015,7 +998,7 @@ retry2: goto connect_errReturn; } } - if (conn->require_ssl && !conn->ssl) + if (conn->require_ssl && !PQgetssl(conn)) { /* Require SSL, but server does not support/want it */ printfPQExpBuffer(&conn->errorMessage, @@ -1914,8 +1897,7 @@ freePGconn(PGconn *conn) return; pqClearAsyncResult(conn); /* deallocate result and curTuple */ #ifdef USE_SSL - if (conn->ssl) - SSL_free(conn->ssl); + close_SSL(conn); #endif if (conn->sock >= 0) { @@ -2641,35 +2623,6 @@ PQconninfoFree(PQconninfoOption *connOptions) } -#ifdef USE_SSL - -/* - * Obtain reason string for last SSL error - * - * Some caution is needed here since ERR_reason_error_string will - * return NULL if it doesn't recognize the error code. We don't - * want to return NULL ever. - */ -static const char * -SSLerrmessage(void) -{ - unsigned long errcode; - const char *errreason; - static char errbuf[32]; - - errcode = ERR_get_error(); - if (errcode == 0) - return "No SSL error reported"; - errreason = ERR_reason_error_string(errcode); - if (errreason != NULL) - return errreason; - snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode); - return errbuf; -} - -#endif /* USE_SSL */ - - /* =========== accessor functions for PGconn ========= */ char * PQdb(const PGconn *conn) @@ -2814,16 +2767,6 @@ PQsetClientEncoding(PGconn *conn, const char *encoding) } #endif -#ifdef USE_SSL -SSL * -PQgetssl(PGconn *conn) -{ - if (!conn) - return NULL; - return conn->ssl; -} -#endif - void PQtrace(PGconn *conn, FILE *debug_port) { diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index 643c81055f..64a05c8dbe 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -25,7 +25,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.70 2002/04/24 02:26:06 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.71 2002/06/14 03:56:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -55,6 +55,13 @@ #include "mb/pg_wchar.h" #endif +/* these functions are misnamed - they handle both SSL and non-SSL case */ +extern ssize_t read_SSL (PGconn *, void *ptr, size_t); +extern ssize_t write_SSL (PGconn *, const void *ptr, size_t); + +#ifdef USE_SSL +extern ssize_t close_SSL (PGconn *); +#endif #define DONOTICE(conn,message) \ ((*(conn)->noticeHook) ((conn)->noticeArg, (message))) @@ -477,14 +484,8 @@ pqReadData(PGconn *conn) /* OK, try to read some data */ retry3: -#ifdef USE_SSL - if (conn->ssl) - nread = SSL_read(conn->ssl, conn->inBuffer + conn->inEnd, - conn->inBufSize - conn->inEnd); - else -#endif - nread = recv(conn->sock, conn->inBuffer + conn->inEnd, - conn->inBufSize - conn->inEnd, 0); + nread = read_SSL(conn, conn->inBuffer + conn->inEnd, + conn->inBufSize - conn->inEnd); if (nread < 0) { if (SOCK_ERRNO == EINTR) @@ -563,14 +564,8 @@ retry3: * arrived. */ retry4: -#ifdef USE_SSL - if (conn->ssl) - nread = SSL_read(conn->ssl, conn->inBuffer + conn->inEnd, - conn->inBufSize - conn->inEnd); - else -#endif - nread = recv(conn->sock, conn->inBuffer + conn->inEnd, - conn->inBufSize - conn->inEnd, 0); + nread = read_SSL(conn, conn->inBuffer + conn->inEnd, + conn->inBufSize - conn->inEnd); if (nread < 0) { if (SOCK_ERRNO == EINTR) @@ -611,6 +606,9 @@ definitelyFailed: "\tThis probably means the server terminated abnormally\n" "\tbefore or while processing the request.\n")); conn->status = CONNECTION_BAD; /* No more connection to backend */ +#ifdef USE_SSL + close_SSL(conn); +#endif #ifdef WIN32 closesocket(conn->sock); #else @@ -650,23 +648,9 @@ pqSendSome(PGconn *conn) /* while there's still data to send */ while (len > 0) { - /* Prevent being SIGPIPEd if backend has closed the connection. */ -#ifndef WIN32 - pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN); -#endif - int sent; -#ifdef USE_SSL - if (conn->ssl) - sent = SSL_write(conn->ssl, ptr, len); - else -#endif - sent = send(conn->sock, ptr, len, 0); - -#ifndef WIN32 - pqsignal(SIGPIPE, oldsighandler); -#endif + sent = write_SSL(conn, ptr, len); if (sent < 0) { @@ -732,7 +716,7 @@ pqSendSome(PGconn *conn) */ #ifdef USE_SSL /* can't do anything for our SSL users yet */ - if (conn->ssl == NULL) + if (PQgetssl(conn) == NULL) { #endif if (pqIsnonblocking(conn)) diff --git a/src/interfaces/libpq/fe-ssl.c b/src/interfaces/libpq/fe-ssl.c new file mode 100644 index 0000000000..dc0590357d --- /dev/null +++ b/src/interfaces/libpq/fe-ssl.c @@ -0,0 +1,785 @@ +#include "postgres_fe.h" + +#include +#include +#include +#include +#include +#include + +#include "libpq-fe.h" +#include "libpq-int.h" +#include "fe-auth.h" +#include "pqsignal.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + +#ifdef USE_SSL +#include +#include +#include + +int initialize_ctx(const char *, void (*err)(const char *fmt,...), PGconn *); +void destroy_ctx(void); +int open_SSL_client(PGconn *); +void close_SSL(PGconn *); +SSL PGgetssl(PGconn *); +static int clientCert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey); +static int verify_cb(int, X509_STORE_CTX *); +static void info_cb(SSL *ssl, int type, int args); +static void load_hardcoded_certs(void); +static X509 * load_cert_buffer(const char *buf, size_t len); +static const char *SSLerrmessage(void); +#endif + +ssize_t read_SSL(PGconn *, void *, size_t); +ssize_t write_SSL(PGconn *, const void *, size_t); + +extern int h_error; + +#ifdef USE_SSL +static SSL_CTX *ctx = NULL; +#endif + +#define PING() fprintf(stderr,"%s, line %d, %s\n", __FILE__, __LINE__, __func__) + +/* + * Read data from network. + */ +ssize_t read_SSL (PGconn *conn, void *ptr, size_t len) +{ + ssize_t n; + +#ifdef USE_SSL + if (conn->ssl) + { + n = SSL_read(conn->ssl, ptr, len); + switch (SSL_get_error(conn->ssl, n)) + { + case SSL_ERROR_NONE: + break; + case SSL_ERROR_WANT_READ: + break; + case SSL_ERROR_SYSCALL: + SOCK_ERRNO = get_last_socket_error(); + break; + case SSL_ERROR_SSL: +// log error... + SOCK_ERRNO = ECONNRESET; + break; + case SSL_ERROR_ZERO_RETURN: + SOCK_ERRNO = ECONNRESET; + break; + } + } + else +#endif /* USE_SSL */ + n = recv(conn->sock, ptr, len, 0); + + return n; +} + +/* + * Write data to network. + */ +ssize_t write_SSL (PGconn *conn, const void *ptr, size_t len) +{ + ssize_t n; + + /* prevent being SIGPIPEd if backend has closed the connection. */ +#ifndef WIN32 + pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN); +#endif + +#ifdef USE_SSL + if (conn->ssl) + { + n = SSL_write(conn->ssl, ptr, len); + switch (SSL_get_error(conn->ssl, n)) + { + case SSL_ERROR_NONE: + break; + case SSL_ERROR_WANT_WRITE: + break; + case SSL_ERROR_SYSCALL: + SOCK_ERRNO = get_last_socket_error(); + break; + case SSL_ERROR_SSL: +fprintf(stderr, "ssl error\n"); +// log error... + SOCK_ERRNO = ECONNRESET; + break; + case SSL_ERROR_ZERO_RETURN: +fprintf(stderr, "zero bytes\n"); + SOCK_ERRNO = ECONNRESET; + break; + } + } + else +#endif + n = send(conn->sock, ptr, len, 0); + +#ifndef WIN32 + pqsignal(SIGPIPE, oldsighandler); +#endif + + return n; +} + +#ifdef USE_SSL +/* + * Null authentication callback + */ +static int +verify_cb (int ok, X509_STORE_CTX *ctx) +{ + char sn[256], buf[256]; + X509 *cert; + int err, depth, n; + BIO *bio; + + cert = X509_STORE_CTX_get_current_cert(ctx); + err = X509_STORE_CTX_get_error(ctx); + depth= X509_STORE_CTX_get_error_depth(ctx); + + X509_NAME_oneline(X509_get_subject_name(cert), sn, sizeof sn); + if (!ok) + { + switch (err) + { + /* accept self-signed certs */ +// case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: +// ok = 1; +// break; + + default: + fprintf(stderr, "client cert %s: %s", sn, + X509_verify_cert_error_string(err)); + } + } + + switch (ctx->error) + { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof buf); + fprintf(stderr, "client cert %s: cannot find issuer %s", sn, buf); + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + bio = BIO_new(BIO_s_mem()); + ASN1_TIME_print(bio, X509_get_notBefore(cert)); + BIO_flush(bio); + n = BIO_read(bio, buf, sizeof buf - 1); + buf[n] = '\0'; + fprintf(stderr, "client cert %s: not valid until %s", sn, buf); + break; + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + bio = BIO_new(BIO_s_mem()); + ASN1_TIME_print(bio, X509_get_notAfter(cert)); + BIO_flush(bio); + n = BIO_read(bio, buf, sizeof buf - 1); + buf[n] = '\0'; + fprintf(stderr, "client cert %s: not valid after %s\n", sn, buf); + break; + } + + return ok; +} + +/* + * Callback used by SSL to provide information messages. + */ +static void +info_cb (SSL *ssl, int type, int args) +{ + PGconn *conn = NULL; + + conn = (PGconn *) SSL_get_app_data(ssl); + if (conn == NULL || conn->Pfdebug == NULL) + return; + + switch (type) + { + case SSL_CB_HANDSHAKE_START: + fprintf(conn->Pfdebug, "Handshake start\n"); + break; + case SSL_CB_HANDSHAKE_DONE: + fprintf(conn->Pfdebug, "Handshake done\n"); + break; + case SSL_CB_ACCEPT_LOOP: + fprintf(conn->Pfdebug, "Accept loop...\n"); + break; + case SSL_CB_ACCEPT_EXIT: + fprintf(conn->Pfdebug, "Accept exit (%d)\n", args); + break; + case SSL_CB_CONNECT_LOOP: + fprintf(conn->Pfdebug, "Connect loop...\n"); + break; + case SSL_CB_CONNECT_EXIT: + fprintf(conn->Pfdebug, "Connect exit (%d)\n", args); + break; + case SSL_CB_READ_ALERT: + fprintf(conn->Pfdebug, "Read alert (0x%04x)\n", args); + break; + case SSL_CB_WRITE_ALERT: + fprintf(conn->Pfdebug, "Write alert (0x%04x)\n", args); + break; + } +} + +/* + * Callback used by SSL to load client cert and key. + * At the current time we require the cert and key to be + * located in the .postgresql directory under the user's + * home directory, and the files must be named 'postgresql.crt' + * and 'postgresql.key' respectively. + * + * returns 1 on success, 0 on no data, -1 on error. + */ +static int +clientCert_cb (SSL *ssl, X509 **x509, EVP_PKEY **pkey) +{ + uid_t uid; + struct passwd *pwd; + char fnbuf[2048]; + struct stat buf, buf1; + FILE *fp; + int (*cb)() = NULL; + + if ((uid = getuid()) == -1) + { + fprintf(stderr, "can't get current uid\n"); + return -1; + } + if ((pwd = getpwuid(uid)) == NULL || !pwd->pw_dir) + { + fprintf(stderr, "can't get passwd entry\n"); + return -1; + } + + /* + * if $HOME/.postgresql does not exist, 'no data' case. + * otherwise, it must be a directory, owned by current user, + * and not group- or world-accessible. + */ + snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql", pwd->pw_dir); + if (lstat(fnbuf, &buf) == -1) + return 0; + if (!S_ISDIR(buf.st_mode) || buf.st_uid != uid || + (buf.st_mode & (S_IRWXG | S_IRWXO)) != 0) + { + fprintf(stderr, + "$HOME/.postgresql directory has wrong ownership or permissions\n"); + return -1; + } + + /* + * make sure $HOME/.postgresql/postgresql.crt file exists, + * is regular file and owned by current user. + */ + snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.crt", + pwd->pw_dir); + if (lstat(fnbuf, &buf) == -1) + return 0; + if (!S_ISREG(buf.st_mode) || buf.st_uid != uid) + { + fprintf(stderr, + "certificate file has wrong ownership or permissions\n"); + return -1; + } + if ((fp = fopen(fnbuf, "r")) == NULL) + { + fprintf(stderr, "can't open certificate file (%s)\n", strerror(errno)); + return -1; + } + if (PEM_read_X509(fp, x509, NULL, NULL) == NULL) + { + fprintf(stderr, "can't read certificate %s\n", SSLerrmessage()); + fclose(fp); + return -1; + } + fclose(fp); + + /* + * make sure $HOME/.postgresql/postgresql.key file exists, + * is regular file, owned by current user, and not group- + * or world-accessable. + */ + snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.key", + pwd->pw_dir); + if (lstat(fnbuf, &buf) == -1) + { + fprintf(stderr, "certificate file exists, but no private key\n"); + SSL_use_certificate(ssl, NULL); + return -1; + } + if (!S_ISREG(buf.st_mode) || buf.st_uid != uid || + (buf.st_mode & (S_IRWXG | S_IRWXO)) != 0) + { + fprintf(stderr, + "private key file has wrong ownership or permissions\n"); + SSL_use_certificate(ssl, NULL); + return -1; + } + if ((fp = fopen(fnbuf, "r")) == NULL) + { + fprintf(stderr, "error opening private key file: %s\n", + strerror(errno)); + SSL_use_certificate(ssl, NULL); + return -1; + } + if (fstat(fileno(fp),&buf1) == -1 || + buf.st_dev != buf1.st_dev || buf.st_ino != buf1.st_ino) + { + fprintf(stderr, "private key changed under us!\n"); + fclose(fp); + SSL_use_certificate(ssl, NULL); + return -1; + } + if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL) + { + fprintf(stderr, "can't read private key %s\n", SSLerrmessage()); + fclose(fp); + SSL_use_certificate(ssl, NULL); + return -1; + } + fclose(fp); + + return 1; +} + +/* + * Load a root cert from a buffer. This allows us to avoid + * needing to copy the root cert to deployed systems. + */ +static X509 * +load_cert_buffer(const char *buf, size_t len) +{ + BIO *bio; + X509 *x; + + bio = BIO_new_mem_buf((char *) buf, len); + x = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + + return x; +} + +/* + * Initialize global SSL context. + * + * We want to use 'err' for errors, same as the corresponding + * function on the server, but for now we use legacy error handler + * in PGconn. + */ +int +initialize_ctx (const char *password, + void (*err)(const char * fmt,...), PGconn *conn) +{ + SSL_METHOD *meth = NULL; + struct stat buf; + struct passwd *pwd; + char fnbuf[2048]; + + if (!ctx) + { + SSL_library_init(); + SSL_load_error_strings(); +// meth = SSLv23_method(); + meth = TLSv1_method(); + ctx = SSL_CTX_new(meth); + + if (!ctx) { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not create SSL context: %s\n"), + SSLerrmessage()); + return -1; + } + } + + /* load any hard-coded root cert */ + load_hardcoded_certs(); + + /* load the CAs we trust */ + if ((pwd = getpwuid(getuid())) != NULL) + { + snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/root.crt", pwd->pw_dir); + if (stat(fnbuf, &buf) != -1) + { + if (!SSL_CTX_load_verify_locations(ctx, fnbuf, 0)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not read CA list (%s): %s\n"), + fnbuf, SSLerrmessage()); + return -1; + } + } + } + + /* load randomness */ +#ifdef RANDOM + if (!RAND_load_file(RANDOM, 1024 * 1024)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not load randomness (%s): %s\n"), + RANDOM, SSLerrmessage()); + return -1; + } +#else /* RANDOM */ + if (lstat("/dev/urandom", &buf) == 0 && S_ISCHR(buf.st_mode)) + { + if (!RAND_load_file("/dev/urandom", 16 * 1024)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not load randomness (%s): %s\n"), + "/dev/urandom", SSLerrmessage()); + return -1; + } + } +#endif /* RANDOM */ + + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); + SSL_CTX_set_verify_depth(ctx, 1); + + SSL_CTX_set_info_callback(ctx, info_cb); + SSL_CTX_set_client_cert_cb(ctx, clientCert_cb); + + return 0; +} + +/* + * Destroy the global SSL context. + */ +void destroy_ctx (void) +{ + SSL_CTX_free(ctx); + ctx = NULL; +} + +/* + * Open a SSL connection. + */ +int +open_SSL_client (PGconn *conn) +{ + char peerName[256]; + struct sockaddr addr; + struct sockaddr_in *sin1, *sin2; + socklen_t len; + struct hostent *h; + const char *reason; + char **s; + int r; + + if (!(conn->ssl = SSL_new(ctx)) || + !SSL_set_app_data(conn->ssl, conn) || + !SSL_set_fd(conn->ssl, conn->sock) || + SSL_connect(conn->ssl) <= 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not establish SSL connection: %s\n"), + SSLerrmessage()); + return -1; + } + + /* check the certificate chain */ + /* for now, we allow self-signed server certs */ + r = SSL_get_verify_result(conn->ssl); +// if (r != X509_V_OK && r != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) + if (r != X509_V_OK) + { + switch (r) + { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + reason = "unable to get issuer cert"; + break; + case X509_V_ERR_UNABLE_TO_GET_CRL: + reason = "unable to get CRL"; + break; + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + reason = "unable to decrypt cert signature"; + break; + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + reason = "unable to decrypt CRL signature"; + break; + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + reason = "unable to decode issuer public key"; + break; + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + reason = "cert signature failure"; + break; + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + reason = "CRL signature failure"; + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + reason = "cert is not yet valid"; + break; + case X509_V_ERR_CERT_HAS_EXPIRED: + reason = "cert has expired"; + break; + case X509_V_ERR_CRL_NOT_YET_VALID: + reason = "CRL not yet valid"; + break; + case X509_V_ERR_CRL_HAS_EXPIRED: + reason = "CRL has expired"; + break; + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + reason = "error in cert notBefore field"; + break; + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + reason = "error in cert notAfter field"; + break; + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + reason = "error in CRL last update field"; + break; + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + reason = "error in CRL next update field"; + break; + case X509_V_ERR_OUT_OF_MEM: + reason = "out of memory"; + break; + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + reason = "depth zero self-signed cert"; + break; + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + reason = "self-signed cert in chain"; + break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + reason = "unable to get issuer cert locally"; + break; + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + reason = "unable to verify leaf signature"; + break; + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + reason = "cert chain too long"; + break; + case X509_V_ERR_CERT_REVOKED: + reason = "cert revoked"; + break; + case X509_V_ERR_INVALID_CA: + reason = "invalid CA"; + break; + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + reason = "path length exceeded"; + break; + case X509_V_ERR_INVALID_PURPOSE: + reason = "invalid purpose"; + break; + case X509_V_ERR_CERT_UNTRUSTED: + reason = "cert untrusted"; + break; + case X509_V_ERR_CERT_REJECTED: + reason = "cert rejected"; + break; + /* These are 'informational' when looking for issuer cert */ + case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: + reason = "cert issuer/issuer subject mismatch"; + break; + case X509_V_ERR_AKID_SKID_MISMATCH: + reason = "cert akid/issuer skid mismatch"; + break; + case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: + reason = "cert akid/issuer serial mismatch"; + break; + case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: + reason = "keyusage no certsign"; + break; + /* The application is not happy */ + case X509_V_ERR_APPLICATION_VERIFICATION: + reason = "application-specific verification error"; + break; + default: + reason = "unknown reason"; + } + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("certificate could not be verified: %s (%d)\n"), + reason, r); + return -1; + } + + /* do a reverse lookup on the server */ + len = sizeof(addr); + if (getpeername(conn->sock, &addr, &len) == -1) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("error querying socket: %s\n"), strerror(errno)); + return -1; + } + if (addr.sa_family != AF_INET) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("not on IPv4 socket\n")); + return -1; + } + + /* check the cert common name */ + conn->peer = SSL_get_peer_certificate(conn->ssl); + X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer), + NID_commonName, peerName, sizeof peerName); + if ((h = gethostbyname2(peerName, addr.sa_family)) == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("error looking up address %s: %s\n"), + peerName, hstrerror(h_errno)); + return -1; + } + + /* check for a match on actual socket address */ + sin1 = (struct sockaddr_in *) &addr; + for (s = h->h_addr_list; *s != NULL; s++) + { + sin2 = (struct sockaddr_in *) *s; + if (sin1->sin_addr.s_addr == sin2->sin_addr.s_addr) + break; + } + + /* if that failed, check for a match on alias */ + if (*s == NULL) + { + if (strcasecmp(peerName, conn->pghost) == 0) + ; + else + { + for (s = h->h_aliases; *s != NULL; s++) + { + if (strcasecmp(peerName, *s) == 0) + break; + } + if (*s == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "certificate name (%s) does not match peer address\n"), + peerName); + return -1; + } + } + } + + return 0; +} + +/* + * Close a SSL connection. + */ +void +close_SSL (PGconn *conn) +{ + if (conn->ssl) + { + SSL_shutdown(conn->ssl); + SSL_free(conn->ssl); + conn->ssl = NULL; + } +} + +/* + * Accessor function that retrieves SSL connection pointer. + */ +SSL * +PQgetssl (PGconn *conn) +{ + if (!conn) + return NULL; + return conn->ssl; +} + +/* + * Obtain reason string for last SSL error + * + * Some caution is needed here since ERR_reason_error_string will + * return NULL if it doesn't recognize the error code. We don't + * want to return NULL ever. + */ +static const char * +SSLerrmessage(void) +{ + unsigned long errcode; + const char *errreason; + static char errbuf[32]; + + errcode = ERR_get_error(); + if (errcode == 0) + return "No SSL error reported"; + errreason = ERR_reason_error_string(errcode); + if (errreason != NULL) + return errreason; + snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode); + return errbuf; +} + +/* + * The following conditional block shows how to embedded + * one or more root certs into the libpq library. This + * eliminates any need to copy the file to the clients, but + * obviously must be done on a per-site basis. + */ +#if 0 +/* + * The cert file, in PEM format, copied into a string buffer. + */ +static const char root1[] = +"-----BEGIN CERTIFICATE-----\n\ +MIIEqDCCBGagAwIBAgIBADALBgcqhkjOOAQDBQAwgYwxEzARBgoJkiaJk/IsZAEZ\n\ +EwNjb20xGjAYBgoJkiaJk/IsZAEZEwpjb3lvdGVzb25nMRIwEAYDVQQKEwlTbmFr\n\ +ZSBPaWwxHTAbBgNVBAMTFFBvc3RncmVTUUwgUm9vdCBDZXJ0MSYwJAYJKoZIhvcN\n\ +AQkBFhdwb3N0Z3Jlc0Bjb3lvdGVzb25nLmNvbTAeFw0wMjA1MjEwMDE4MDZaFw0w\n\ +MjA2MjAwMDE4MDZaMIGMMRMwEQYKCZImiZPyLGQBGRMDY29tMRowGAYKCZImiZPy\n\ +LGQBGRMKY295b3Rlc29uZzESMBAGA1UEChMJU25ha2UgT2lsMR0wGwYDVQQDExRQ\n\ +b3N0Z3JlU1FMIFJvb3QgQ2VydDEmMCQGCSqGSIb3DQEJARYXcG9zdGdyZXNAY295\n\ +b3Rlc29uZy5jb20wggG2MIIBKwYHKoZIzjgEATCCAR4CgYEAxgmwTdzv7eSqUjcS\n\ +8fdT/3lm+On8LmHL+CkmF7IlvZKm2kwIiQqjcrG6JqgXBdBTIzeqSZV8cGrc0/f5\n\ +zMh6rDVxuSrEwCh8DtAC9LdwWyHp7Tw79z9khkZNTAlBonwOLvm0BJaroH5FLK9S\n\ +PvAHmjmLA1zd/2K8o+CqFFJasTkCFQDXfI1tnskPUtPXz/W88wRg5y5zpQKBgGwk\n\ +3a+tfWmw2mMDXh2sSHoGwVlzwqKZnDfk97I7Tz/zmGOLEGdA7s+2YqKKfW7F0S8p\n\ +Ho/cYDNE2lyaGqaxl2pscqdIhEmKYjJtjgaOOkQwfaYXs5GY0zkiSaxxtvJTj0WK\n\ +OQ+J/0iunsyyukYc3+TiosHENz4Y2ZgaGseJTMz0A4GEAAKBgFG5WK5/64gjuJ7D\n\ +D4RQ7QZtZ+wxP4s3oEqphz4hPGpGOPYlHdo2PhHMEAVrgMnX44yqUAnwmG5LT1RI\n\ +5KPCDwgyxBQVq2FDJrYoRb/AVbqMQ8cyJZ1etd7J1ies31b3fHp+uYSFHuCmLfFp\n\ +RO8wLplYM6XmJ5X5BF8zlclDxIj/o4IBVTCCAVEwHQYDVR0OBBYEFMO7rhIEVsrn\n\ +6k/gxKR5bCdEo8jZMIG5BgNVHSMEgbEwga6AFMO7rhIEVsrn6k/gxKR5bCdEo8jZ\n\ +oYGSpIGPMIGMMRMwEQYKCZImiZPyLGQBGRMDY29tMRowGAYKCZImiZPyLGQBGRMK\n\ +Y295b3Rlc29uZzESMBAGA1UEChMJU25ha2UgT2lsMR0wGwYDVQQDExRQb3N0Z3Jl\n\ +U1FMIFJvb3QgQ2VydDEmMCQGCSqGSIb3DQEJARYXcG9zdGdyZXNAY295b3Rlc29u\n\ +Zy5jb22CAQAwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEB\n\ +BAQDAgEGMCIGA1UdEQQbMBmBF3Bvc3RncmVzQGNveW90ZXNvbmcuY29tMCIGA1Ud\n\ +EgQbMBmBF3Bvc3RncmVzQGNveW90ZXNvbmcuY29tMAsGByqGSM44BAMFAAMvADAs\n\ +AhUAhcafaeM39bK2z2tgRD8OLbrr3fICEwdVqUy9ykb9Hc7SjcKB51lUJ9s=\n\ +-----END CERTIFICATE-----\n"; + +static void +load_hardcoded_certs(void) +{ + X509_STORE *store; + X509 *x; + + store = SSL_CTX_get_cert_store(ctx); + if (store != NULL) + { + x = load_cert_buffer(root1, sizeof (root1)); + X509_STORE_add_cert(store, x); + X509_free(x); + + /* repeat as necessary.... */ + } +} +#else +static void +load_hardcoded_certs(void) +{ +} +#endif + +#endif /* USE_SSL */ diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 34308ae6ed..a874d6bfda 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-int.h,v 1.46 2002/03/05 06:07:27 momjian Exp $ + * $Id: libpq-int.h,v 1.47 2002/06/14 03:56:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -270,6 +270,7 @@ struct pg_conn bool allow_ssl_try; /* Allowed to try SSL negotiation */ bool require_ssl; /* Require SSL to make connection */ SSL *ssl; /* SSL status, if have SSL connection */ + X509 *peer; /* server certificate */ #endif /* Buffer for current error message */ diff --git a/src/interfaces/ssl/client.conf b/src/interfaces/ssl/client.conf new file mode 100644 index 0000000000..48793dc848 --- /dev/null +++ b/src/interfaces/ssl/client.conf @@ -0,0 +1,120 @@ +# +# PostgreSQL sample configuration for *client* cert. +# Contrast and compare with server.conf and root.conf. +# + +#################################################################### +[ req ] +default_bits = 1024 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +#x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString. +# utf8only: only UTF8Strings. +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings +# so use this option with caution! +string_mask = nombstr + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +0.domainComponent = domain name (TLD) +0.domainComponent_default = com +0.domainComponent_min = 2 +0.domainComponent_max = 3 + +1.domainComponent = domain name +1.domainComponent_default = example +1.domainComponent_min = 1 +1.domainComponent_max = 64 + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Snake Oil + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +#organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Your name +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 40 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +pgName = PostgreSQL user name +pgName_min = 1 +pgName_max = 12 + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +#nsComment = "OpenSSL Generated Certificate" +nsComment = "PostgreSQL/OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +subjectAltName=email:copy +subjectAltName=pgName + +# Copy subject details +issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + diff --git a/src/interfaces/ssl/mkcert.sh b/src/interfaces/ssl/mkcert.sh new file mode 100755 index 0000000000..8728a3a7a3 --- /dev/null +++ b/src/interfaces/ssl/mkcert.sh @@ -0,0 +1,114 @@ +#!/bin/sh + +# === FIRST DRAFT === + +PG_HOME=/var/lib/postgres +PG_DATA=$PG_HOME/data + +# default password for CA key +PASSWORD=postgresql + +# +# this script creates the root (CA) certificate and +# server cert for PostgreSQL. The OpenSSL applications +# must be in the path. +# + +if [ $PG_HOME"." = "." -o $PG_DATA"." = "." ] +then + /bin/echo You must define \$PG_HOME and \$PG_DATA before running this program. + exit 0 +fi + +# +# generate DSA parameters file used for keys, if one does +# not already exist. +# +if [ ! -f $PG_HOME/dsa1024.pem -o -z $PG_HOME/dsa1024.pem ] +then + openssl dsaparam -out $PG_HOME/dsa1024.pem 1024 +fi + +# +# generate CA directory tree and contents, if it does not already +# exist. +# +if [ ! -d $PG_HOME/CA ] +then + /bin/mkdir $PG_HOME/CA; +fi +if [ ! -d $PG_HOME/CA/certs ] +then + /bin/mkdir $PG_HOME/CA/certs +fi +if [ ! -d $PG_HOME/CA/crl ] +then + /bin/mkdir $PG_HOME/CA/crl +fi +if [ ! -d $PG_HOME/CA/newcerts ] +then + /bin/mkdir $PG_HOME/CA/newcerts +fi +if [ ! -d $PG_HOME/CA/private ] +then + /bin/mkdir $PG_HOME/CA/private + /bin/chmod 0700 $PG_HOME/CA/private +fi +if [ ! -f $PG_HOME/CA/index.txt ] +then + /usr/bin/touch $PG_HOME/CA/index.txt +fi +if [ ! -f $PG_HOME/CA/serial ] +then + /bin/echo 01 > $PG_HOME/CA/serial +fi + +# +# generate root key, if one does not already exist. +# +if [ ! -f $PG_HOME/CA/private/cakey.pem -o -z $PG_HOME/CA/private/cakey.pem ] +then + openssl gendsa $PG_HOME/dsa1024.pem |\ + openssl pkcs8 -topk8 -v2 bf -out $PG_HOME/CA/private/cakey.pem + /bin/chmod 0700 $PG_HOME/CA/private/cakey.pem +fi + +# +# generate self-signed root certificate, if one does not already exist +# +if [ ! -f $PG_HOME/CA/cacert.pem -o -z $PG_HOME/CA/cacert.pem ] +then + /bin/echo "Creating the root certificate...." + /bin/echo "" + openssl req -new -x509 -out $PG_HOME/CA/cacert.pem \ + -key $PG_HOME/CA/private/cakey.pem \ + -config $PG_HOME/root.conf + link -s $PG_HOME/CA/cacert.pem $PG_DATA/root.crt +fi + +# +# generate server key, if one does not already exist. +# +if [ ! -f $PG_DATA/server.key -o -z $PG_DATA/server.key ] +then + openssl gendsa -out $PG_DATA/server.key $PG_HOME/dsa1024.pem + /bin/chmod 0700 $PG_HOME/CA/private/cakey.pem +fi + +# +# generate server certificate, if one does not already exist. +# +if [ ! -f $PG_DATA/server.crt -o -z $PG_DATA/server.crt ] +then + /bin/echo "Creating the PostgreSQL server certificate...." + /bin/echo "" + openssl req -new -x509 -out $PG_DATA/server.self \ + -key $PG_DATA/server.key \ + -config $PG_HOME/server.conf + if [ -f $PG_DATA/server.self ] + then + openssl ca -out $PG_DATA/server.crt -ss_cert $PG_DATA/server.self \ + -config $PG_HOME/root.conf -extensions svr_cert + /bin/rm -f $PG_DATA/server.self + fi +fi diff --git a/src/interfaces/ssl/pgkeygen.sh b/src/interfaces/ssl/pgkeygen.sh new file mode 100644 index 0000000000..a65bb0fc4e --- /dev/null +++ b/src/interfaces/ssl/pgkeygen.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +echo \$HOME = $HOME + +CLIENTDIR=$HOME/.postgresql + +# +# copy root certificate, if necessary +# +if [ ! -f $CLIENTDIR/root.crt -o -z $CLIENTDIR/root.crt ] +then + if [ -f /etc/postgresql/root.crt ] + then + /bin/cp -p /etc/postgresql/root.crt $CLIENTDIR + fi +fi + +# +# generate client key, if one does not already exist. +# +if [ ! -f $CLIENTDIR/postgresql.key -o -z $CLIENTDIR/postgresql.key ] +then + if [ ! -f /etc/postgresql/dsa1024.pem -o -z /etc/postgresql/dsa1024.pem ] + then + /bin/echo "You must get the dsa1024.pem file from your DBA." + exit 0 + fi + openssl gendsa /etc/postgresql/dsa1024.pem |\ + openssl pkcs8 -topk8 -v2 bf -out $CLIENTDIR/postgresql.key + /bin/chmod 0600 $CLIENTDIR/postgresql.key +fi + +# +# generate client SS certificate, if one does not already exist. +# +if [ ! -f $CLIENTDIR/postgresql.crt -o -z $CLIENTDIR/postgresql.crt ] +then + if [ ! -f $CLIENTDIR/postgresql.pem -o -z $CLIENTDIR/postgresql.pem ] + then + /bin/echo "Creating client certificate...." + /bin/echo "" + openssl req -new -x509 -out $CLIENTDIR/postgresql.pem \ + -key $CLIENTDIR/postgresql.key -config /etc/postgresql/client.conf + /bin/echo "" + /bin/cat << EOM + +You must now provide a copy of your ~/.postgresql/postgresql.pem file +to your DBA for them to sign. When they have done so, you should rerun +this application. +EOM + else + cp -p $CLIENTDIR/postgresql.pem $CLIENTDIR/postgresql.crt + fi +fi diff --git a/src/interfaces/ssl/root.conf b/src/interfaces/ssl/root.conf new file mode 100644 index 0000000000..d7ed143b26 --- /dev/null +++ b/src/interfaces/ssl/root.conf @@ -0,0 +1,270 @@ +# +# PostgreSQL sample configuration for *root* cert. +# Contrast and compare with server.conf and client.conf. +# + +# define something in case $PG_HOME isn't defined. +PG_HOME = /var/lib/postgres + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = $ENV::PG_HOME/CA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = clnt_cert # The extentions to add to the cert + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = sha1 # which md to use. +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +domainComponent = match +#1.domainComponent = match +#organizationName = match +#organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +domainComponent = optional +#1.domainComponent = optional +#countryName = optional +#stateOrProvinceName = optional +#localityName = optional +#organizationName = optional +#organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 1024 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString. +# utf8only: only UTF8Strings. +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings +# so use this option with caution! +string_mask = nombstr + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +0.domainComponent = domain name (TLD) +0.domainComponent_default = com +0.domainComponent_min = 2 +0.domainComponent_max = 3 + +1.domainComponent = domain name +1.domainComponent_default = example +1.domainComponent_min = 1 +1.domainComponent_max = 64 + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Snake Oil + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +#organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name +commonName_value = PostgreSQL Root Cert +#commonName_max = 64 + +emailAddress = Email Address +emailAddress_default = postgres@example.com +emailAddress_max = 40 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] + +[ svr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +#nsComment = "OpenSSL Generated Certificate" +nsComment = "PostgreSQL/OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +subjectAltName=email:copy + +# Copy subject details +issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +[ clnt_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +#nsComment = "OpenSSL Generated Certificate" +nsComment = "PostgreSQL/OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +subjectAltName=email:copy + +# Copy subject details +issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + +# Extensions for a typical CA + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer:always + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +keyUsage = cRLSign, keyCertSign + +# Some might want this also +nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +subjectAltName=email:copy +# Copy issuer details +issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always,issuer:always diff --git a/src/interfaces/ssl/server.conf b/src/interfaces/ssl/server.conf new file mode 100644 index 0000000000..fe6cfe4106 --- /dev/null +++ b/src/interfaces/ssl/server.conf @@ -0,0 +1,118 @@ +# +# PostgreSQL sample configuration for *server* cert. +# Contrast and compare with root.conf and client.conf. +# + +#################################################################### +[ req ] +default_bits = 1024 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +#x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString. +# utf8only: only UTF8Strings. +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings +# so use this option with caution! +string_mask = nombstr + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +0.domainComponent = domain name (TLD) +0.domainComponent_default = com +0.domainComponent_min = 2 +0.domainComponent_max = 3 + +1.domainComponent = domain name +1.domainComponent_default = example +1.domainComponent_min = 1 +1.domainComponent_max = 64 + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Snake Oil + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +#organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = FQDN of server +commonName_default = postgres.example.com +commonName_max = 64 + +emailAddress = Email Address +emailAddress_default = postgres@example.com +emailAddress_max = 40 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +#nsComment = "OpenSSL Generated Certificate" +nsComment = "PostgreSQL/OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +subjectAltName=email:copy + +# Copy subject details +issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +