fe-secure.c 30 KB
Newer Older
Bruce Momjian's avatar
Bruce Momjian committed
1 2
/*-------------------------------------------------------------------------
 *
3
 * fe-secure.c
Bruce Momjian's avatar
Bruce Momjian committed
4 5 6 7 8
 *	  functions related to setting up a secure connection to the backend.
 *	  Secure connections are expected to provide confidentiality,
 *	  message integrity and endpoint authentication.
 *
 *
Bruce Momjian's avatar
Bruce Momjian committed
9
 * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
Bruce Momjian's avatar
Bruce Momjian committed
10 11 12 13
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
14
 *	  $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.51 2004/09/23 20:27:43 tgl Exp $
Bruce Momjian's avatar
Bruce Momjian committed
15
 *
Bruce Momjian's avatar
Bruce Momjian committed
16 17 18 19 20 21 22 23
 * NOTES
 *	  The client *requires* a valid server certificate.  Since
 *	  SSH tunnels provide anonymous confidentiality, the presumption
 *	  is that sites that want endpoint authentication will use the
 *	  direct SSL support, while sites that are comfortable with
 *	  anonymous connections will use SSH tunnels.
 *
 *	  This code verifies the server certificate, to detect simple
Bruce Momjian's avatar
Bruce Momjian committed
24
 *	  "man-in-the-middle" and "impersonation" attacks.	The
Bruce Momjian's avatar
Bruce Momjian committed
25 26 27
 *	  server certificate, or better yet the CA certificate used
 *	  to sign the server certificate, should be present in the
 *	  "$HOME/.postgresql/root.crt" file.  If this file isn't
Bruce Momjian's avatar
Bruce Momjian committed
28
 *	  readable, or the server certificate can't be validated,
29
 *	  pqsecure_open_client() will return an error code.
Bruce Momjian's avatar
Bruce Momjian committed
30 31 32 33 34
 *
 *	  Additionally, the server certificate's "common name" must
 *	  resolve to the other end of the socket.  This makes it
 *	  substantially harder to pull off a "man-in-the-middle" or
 *	  "impersonation" attack even if the server's private key
Bruce Momjian's avatar
Bruce Momjian committed
35
 *	  has been stolen.	This check limits acceptable network
Bruce Momjian's avatar
Bruce Momjian committed
36 37 38 39 40
 *	  layers to Unix sockets (weird, but legal), TCPv4 and TCPv6.
 *
 *	  Unfortunately neither the current front- or back-end handle
 *	  failure gracefully, resulting in the backend hiccupping.
 *	  This points out problems in each (the frontend shouldn't even
41
 *	  try to do SSL if pqsecure_initialize() fails, and the backend
Bruce Momjian's avatar
Bruce Momjian committed
42
 *	  shouldn't crash/recover if an SSH negotiation fails.  The
Bruce Momjian's avatar
Bruce Momjian committed
43
 *	  backend definitely needs to be fixed, to prevent a "denial
Bruce Momjian's avatar
Bruce Momjian committed
44
 *	  of service" attack, but I don't know enough about how the
Bruce Momjian's avatar
Bruce Momjian committed
45 46 47
 *	  backend works (especially that pre-SSL negotiation) to identify
 *	  a fix.
 *
48 49 50 51
 *	  ...
 *
 *	  Unlike the server's static private key, the client's
 *	  static private key ($HOME/.postgresql/postgresql.key)
Bruce Momjian's avatar
Bruce Momjian committed
52
 *	  should normally be stored encrypted.	However we still
53 54
 *	  support EPH since it's useful for other reasons.
 *
55 56 57 58 59 60 61 62 63
 *	  ...
 *
 *	  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
Bruce Momjian's avatar
Bruce Momjian committed
64
 *		$HOME/.postgresql/postgresql.crt
65
 *	  and
Bruce Momjian's avatar
Bruce Momjian committed
66
 *		$HOME/.postgresql/postgresql.key
67 68
 *	  respectively.
 *
69 70 71 72 73 74
 *	  ...
 *
 *	  We don't provide informational callbacks here (like
 *	  info_cb() in be-secure.c), since there's mechanism to
 *	  display that information to the client.
 *
Bruce Momjian's avatar
Bruce Momjian committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
 * OS DEPENDENCIES
 *	  The code currently assumes a POSIX password entry.  How should
 *	  Windows and Mac users be handled?
 *
 *-------------------------------------------------------------------------
 */

#include "postgres_fe.h"

#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>

#include "libpq-fe.h"
#include "libpq-int.h"
#include "fe-auth.h"
#include "pqsignal.h"

#ifdef WIN32
#include "win32.h"
#else
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#include <arpa/inet.h>
#endif

109 110 111 112
#ifdef ENABLE_THREAD_SAFETY
#include <pthread.h>
#endif

Bruce Momjian's avatar
Bruce Momjian committed
113 114 115 116
#ifndef HAVE_STRDUP
#include "strdup.h"
#endif

117
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
118
#include <pwd.h>
119
#endif
Bruce Momjian's avatar
Bruce Momjian committed
120 121 122 123
#include <sys/stat.h>

#ifdef USE_SSL
#include <openssl/ssl.h>
124
#include <openssl/dh.h>
Bruce Momjian's avatar
Bruce Momjian committed
125
#endif   /* USE_SSL */
Bruce Momjian's avatar
Bruce Momjian committed
126 127 128


#ifdef USE_SSL
Bruce Momjian's avatar
Bruce Momjian committed
129
static int	verify_cb(int ok, X509_STORE_CTX *ctx);
Bruce Momjian's avatar
Bruce Momjian committed
130

131
#ifdef NOT_USED
Bruce Momjian's avatar
Bruce Momjian committed
132
static int	verify_peer(PGconn *);
133
#endif
Bruce Momjian's avatar
Bruce Momjian committed
134 135 136 137
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 **);
138
static int	init_ssl_system(PGconn *conn);
Bruce Momjian's avatar
Bruce Momjian committed
139
static int	initialize_SSL(PGconn *);
Bruce Momjian's avatar
Bruce Momjian committed
140
static void destroy_SSL(void);
141
static PostgresPollingStatusType open_client_SSL(PGconn *);
Bruce Momjian's avatar
Bruce Momjian committed
142
static void close_SSL(PGconn *);
143 144
static char *SSLerrmessage(void);
static void SSLerrfree(char *buf);
Bruce Momjian's avatar
Bruce Momjian committed
145 146 147
#endif

#ifdef USE_SSL
Bruce Momjian's avatar
Bruce Momjian committed
148
bool		pq_initssllib = true;
149

Bruce Momjian's avatar
Bruce Momjian committed
150 151 152
static SSL_CTX *SSL_context = NULL;
#endif

153 154
#ifdef ENABLE_THREAD_SAFETY
static void sigpipe_handler_ignore_send(int signo);
155 156
pthread_key_t pq_thread_in_send = 0;	/* initializer needed on Darwin */
static pqsigfunc pq_pipe_handler;
157 158
#endif

159
/* ------------------------------------------------------------ */
Bruce Momjian's avatar
Bruce Momjian committed
160
/*						 Hardcoded values						*/
161 162 163 164 165 166 167
/* ------------------------------------------------------------ */

/*
 *	Hardcoded DH parameters, used in empheral DH keying.
 *	As discussed above, EDH protects the confidentiality of
 *	sessions even if the static private key is compromised,
 *	so we are *highly* motivated to ensure that we can use
Bruce Momjian's avatar
Bruce Momjian committed
168
 *	EDH even if the user... or an attacker... deletes the
169 170 171 172 173
 *	$HOME/.postgresql/dh*.pem files.
 *
 *	It's not critical that users have EPH keys, but it doesn't
 *	hurt and if it's missing someone will demand it, so....
 */
174 175
#ifdef USE_SSL

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
static const char file_dh512[] =
"-----BEGIN DH PARAMETERS-----\n\
MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
-----END DH PARAMETERS-----\n";

static const char file_dh1024[] =
"-----BEGIN DH PARAMETERS-----\n\
MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
-----END DH PARAMETERS-----\n";

static const char file_dh2048[] =
"-----BEGIN DH PARAMETERS-----\n\
MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
-----END DH PARAMETERS-----\n";

static const char file_dh4096[] =
"-----BEGIN DH PARAMETERS-----\n\
MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
-----END DH PARAMETERS-----\n";
213 214
#endif

Bruce Momjian's avatar
Bruce Momjian committed
215
/* ------------------------------------------------------------ */
Bruce Momjian's avatar
Bruce Momjian committed
216
/*			 Procedures common to all secure sessions			*/
Bruce Momjian's avatar
Bruce Momjian committed
217 218 219 220 221 222
/* ------------------------------------------------------------ */

/*
 *	Initialize global context
 */
int
Bruce Momjian's avatar
Bruce Momjian committed
223
pqsecure_initialize(PGconn *conn)
Bruce Momjian's avatar
Bruce Momjian committed
224
{
Bruce Momjian's avatar
Bruce Momjian committed
225
	int			r = 0;
Bruce Momjian's avatar
Bruce Momjian committed
226 227 228 229 230 231 232 233 234 235 236 237

#ifdef USE_SSL
	r = initialize_SSL(conn);
#endif

	return r;
}

/*
 *	Destroy global context
 */
void
Bruce Momjian's avatar
Bruce Momjian committed
238
pqsecure_destroy(void)
Bruce Momjian's avatar
Bruce Momjian committed
239 240 241 242 243 244 245 246 247
{
#ifdef USE_SSL
	destroy_SSL();
#endif
}

/*
 *	Attempt to negotiate secure session.
 */
248
PostgresPollingStatusType
Bruce Momjian's avatar
Bruce Momjian committed
249
pqsecure_open_client(PGconn *conn)
Bruce Momjian's avatar
Bruce Momjian committed
250 251
{
#ifdef USE_SSL
252 253 254 255 256 257 258
	/* First time through? */
	if (conn->ssl == NULL)
	{
		if (!(conn->ssl = SSL_new(SSL_context)) ||
			!SSL_set_app_data(conn->ssl, conn) ||
			!SSL_set_fd(conn->ssl, conn->sock))
		{
Bruce Momjian's avatar
Bruce Momjian committed
259 260
			char	   *err = SSLerrmessage();

261 262
			printfPQExpBuffer(&conn->errorMessage,
			   libpq_gettext("could not establish SSL connection: %s\n"),
263 264
							  err);
			SSLerrfree(err);
265 266 267 268 269 270 271 272 273
			close_SSL(conn);
			return PGRES_POLLING_FAILED;
		}
	}
	/* Begin or continue the actual handshake */
	return open_client_SSL(conn);
#else
	/* shouldn't get here */
	return PGRES_POLLING_FAILED;
Bruce Momjian's avatar
Bruce Momjian committed
274 275 276 277 278 279 280
#endif
}

/*
 *	Close secure session.
 */
void
Bruce Momjian's avatar
Bruce Momjian committed
281
pqsecure_close(PGconn *conn)
Bruce Momjian's avatar
Bruce Momjian committed
282 283 284 285 286 287 288 289 290 291 292
{
#ifdef USE_SSL
	if (conn->ssl)
		close_SSL(conn);
#endif
}

/*
 *	Read data from a secure connection.
 */
ssize_t
Bruce Momjian's avatar
Bruce Momjian committed
293
pqsecure_read(PGconn *conn, void *ptr, size_t len)
Bruce Momjian's avatar
Bruce Momjian committed
294
{
Bruce Momjian's avatar
Bruce Momjian committed
295
	ssize_t		n;
Bruce Momjian's avatar
Bruce Momjian committed
296 297 298 299

#ifdef USE_SSL
	if (conn->ssl)
	{
300 301
		int			err;

Bruce Momjian's avatar
Bruce Momjian committed
302
rloop:
Bruce Momjian's avatar
Bruce Momjian committed
303
		n = SSL_read(conn->ssl, ptr, len);
304 305
		err = SSL_get_error(conn->ssl, n);
		switch (err)
Bruce Momjian's avatar
Bruce Momjian committed
306
		{
Bruce Momjian's avatar
Bruce Momjian committed
307 308 309
			case SSL_ERROR_NONE:
				break;
			case SSL_ERROR_WANT_READ:
310 311
				n = 0;
				break;
312
			case SSL_ERROR_WANT_WRITE:
Bruce Momjian's avatar
Bruce Momjian committed
313

314
				/*
Bruce Momjian's avatar
Bruce Momjian committed
315 316 317 318 319
				 * Returning 0 here would cause caller to wait for
				 * read-ready, which is not correct since what SSL wants
				 * is wait for write-ready.  The former could get us stuck
				 * in an infinite wait, so don't risk it; busy-loop
				 * instead.
320
				 */
321
				goto rloop;
Bruce Momjian's avatar
Bruce Momjian committed
322
			case SSL_ERROR_SYSCALL:
Bruce Momjian's avatar
Bruce Momjian committed
323 324 325 326 327
				{
					char		sebuf[256];

					if (n == -1)
						printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
328
								libpq_gettext("SSL SYSCALL error: %s\n"),
Bruce Momjian's avatar
Bruce Momjian committed
329 330
						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
					else
331
					{
Bruce Momjian's avatar
Bruce Momjian committed
332 333
						printfPQExpBuffer(&conn->errorMessage,
										  libpq_gettext("SSL SYSCALL error: EOF detected\n"));
334

335
						SOCK_ERRNO_SET(ECONNRESET);
336 337
						n = -1;
					}
Bruce Momjian's avatar
Bruce Momjian committed
338 339
					break;
				}
Bruce Momjian's avatar
Bruce Momjian committed
340
			case SSL_ERROR_SSL:
341
				{
Bruce Momjian's avatar
Bruce Momjian committed
342 343
					char	   *err = SSLerrmessage();

344
					printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
345
								  libpq_gettext("SSL error: %s\n"), err);
346 347
					SSLerrfree(err);
				}
Bruce Momjian's avatar
Bruce Momjian committed
348 349
				/* fall through */
			case SSL_ERROR_ZERO_RETURN:
350
				SOCK_ERRNO_SET(ECONNRESET);
Bruce Momjian's avatar
Bruce Momjian committed
351 352
				n = -1;
				break;
353 354
			default:
				printfPQExpBuffer(&conn->errorMessage,
355 356
						 libpq_gettext("unrecognized SSL error code: %d\n"),
								  err);
357
				n = -1;
358
				break;
Bruce Momjian's avatar
Bruce Momjian committed
359 360 361 362
		}
	}
	else
#endif
Bruce Momjian's avatar
Bruce Momjian committed
363
		n = recv(conn->sock, ptr, len, 0);
Bruce Momjian's avatar
Bruce Momjian committed
364 365 366 367 368 369 370 371

	return n;
}

/*
 *	Write data to a secure connection.
 */
ssize_t
Bruce Momjian's avatar
Bruce Momjian committed
372
pqsecure_write(PGconn *conn, const void *ptr, size_t len)
Bruce Momjian's avatar
Bruce Momjian committed
373
{
Bruce Momjian's avatar
Bruce Momjian committed
374
	ssize_t		n;
Bruce Momjian's avatar
Bruce Momjian committed
375

376
#ifdef ENABLE_THREAD_SAFETY
377
	pthread_setspecific(pq_thread_in_send, "t");
378
#else
Bruce Momjian's avatar
Bruce Momjian committed
379
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
380
	pqsigfunc	oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
Bruce Momjian's avatar
Bruce Momjian committed
381
#endif
382
#endif
Bruce Momjian's avatar
Bruce Momjian committed
383 384 385 386

#ifdef USE_SSL
	if (conn->ssl)
	{
387 388
		int			err;

Bruce Momjian's avatar
Bruce Momjian committed
389
		n = SSL_write(conn->ssl, ptr, len);
390 391
		err = SSL_get_error(conn->ssl, n);
		switch (err)
Bruce Momjian's avatar
Bruce Momjian committed
392
		{
Bruce Momjian's avatar
Bruce Momjian committed
393 394
			case SSL_ERROR_NONE:
				break;
395
			case SSL_ERROR_WANT_READ:
Bruce Momjian's avatar
Bruce Momjian committed
396

397 398 399 400 401 402 403
				/*
				 * Returning 0 here causes caller to wait for write-ready,
				 * which is not really the right thing, but it's the best
				 * we can do.
				 */
				n = 0;
				break;
Bruce Momjian's avatar
Bruce Momjian committed
404
			case SSL_ERROR_WANT_WRITE:
405 406
				n = 0;
				break;
Bruce Momjian's avatar
Bruce Momjian committed
407
			case SSL_ERROR_SYSCALL:
Bruce Momjian's avatar
Bruce Momjian committed
408 409
				{
					char		sebuf[256];
410

Bruce Momjian's avatar
Bruce Momjian committed
411 412
					if (n == -1)
						printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
413
								libpq_gettext("SSL SYSCALL error: %s\n"),
Bruce Momjian's avatar
Bruce Momjian committed
414 415
						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
					else
416
					{
Bruce Momjian's avatar
Bruce Momjian committed
417 418
						printfPQExpBuffer(&conn->errorMessage,
										  libpq_gettext("SSL SYSCALL error: EOF detected\n"));
419
						SOCK_ERRNO_SET(ECONNRESET);
420 421
						n = -1;
					}
Bruce Momjian's avatar
Bruce Momjian committed
422 423
					break;
				}
Bruce Momjian's avatar
Bruce Momjian committed
424
			case SSL_ERROR_SSL:
425
				{
Bruce Momjian's avatar
Bruce Momjian committed
426 427
					char	   *err = SSLerrmessage();

428
					printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
429
								  libpq_gettext("SSL error: %s\n"), err);
430 431
					SSLerrfree(err);
				}
Bruce Momjian's avatar
Bruce Momjian committed
432 433
				/* fall through */
			case SSL_ERROR_ZERO_RETURN:
434
				SOCK_ERRNO_SET(ECONNRESET);
Bruce Momjian's avatar
Bruce Momjian committed
435 436
				n = -1;
				break;
437 438
			default:
				printfPQExpBuffer(&conn->errorMessage,
439 440
						 libpq_gettext("unrecognized SSL error code: %d\n"),
								  err);
441
				n = -1;
442
				break;
Bruce Momjian's avatar
Bruce Momjian committed
443 444 445 446
		}
	}
	else
#endif
Bruce Momjian's avatar
Bruce Momjian committed
447
		n = send(conn->sock, ptr, len, 0);
Bruce Momjian's avatar
Bruce Momjian committed
448

449
#ifdef ENABLE_THREAD_SAFETY
450
	pthread_setspecific(pq_thread_in_send, "f");
451
#else
Bruce Momjian's avatar
Bruce Momjian committed
452 453
#ifndef WIN32
	pqsignal(SIGPIPE, oldsighandler);
454
#endif
Bruce Momjian's avatar
Bruce Momjian committed
455 456 457 458 459 460
#endif

	return n;
}

/* ------------------------------------------------------------ */
Bruce Momjian's avatar
Bruce Momjian committed
461
/*						  SSL specific code						*/
Bruce Momjian's avatar
Bruce Momjian committed
462 463 464 465 466 467 468 469 470 471 472 473 474 475
/* ------------------------------------------------------------ */
#ifdef USE_SSL
/*
 *	Certificate verification callback
 *
 *	This callback allows us to log intermediate problems during
 *	verification, but there doesn't seem to be a clean way to get
 *	our PGconn * structure.  So we can't log anything!
 *
 *	This callback also allows us to override the default acceptance
 *	criteria (e.g., accepting self-signed or expired certs), but
 *	for now we accept the default checks.
 */
static int
Bruce Momjian's avatar
Bruce Momjian committed
476
verify_cb(int ok, X509_STORE_CTX *ctx)
Bruce Momjian's avatar
Bruce Momjian committed
477 478 479 480
{
	return ok;
}

481
#ifdef NOT_USED
Bruce Momjian's avatar
Bruce Momjian committed
482 483 484 485
/*
 *	Verify that common name resolves to peer.
 */
static int
Bruce Momjian's avatar
Bruce Momjian committed
486
verify_peer(PGconn *conn)
Bruce Momjian's avatar
Bruce Momjian committed
487 488 489 490
{
	struct hostent *h = NULL;
	struct sockaddr addr;
	struct sockaddr_in *sin;
Bruce Momjian's avatar
Bruce Momjian committed
491 492
	socklen_t	len;
	char	  **s;
Bruce Momjian's avatar
Bruce Momjian committed
493 494 495 496 497 498
	unsigned long l;

	/* get the address on the other side of the socket */
	len = sizeof(addr);
	if (getpeername(conn->sock, &addr, &len) == -1)
	{
Bruce Momjian's avatar
Bruce Momjian committed
499 500
		char		sebuf[256];

Bruce Momjian's avatar
Bruce Momjian committed
501
		printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
502
						  libpq_gettext("error querying socket: %s\n"),
Bruce Momjian's avatar
Bruce Momjian committed
503
						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
Bruce Momjian's avatar
Bruce Momjian committed
504 505 506 507 508 509 510
		return -1;
	}

	/* weird, but legal case */
	if (addr.sa_family == AF_UNIX)
		return 0;

511 512
	{
		struct hostent hpstr;
Bruce Momjian's avatar
Bruce Momjian committed
513 514
		char		buf[BUFSIZ];
		int			herrno = 0;
Bruce Momjian's avatar
Bruce Momjian committed
515

516
		/*
Bruce Momjian's avatar
Bruce Momjian committed
517 518 519 520
		 * Currently, pqGethostbyname() is used only on platforms that
		 * don't have getaddrinfo().  If you enable this function, you
		 * should convert the pqGethostbyname() function call to use
		 * getaddrinfo().
521
		 */
522
		pqGethostbyname(conn->peer_cn, &hpstr, buf, sizeof(buf),
Bruce Momjian's avatar
Bruce Momjian committed
523
						&h, &herrno);
524
	}
Bruce Momjian's avatar
Bruce Momjian committed
525

Bruce Momjian's avatar
Bruce Momjian committed
526
	/* what do we know about the peer's common name? */
527
	if (h == NULL)
Bruce Momjian's avatar
Bruce Momjian committed
528 529
	{
		printfPQExpBuffer(&conn->errorMessage,
530
		libpq_gettext("could not get information about host (%s): %s\n"),
Bruce Momjian's avatar
Bruce Momjian committed
531
						  conn->peer_cn, hstrerror(h_errno));
Bruce Momjian's avatar
Bruce Momjian committed
532 533 534 535 536 537
		return -1;
	}

	/* does the address match? */
	switch (addr.sa_family)
	{
Bruce Momjian's avatar
Bruce Momjian committed
538 539 540 541 542 543 544 545
		case AF_INET:
			sin = (struct sockaddr_in *) & addr;
			for (s = h->h_addr_list; *s != NULL; s++)
			{
				if (!memcmp(&sin->sin_addr.s_addr, *s, h->h_length))
					return 0;
			}
			break;
Bruce Momjian's avatar
Bruce Momjian committed
546

Bruce Momjian's avatar
Bruce Momjian committed
547 548
		default:
			printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
549
							  libpq_gettext("unsupported protocol\n"));
Bruce Momjian's avatar
Bruce Momjian committed
550
			return -1;
Bruce Momjian's avatar
Bruce Momjian committed
551 552
	}

Bruce Momjian's avatar
Bruce Momjian committed
553 554 555 556
	/*
	 * the prior test should be definitive, but in practice it sometimes
	 * fails.  So we also check the aliases.
	 */
Bruce Momjian's avatar
Bruce Momjian committed
557 558
	for (s = h->h_aliases; *s != NULL; s++)
	{
559
		if (pg_strcasecmp(conn->peer_cn, *s) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
560 561 562 563 564 565
			return 0;
	}

	/* generate protocol-aware error message */
	switch (addr.sa_family)
	{
Bruce Momjian's avatar
Bruce Momjian committed
566 567 568 569 570
		case AF_INET:
			sin = (struct sockaddr_in *) & addr;
			l = ntohl(sin->sin_addr.s_addr);
			printfPQExpBuffer(&conn->errorMessage,
							  libpq_gettext(
571
											"server common name \"%s\" does not resolve to %ld.%ld.%ld.%ld\n"),
Bruce Momjian's avatar
Bruce Momjian committed
572 573 574 575 576 577
					 conn->peer_cn, (l >> 24) % 0x100, (l >> 16) % 0x100,
							  (l >> 8) % 0x100, l % 0x100);
			break;
		default:
			printfPQExpBuffer(&conn->errorMessage,
							  libpq_gettext(
Bruce Momjian's avatar
Bruce Momjian committed
578
											"server common name \"%s\" does not resolve to peer address\n"),
Bruce Momjian's avatar
Bruce Momjian committed
579
							  conn->peer_cn);
Bruce Momjian's avatar
Bruce Momjian committed
580 581 582 583
	}

	return -1;
}
584
#endif
Bruce Momjian's avatar
Bruce Momjian committed
585

586 587 588 589
/*
 *	Load precomputed DH parameters.
 *
 *	To prevent "downgrade" attacks, we perform a number of checks
Bruce Momjian's avatar
Bruce Momjian committed
590
 *	to verify that the DBA-generated DH parameters file contains
591 592
 *	what we expect it to contain.
 */
Bruce Momjian's avatar
Bruce Momjian committed
593 594
static DH  *
load_dh_file(int keylength)
595
{
596
#ifdef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
597
	return NULL;
598
#else
Bruce Momjian's avatar
Bruce Momjian committed
599
	char		pwdbuf[BUFSIZ];
600 601
	struct passwd pwdstr;
	struct passwd *pwd = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
602 603 604 605
	FILE	   *fp;
	char		fnbuf[2048];
	DH		   *dh = NULL;
	int			codes;
606

Bruce Momjian's avatar
Bruce Momjian committed
607 608
	if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0)
		return NULL;
609 610 611

	/* attempt to open file.  It's not an error if it doesn't exist. */
	snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/dh%d.pem",
Bruce Momjian's avatar
Bruce Momjian committed
612
			 pwd->pw_dir, keylength);
613

614 615 616 617 618 619 620 621 622
	if ((fp = fopen(fnbuf, "r")) == NULL)
		return NULL;

/*	flock(fileno(fp), LOCK_SH); */
	dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
/*	flock(fileno(fp), LOCK_UN); */
	fclose(fp);

	/* is the prime the correct size? */
Bruce Momjian's avatar
Bruce Momjian committed
623
	if (dh != NULL && 8 * DH_size(dh) < keylength)
624 625 626 627 628 629 630 631 632
		dh = NULL;

	/* make sure the DH parameters are usable */
	if (dh != NULL)
	{
		if (DH_check(dh, &codes))
			return NULL;
		if (codes & DH_CHECK_P_NOT_PRIME)
			return NULL;
Bruce Momjian's avatar
Bruce Momjian committed
633
		if ((codes & DH_NOT_SUITABLE_GENERATOR) &&
634 635 636 637 638
			(codes & DH_CHECK_P_NOT_SAFE_PRIME))
			return NULL;
	}

	return dh;
639
#endif
640 641 642 643 644 645 646 647
}

/*
 *	Load hardcoded DH parameters.
 *
 *	To prevent problems if the DH parameters files don't even
 *	exist, we can load DH parameters hardcoded into this file.
 */
Bruce Momjian's avatar
Bruce Momjian committed
648 649
static DH  *
load_dh_buffer(const char *buffer, size_t len)
650
{
Bruce Momjian's avatar
Bruce Momjian committed
651 652
	BIO		   *bio;
	DH		   *dh = NULL;
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675

	bio = BIO_new_mem_buf((char *) buffer, len);
	if (bio == NULL)
		return NULL;
	dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
	BIO_free(bio);

	return dh;
}

/*
 *	Generate an empheral DH key.  Because this can take a long
 *	time to compute, we can use precomputed parameters of the
 *	common key sizes.
 *
 *	Since few sites will bother to precompute these parameter
 *	files, we also provide a fallback to the parameters provided
 *	by the OpenSSL project.
 *
 *	These values can be static (once loaded or computed) since
 *	the OpenSSL library can efficiently generate random keys from
 *	the information provided.
 */
Bruce Momjian's avatar
Bruce Momjian committed
676 677
static DH  *
tmp_dh_cb(SSL *s, int is_export, int keylength)
678
{
Bruce Momjian's avatar
Bruce Momjian committed
679 680 681 682 683 684
	DH		   *r = NULL;
	static DH  *dh = NULL;
	static DH  *dh512 = NULL;
	static DH  *dh1024 = NULL;
	static DH  *dh2048 = NULL;
	static DH  *dh4096 = NULL;
685 686 687

	switch (keylength)
	{
Bruce Momjian's avatar
Bruce Momjian committed
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
		case 512:
			if (dh512 == NULL)
				dh512 = load_dh_file(keylength);
			if (dh512 == NULL)
				dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
			r = dh512;
			break;

		case 1024:
			if (dh1024 == NULL)
				dh1024 = load_dh_file(keylength);
			if (dh1024 == NULL)
				dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
			r = dh1024;
			break;

		case 2048:
			if (dh2048 == NULL)
				dh2048 = load_dh_file(keylength);
			if (dh2048 == NULL)
				dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
			r = dh2048;
			break;

		case 4096:
			if (dh4096 == NULL)
				dh4096 = load_dh_file(keylength);
			if (dh4096 == NULL)
				dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
			r = dh4096;
			break;

		default:
			if (dh == NULL)
				dh = load_dh_file(keylength);
			r = dh;
724 725 726
	}

	/* this may take a long time, but it may be necessary... */
Bruce Momjian's avatar
Bruce Momjian committed
727
	if (r == NULL || 8 * DH_size(r) < keylength)
728
		r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
Bruce Momjian's avatar
Bruce Momjian committed
729

730 731 732
	return r;
}

733 734 735 736 737 738 739 740
/*
 *	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
Bruce Momjian's avatar
Bruce Momjian committed
741
client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
742
{
743
#ifdef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
744
	return 0;
745
#else
Bruce Momjian's avatar
Bruce Momjian committed
746
	char		pwdbuf[BUFSIZ];
747 748
	struct passwd pwdstr;
	struct passwd *pwd = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
749 750 751 752 753 754
	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 */
Bruce Momjian's avatar
Bruce Momjian committed
755
	char		sebuf[256];
756

757

Bruce Momjian's avatar
Bruce Momjian committed
758
	if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0)
759
	{
Bruce Momjian's avatar
Bruce Momjian committed
760
		printfPQExpBuffer(&conn->errorMessage,
761
					  libpq_gettext("could not get user information\n"));
762 763 764 765 766
		return -1;
	}

	/* read the user certificate */
	snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.crt",
Bruce Momjian's avatar
Bruce Momjian committed
767
			 pwd->pw_dir);
768 769 770 771
	if (stat(fnbuf, &buf) == -1)
		return 0;
	if ((fp = fopen(fnbuf, "r")) == NULL)
	{
Bruce Momjian's avatar
Bruce Momjian committed
772
		printfPQExpBuffer(&conn->errorMessage,
773
				  libpq_gettext("could not open certificate (%s): %s\n"),
774
						  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
775 776 777 778
		return -1;
	}
	if (PEM_read_X509(fp, x509, NULL, NULL) == NULL)
	{
Bruce Momjian's avatar
Bruce Momjian committed
779 780
		char	   *err = SSLerrmessage();

Bruce Momjian's avatar
Bruce Momjian committed
781
		printfPQExpBuffer(&conn->errorMessage,
782
				  libpq_gettext("could not read certificate (%s): %s\n"),
783 784
						  fnbuf, err);
		SSLerrfree(err);
785 786 787 788 789 790 791
		fclose(fp);
		return -1;
	}
	fclose(fp);

	/* read the user key */
	snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.key",
Bruce Momjian's avatar
Bruce Momjian committed
792
			 pwd->pw_dir);
793 794
	if (stat(fnbuf, &buf) == -1)
	{
Bruce Momjian's avatar
Bruce Momjian committed
795 796 797
		printfPQExpBuffer(&conn->errorMessage,
		libpq_gettext("certificate present, but not private key (%s)\n"),
						  fnbuf);
798 799 800 801 802 803
		X509_free(*x509);
		return 0;
	}
	if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) ||
		buf.st_uid != getuid())
	{
Bruce Momjian's avatar
Bruce Momjian committed
804
		printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
805
		libpq_gettext("private key (%s) has wrong permissions\n"), fnbuf);
806 807 808 809 810
		X509_free(*x509);
		return -1;
	}
	if ((fp = fopen(fnbuf, "r")) == NULL)
	{
Bruce Momjian's avatar
Bruce Momjian committed
811
		printfPQExpBuffer(&conn->errorMessage,
812
			 libpq_gettext("could not open private key file (%s): %s\n"),
813
						  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
814 815 816 817 818 819
		X509_free(*x509);
		return -1;
	}
	if (fstat(fileno(fp), &buf2) == -1 ||
		buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
	{
Bruce Momjian's avatar
Bruce Momjian committed
820
		printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
821
						  libpq_gettext("private key (%s) changed during execution\n"), fnbuf);
822 823 824 825 826
		X509_free(*x509);
		return -1;
	}
	if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL)
	{
Bruce Momjian's avatar
Bruce Momjian committed
827 828
		char	   *err = SSLerrmessage();

Bruce Momjian's avatar
Bruce Momjian committed
829
		printfPQExpBuffer(&conn->errorMessage,
830
				  libpq_gettext("could not read private key (%s): %s\n"),
831 832
						  fnbuf, err);
		SSLerrfree(err);
833 834 835 836 837 838
		X509_free(*x509);
		fclose(fp);
		return -1;
	}
	fclose(fp);

839 840 841
	/* verify that the cert and key go together */
	if (!X509_check_private_key(*x509, *pkey))
	{
Bruce Momjian's avatar
Bruce Momjian committed
842 843
		char	   *err = SSLerrmessage();

Bruce Momjian's avatar
Bruce Momjian committed
844
		printfPQExpBuffer(&conn->errorMessage,
845
			libpq_gettext("certificate/private key mismatch (%s): %s\n"),
846 847
						  fnbuf, err);
		SSLerrfree(err);
848 849 850 851 852
		X509_free(*x509);
		EVP_PKEY_free(*pkey);
		return -1;
	}

853
	return 1;
854
#endif
855 856
}

857 858 859 860 861
#ifdef ENABLE_THREAD_SAFETY

static unsigned long
pq_threadidcallback(void)
{
Bruce Momjian's avatar
Bruce Momjian committed
862
	return (unsigned long) pthread_self();
863 864 865 866 867 868
}

static pthread_mutex_t *pq_lockarray;
static void
pq_lockingcallback(int mode, int n, const char *file, int line)
{
Bruce Momjian's avatar
Bruce Momjian committed
869
	if (mode & CRYPTO_LOCK)
870
		pthread_mutex_lock(&pq_lockarray[n]);
Bruce Momjian's avatar
Bruce Momjian committed
871
	else
872 873
		pthread_mutex_unlock(&pq_lockarray[n]);
}
Bruce Momjian's avatar
Bruce Momjian committed
874
#endif   /* ENABLE_THREAD_SAFETY */
875 876 877 878 879

static int
init_ssl_system(PGconn *conn)
{
#ifdef ENABLE_THREAD_SAFETY
880
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
881 882
	static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;

883
#else
884 885 886
	static pthread_mutex_t init_mutex = NULL;
	static long mutex_initlock = 0;

Bruce Momjian's avatar
Bruce Momjian committed
887 888 889 890
	if (init_mutex == NULL)
	{
		while (InterlockedExchange(&mutex_initlock, 1) == 1)
			 /* loop, another thread own the lock */ ;
891 892
		if (init_mutex == NULL)
			pthread_mutex_init(&init_mutex, NULL);
Bruce Momjian's avatar
Bruce Momjian committed
893
		InterlockedExchange(&mutex_initlock, 0);
894
	}
895
#endif
896
	pthread_mutex_lock(&init_mutex);
Bruce Momjian's avatar
Bruce Momjian committed
897 898 899 900 901

	if (pq_initssllib && pq_lockarray == NULL)
	{
		int			i;

902 903
		CRYPTO_set_id_callback(pq_threadidcallback);

Bruce Momjian's avatar
Bruce Momjian committed
904 905 906
		pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
		if (!pq_lockarray)
		{
907 908 909
			pthread_mutex_unlock(&init_mutex);
			return -1;
		}
Bruce Momjian's avatar
Bruce Momjian committed
910
		for (i = 0; i < CRYPTO_num_locks(); i++)
911 912 913 914 915 916 917
			pthread_mutex_init(&pq_lockarray[i], NULL);

		CRYPTO_set_locking_callback(pq_lockingcallback);
	}
#endif
	if (!SSL_context)
	{
Bruce Momjian's avatar
Bruce Momjian committed
918 919
		if (pq_initssllib)
		{
920 921 922 923 924 925
			SSL_library_init();
			SSL_load_error_strings();
		}
		SSL_context = SSL_CTX_new(TLSv1_method());
		if (!SSL_context)
		{
Bruce Momjian's avatar
Bruce Momjian committed
926 927
			char	   *err = SSLerrmessage();

928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
			printfPQExpBuffer(&conn->errorMessage,
					 libpq_gettext("could not create SSL context: %s\n"),
							  err);
			SSLerrfree(err);
#ifdef ENABLE_THREAD_SAFETY
			pthread_mutex_unlock(&init_mutex);
#endif
			return -1;
		}
	}
#ifdef ENABLE_THREAD_SAFETY
	pthread_mutex_unlock(&init_mutex);
#endif
	return 0;
}
Bruce Momjian's avatar
Bruce Momjian committed
943

Bruce Momjian's avatar
Bruce Momjian committed
944 945 946 947
/*
 *	Initialize global SSL context.
 */
static int
Bruce Momjian's avatar
Bruce Momjian committed
948
initialize_SSL(PGconn *conn)
Bruce Momjian's avatar
Bruce Momjian committed
949
{
950
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
951
	struct stat buf;
Bruce Momjian's avatar
Bruce Momjian committed
952
	char		pwdbuf[BUFSIZ];
953 954
	struct passwd pwdstr;
	struct passwd *pwd = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
955
	char		fnbuf[2048];
956
#endif
Bruce Momjian's avatar
Bruce Momjian committed
957

Bruce Momjian's avatar
Bruce Momjian committed
958
	if (init_ssl_system(conn))
959
		return -1;
Bruce Momjian's avatar
Bruce Momjian committed
960

961
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
962
	if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
963 964
	{
		snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/root.crt",
Bruce Momjian's avatar
Bruce Momjian committed
965
				 pwd->pw_dir);
Bruce Momjian's avatar
Bruce Momjian committed
966 967
		if (stat(fnbuf, &buf) == -1)
		{
968 969
			return 0;
#ifdef NOT_USED
Bruce Momjian's avatar
Bruce Momjian committed
970 971
			char		sebuf[256];

972
			/* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
Bruce Momjian's avatar
Bruce Momjian committed
973
			printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
974 975
							  libpq_gettext("could not read root certificate list (%s): %s\n"),
						 fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
Bruce Momjian's avatar
Bruce Momjian committed
976
			return -1;
977
#endif
Bruce Momjian's avatar
Bruce Momjian committed
978 979 980
		}
		if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, 0))
		{
Bruce Momjian's avatar
Bruce Momjian committed
981 982
			char	   *err = SSLerrmessage();

Bruce Momjian's avatar
Bruce Momjian committed
983
			printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
984
							  libpq_gettext("could not read root certificate list (%s): %s\n"),
985 986
							  fnbuf, err);
			SSLerrfree(err);
Bruce Momjian's avatar
Bruce Momjian committed
987 988 989 990
			return -1;
		}
	}

Bruce Momjian's avatar
Bruce Momjian committed
991 992
	SSL_CTX_set_verify(SSL_context,
		   SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
Bruce Momjian's avatar
Bruce Momjian committed
993 994
	SSL_CTX_set_verify_depth(SSL_context, 1);

995 996 997 998
	/* set up empheral DH keys */
	SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
	SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);

999 1000
	/* set up mechanism to provide client certificate, if available */
	SSL_CTX_set_client_cert_cb(SSL_context, client_cert_cb);
1001
#endif
1002

Bruce Momjian's avatar
Bruce Momjian committed
1003 1004 1005 1006 1007 1008 1009
	return 0;
}

/*
 *	Destroy global SSL context.
 */
static void
Bruce Momjian's avatar
Bruce Momjian committed
1010
destroy_SSL(void)
Bruce Momjian's avatar
Bruce Momjian committed
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
{
	if (SSL_context)
	{
		SSL_CTX_free(SSL_context);
		SSL_context = NULL;
	}
}

/*
 *	Attempt to negotiate SSL connection.
 */
1022
static PostgresPollingStatusType
Bruce Momjian's avatar
Bruce Momjian committed
1023
open_client_SSL(PGconn *conn)
Bruce Momjian's avatar
Bruce Momjian committed
1024
{
Bruce Momjian's avatar
Bruce Momjian committed
1025
	int			r;
Bruce Momjian's avatar
Bruce Momjian committed
1026

1027 1028
	r = SSL_connect(conn->ssl);
	if (r <= 0)
Bruce Momjian's avatar
Bruce Momjian committed
1029
	{
1030
		int err = SSL_get_error(conn->ssl, r);
1031

1032
		switch (err)
1033 1034 1035
		{
			case SSL_ERROR_WANT_READ:
				return PGRES_POLLING_READING;
Bruce Momjian's avatar
Bruce Momjian committed
1036

1037 1038 1039 1040
			case SSL_ERROR_WANT_WRITE:
				return PGRES_POLLING_WRITING;

			case SSL_ERROR_SYSCALL:
Bruce Momjian's avatar
Bruce Momjian committed
1041 1042 1043 1044 1045
				{
					char		sebuf[256];

					if (r == -1)
						printfPQExpBuffer(&conn->errorMessage,
1046
								libpq_gettext("SSL SYSCALL error: %s\n"),
Bruce Momjian's avatar
Bruce Momjian committed
1047 1048 1049 1050 1051 1052 1053
						SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
					else
						printfPQExpBuffer(&conn->errorMessage,
										  libpq_gettext("SSL SYSCALL error: EOF detected\n"));
					close_SSL(conn);
					return PGRES_POLLING_FAILED;
				}
1054
			case SSL_ERROR_SSL:
1055
				{
Bruce Momjian's avatar
Bruce Momjian committed
1056 1057
					char	   *err = SSLerrmessage();

1058
					printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
1059
								  libpq_gettext("SSL error: %s\n"), err);
1060 1061 1062 1063
					SSLerrfree(err);
					close_SSL(conn);
					return PGRES_POLLING_FAILED;
				}
1064 1065 1066

			default:
				printfPQExpBuffer(&conn->errorMessage,
1067 1068
						 libpq_gettext("unrecognized SSL error code: %d\n"),
								  err);
1069 1070 1071
				close_SSL(conn);
				return PGRES_POLLING_FAILED;
		}
Bruce Momjian's avatar
Bruce Momjian committed
1072 1073 1074
	}

	/* check the certificate chain of the server */
Bruce Momjian's avatar
Bruce Momjian committed
1075

1076 1077
#ifdef NOT_USED
	/* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
Bruce Momjian's avatar
Bruce Momjian committed
1078

Bruce Momjian's avatar
Bruce Momjian committed
1079 1080 1081 1082
	/*
	 * this eliminates simple man-in-the-middle attacks and simple
	 * impersonations
	 */
Bruce Momjian's avatar
Bruce Momjian committed
1083 1084 1085 1086
	r = SSL_get_verify_result(conn->ssl);
	if (r != X509_V_OK)
	{
		printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
1087 1088
			   libpq_gettext("certificate could not be validated: %s\n"),
						  X509_verify_cert_error_string(r));
Bruce Momjian's avatar
Bruce Momjian committed
1089
		close_SSL(conn);
1090
		return PGRES_POLLING_FAILED;
Bruce Momjian's avatar
Bruce Momjian committed
1091
	}
1092
#endif
Bruce Momjian's avatar
Bruce Momjian committed
1093 1094 1095 1096 1097

	/* pull out server distinguished and common names */
	conn->peer = SSL_get_peer_certificate(conn->ssl);
	if (conn->peer == NULL)
	{
Bruce Momjian's avatar
Bruce Momjian committed
1098 1099
		char	   *err = SSLerrmessage();

Bruce Momjian's avatar
Bruce Momjian committed
1100
		printfPQExpBuffer(&conn->errorMessage,
Bruce Momjian's avatar
Bruce Momjian committed
1101
				libpq_gettext("certificate could not be obtained: %s\n"),
1102 1103
						  err);
		SSLerrfree(err);
Bruce Momjian's avatar
Bruce Momjian committed
1104
		close_SSL(conn);
1105
		return PGRES_POLLING_FAILED;
Bruce Momjian's avatar
Bruce Momjian committed
1106 1107 1108
	}

	X509_NAME_oneline(X509_get_subject_name(conn->peer),
Bruce Momjian's avatar
Bruce Momjian committed
1109 1110
					  conn->peer_dn, sizeof(conn->peer_dn));
	conn->peer_dn[sizeof(conn->peer_dn) - 1] = '\0';
Bruce Momjian's avatar
Bruce Momjian committed
1111 1112

	X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
Bruce Momjian's avatar
Bruce Momjian committed
1113
							  NID_commonName, conn->peer_cn, SM_USER);
Bruce Momjian's avatar
Bruce Momjian committed
1114 1115 1116
	conn->peer_cn[SM_USER] = '\0';

	/* verify that the common name resolves to peer */
Bruce Momjian's avatar
Bruce Momjian committed
1117

1118 1119
#ifdef NOT_USED
	/* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
Bruce Momjian's avatar
Bruce Momjian committed
1120

Bruce Momjian's avatar
Bruce Momjian committed
1121 1122 1123 1124 1125
	/*
	 * this is necessary to eliminate man-in-the-middle attacks and
	 * impersonations where the attacker somehow learned the server's
	 * private key
	 */
Bruce Momjian's avatar
Bruce Momjian committed
1126 1127 1128
	if (verify_peer(conn) == -1)
	{
		close_SSL(conn);
1129
		return PGRES_POLLING_FAILED;
Bruce Momjian's avatar
Bruce Momjian committed
1130
	}
1131
#endif
Bruce Momjian's avatar
Bruce Momjian committed
1132

1133 1134
	/* SSL handshake is complete */
	return PGRES_POLLING_OK;
Bruce Momjian's avatar
Bruce Momjian committed
1135 1136 1137 1138 1139 1140
}

/*
 *	Close SSL connection.
 */
static void
Bruce Momjian's avatar
Bruce Momjian committed
1141
close_SSL(PGconn *conn)
Bruce Momjian's avatar
Bruce Momjian committed
1142 1143 1144 1145 1146 1147 1148
{
	if (conn->ssl)
	{
		SSL_shutdown(conn->ssl);
		SSL_free(conn->ssl);
		conn->ssl = NULL;
	}
1149 1150 1151 1152 1153 1154

	if (conn->peer)
	{
		X509_free(conn->peer);
		conn->peer = NULL;
	}
Bruce Momjian's avatar
Bruce Momjian committed
1155 1156 1157 1158 1159 1160 1161 1162 1163
}

/*
 * 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.
 */
1164
static char ssl_nomem[] = "Out of memory allocating error description";
Bruce Momjian's avatar
Bruce Momjian committed
1165 1166

#define SSL_ERR_LEN 128
1167 1168

static char *
Bruce Momjian's avatar
Bruce Momjian committed
1169 1170
SSLerrmessage(void)
{
Bruce Momjian's avatar
Bruce Momjian committed
1171 1172
	unsigned long errcode;
	const char *errreason;
Bruce Momjian's avatar
Bruce Momjian committed
1173
	char	   *errbuf;
Bruce Momjian's avatar
Bruce Momjian committed
1174

1175 1176 1177
	errbuf = malloc(SSL_ERR_LEN);
	if (!errbuf)
		return ssl_nomem;
Bruce Momjian's avatar
Bruce Momjian committed
1178
	errcode = ERR_get_error();
Bruce Momjian's avatar
Bruce Momjian committed
1179 1180
	if (errcode == 0)
	{
1181 1182 1183
		strcpy(errbuf, "No SSL error reported");
		return errbuf;
	}
Bruce Momjian's avatar
Bruce Momjian committed
1184
	errreason = ERR_reason_error_string(errcode);
Bruce Momjian's avatar
Bruce Momjian committed
1185 1186 1187 1188
	if (errreason != NULL)
	{
		strncpy(errbuf, errreason, SSL_ERR_LEN - 1);
		errbuf[SSL_ERR_LEN - 1] = '\0';
1189 1190 1191
		return errbuf;
	}
	snprintf(errbuf, SSL_ERR_LEN, "SSL error code %lu", errcode);
Bruce Momjian's avatar
Bruce Momjian committed
1192 1193 1194
	return errbuf;
}

1195 1196 1197 1198 1199 1200
static void
SSLerrfree(char *buf)
{
	if (buf != ssl_nomem)
		free(buf);
}
Bruce Momjian's avatar
Bruce Momjian committed
1201

Bruce Momjian's avatar
Bruce Momjian committed
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
/*
 *	Return pointer to SSL object.
 */
SSL *
PQgetssl(PGconn *conn)
{
	if (!conn)
		return NULL;
	return conn->ssl;
}
Bruce Momjian's avatar
Bruce Momjian committed
1212
#endif   /* USE_SSL */
1213 1214 1215


#ifdef ENABLE_THREAD_SAFETY
1216
#ifndef WIN32
1217 1218 1219 1220
/*
 *	Check SIGPIPE handler and perhaps install our own.
 */
void
1221
pq_check_sigpipe_handler(void)
1222
{
1223
	pthread_key_create(&pq_thread_in_send, NULL);
Bruce Momjian's avatar
Bruce Momjian committed
1224

1225
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1226
	 * Find current pipe handler and chain on to it.
1227
	 */
1228 1229
	pq_pipe_handler = pqsignalinquire(SIGPIPE);
	pqsignal(SIGPIPE, sigpipe_handler_ignore_send);
1230 1231 1232 1233 1234 1235 1236 1237
}

/*
 *	Threaded SIGPIPE signal handler
 */
void
sigpipe_handler_ignore_send(int signo)
{
1238
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1239 1240 1241
	 * If we have gotten a SIGPIPE outside send(), chain or exit if we are
	 * at the end of the chain. Synchronous signals are delivered to the
	 * thread that caused the signal.
1242
	 */
1243
	if (!PQinSend())
1244
	{
Bruce Momjian's avatar
Bruce Momjian committed
1245
		if (pq_pipe_handler == SIG_DFL) /* not set by application */
1246 1247
			exit(128 + SIGPIPE);	/* typical return value for SIG_DFL */
		else
Bruce Momjian's avatar
Bruce Momjian committed
1248
			(*pq_pipe_handler) (signo); /* call original handler */
1249
	}
1250 1251
}
#endif
1252
#endif
Bruce Momjian's avatar
Bruce Momjian committed
1253

1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
/*
 *	Indicates whether the current thread is in send()
 *	For use by SIGPIPE signal handlers;  they should
 *	ignore SIGPIPE when libpq is in send().  This means
 *	that the backend has died unexpectedly.
 */
pqbool
PQinSend(void)
{
#ifdef ENABLE_THREAD_SAFETY
1264
	return (pthread_getspecific(pq_thread_in_send) /* has it been set? */ &&
Bruce Momjian's avatar
Bruce Momjian committed
1265
			*(char *) pthread_getspecific(pq_thread_in_send) == 't') ? true : false;
1266
#else
Bruce Momjian's avatar
Bruce Momjian committed
1267

1268
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1269 1270 1271
	 * No threading: our code ignores SIGPIPE around send(). Therefore, we
	 * can't be in send() if we are checking from a SIGPIPE signal
	 * handler.
1272
	 */
Bruce Momjian's avatar
Bruce Momjian committed
1273
	return false;
1274 1275
#endif
}