fe-secure.c 26.6 KB
Newer Older
B
Bruce Momjian 已提交
1 2
/*-------------------------------------------------------------------------
 *
3
 * fe-secure.c
B
Bruce Momjian 已提交
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.
 *
 *
9
 * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
B
Bruce Momjian 已提交
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.86 2006/09/04 14:57:27 petere Exp $
B
Bruce Momjian 已提交
15
 *
B
Bruce Momjian 已提交
16
 * NOTES
17 18
 *	  [ Most of these notes are wrong/obsolete, but perhaps not all ]
 *
B
Bruce Momjian 已提交
19 20 21 22 23 24 25
 *	  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
B
Bruce Momjian 已提交
26
 *	  "man-in-the-middle" and "impersonation" attacks.	The
B
Bruce Momjian 已提交
27 28
 *	  server certificate, or better yet the CA certificate used
 *	  to sign the server certificate, should be present in the
29
 *	  "~/.postgresql/root.crt" file.  If this file isn't
B
Bruce Momjian 已提交
30
 *	  readable, or the server certificate can't be validated,
31
 *	  pqsecure_open_client() will return an error code.
B
Bruce Momjian 已提交
32 33 34 35 36
 *
 *	  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
B
Bruce Momjian 已提交
37
 *	  has been stolen.	This check limits acceptable network
B
Bruce Momjian 已提交
38 39 40 41 42
 *	  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
43
 *	  try to do SSL if pqsecure_initialize() fails, and the backend
B
Bruce Momjian 已提交
44
 *	  shouldn't crash/recover if an SSH negotiation fails.  The
B
Bruce Momjian 已提交
45
 *	  backend definitely needs to be fixed, to prevent a "denial
B
Bruce Momjian 已提交
46
 *	  of service" attack, but I don't know enough about how the
B
Bruce Momjian 已提交
47 48 49
 *	  backend works (especially that pre-SSL negotiation) to identify
 *	  a fix.
 *
50 51 52
 *	  ...
 *
 *	  Unlike the server's static private key, the client's
53
 *	  static private key (~/.postgresql/postgresql.key)
B
Bruce Momjian 已提交
54
 *	  should normally be stored encrypted.	However we still
55 56
 *	  support EPH since it's useful for other reasons.
 *
57 58 59 60 61 62 63 64 65
 *	  ...
 *
 *	  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
66
 *		~/.postgresql/postgresql.crt
67
 *	  and
68
 *		~/.postgresql/postgresql.key
69 70
 *	  respectively.
 *
71 72 73 74 75 76
 *	  ...
 *
 *	  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.
 *
B
Bruce Momjian 已提交
77 78 79 80 81 82 83 84 85 86 87
 *-------------------------------------------------------------------------
 */

#include "postgres_fe.h"

#include <signal.h>
#include <fcntl.h>
#include <ctype.h>

#include "libpq-fe.h"
#include "fe-auth.h"
88
#include "pqsignal.h"
B
Bruce Momjian 已提交
89 90 91 92 93 94 95 96 97 98 99 100 101

#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
102
#include <sys/stat.h>
B
Bruce Momjian 已提交
103

104
#ifdef ENABLE_THREAD_SAFETY
105 106 107
#ifdef WIN32
#include "pthread-win32.h"
#else
108 109
#include <pthread.h>
#endif
110
#endif
111

B
Bruce Momjian 已提交
112 113 114 115 116 117
#ifndef HAVE_STRDUP
#include "strdup.h"
#endif

#ifdef USE_SSL
#include <openssl/ssl.h>
B
Bruce Momjian 已提交
118
#endif   /* USE_SSL */
B
Bruce Momjian 已提交
119 120 121


#ifdef USE_SSL
122 123

#ifndef WIN32
B
Bruce Momjian 已提交
124
#define USER_CERT_FILE		".postgresql/postgresql.crt"
125
#define USER_KEY_FILE		".postgresql/postgresql.key"
B
Bruce Momjian 已提交
126
#define ROOT_CERT_FILE		".postgresql/root.crt"
127
#define ROOT_CRL_FILE		".postgresql/root.crl"
128 129
#else
/* On Windows, the "home" directory is already PostgreSQL-specific */
B
Bruce Momjian 已提交
130
#define USER_CERT_FILE		"postgresql.crt"
131
#define USER_KEY_FILE		"postgresql.key"
B
Bruce Momjian 已提交
132
#define ROOT_CERT_FILE		"root.crt"
133
#define ROOT_CRL_FILE		"root.crl"
134
#endif
B
Bruce Momjian 已提交
135

B
Bruce Momjian 已提交
136
#ifdef NOT_USED
B
Bruce Momjian 已提交
137
static int	verify_peer(PGconn *);
B
Bruce Momjian 已提交
138
#endif
139
static int	verify_cb(int ok, X509_STORE_CTX *ctx);
B
Bruce Momjian 已提交
140
static int	client_cert_cb(SSL *, X509 **, EVP_PKEY **);
141
static int	init_ssl_system(PGconn *conn);
B
Bruce Momjian 已提交
142
static int	initialize_SSL(PGconn *);
B
Bruce Momjian 已提交
143
static void destroy_SSL(void);
144
static PostgresPollingStatusType open_client_SSL(PGconn *);
B
Bruce Momjian 已提交
145
static void close_SSL(PGconn *);
146 147
static char *SSLerrmessage(void);
static void SSLerrfree(char *buf);
B
Bruce Momjian 已提交
148 149 150
#endif

#ifdef USE_SSL
151
static bool pq_initssllib = true;
152

B
Bruce Momjian 已提交
153 154 155 156
static SSL_CTX *SSL_context = NULL;
#endif

/* ------------------------------------------------------------ */
B
Bruce Momjian 已提交
157
/*			 Procedures common to all secure sessions			*/
B
Bruce Momjian 已提交
158 159
/* ------------------------------------------------------------ */

160 161

/*
162 163
 *	Exported function to allow application to tell us it's already
 *	initialized OpenSSL.
164 165 166 167 168 169 170 171 172
 */
void
PQinitSSL(int do_init)
{
#ifdef USE_SSL
	pq_initssllib = do_init;
#endif
}

B
Bruce Momjian 已提交
173 174 175 176
/*
 *	Initialize global context
 */
int
B
Bruce Momjian 已提交
177
pqsecure_initialize(PGconn *conn)
B
Bruce Momjian 已提交
178
{
B
Bruce Momjian 已提交
179
	int			r = 0;
B
Bruce Momjian 已提交
180 181 182 183 184 185 186 187 188 189 190 191

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

	return r;
}

/*
 *	Destroy global context
 */
void
B
Bruce Momjian 已提交
192
pqsecure_destroy(void)
B
Bruce Momjian 已提交
193 194 195 196 197 198 199 200 201
{
#ifdef USE_SSL
	destroy_SSL();
#endif
}

/*
 *	Attempt to negotiate secure session.
 */
202
PostgresPollingStatusType
B
Bruce Momjian 已提交
203
pqsecure_open_client(PGconn *conn)
B
Bruce Momjian 已提交
204 205
{
#ifdef USE_SSL
206 207 208 209 210 211 212
	/* 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))
		{
B
Bruce Momjian 已提交
213 214
			char	   *err = SSLerrmessage();

215
			printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
216
				   libpq_gettext("could not establish SSL connection: %s\n"),
217 218
							  err);
			SSLerrfree(err);
219 220 221
			close_SSL(conn);
			return PGRES_POLLING_FAILED;
		}
B
Bruce Momjian 已提交
222

223
		/*
B
Bruce Momjian 已提交
224 225
		 * Initialize errorMessage to empty.  This allows open_client_SSL() to
		 * detect whether client_cert_cb() has stored a message.
226 227
		 */
		resetPQExpBuffer(&conn->errorMessage);
228 229 230 231 232 233
	}
	/* Begin or continue the actual handshake */
	return open_client_SSL(conn);
#else
	/* shouldn't get here */
	return PGRES_POLLING_FAILED;
B
Bruce Momjian 已提交
234 235 236 237 238 239 240
#endif
}

/*
 *	Close secure session.
 */
void
B
Bruce Momjian 已提交
241
pqsecure_close(PGconn *conn)
B
Bruce Momjian 已提交
242 243 244 245 246 247 248 249 250 251 252
{
#ifdef USE_SSL
	if (conn->ssl)
		close_SSL(conn);
#endif
}

/*
 *	Read data from a secure connection.
 */
ssize_t
B
Bruce Momjian 已提交
253
pqsecure_read(PGconn *conn, void *ptr, size_t len)
B
Bruce Momjian 已提交
254
{
B
Bruce Momjian 已提交
255
	ssize_t		n;
B
Bruce Momjian 已提交
256 257 258 259

#ifdef USE_SSL
	if (conn->ssl)
	{
260 261
		int			err;

B
Bruce Momjian 已提交
262
rloop:
B
Bruce Momjian 已提交
263
		n = SSL_read(conn->ssl, ptr, len);
264 265
		err = SSL_get_error(conn->ssl, n);
		switch (err)
B
Bruce Momjian 已提交
266
		{
B
Bruce Momjian 已提交
267 268 269
			case SSL_ERROR_NONE:
				break;
			case SSL_ERROR_WANT_READ:
270 271
				n = 0;
				break;
272
			case SSL_ERROR_WANT_WRITE:
B
Bruce Momjian 已提交
273

274
				/*
B
Bruce Momjian 已提交
275 276 277 278
				 * 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.
279
				 */
280
				goto rloop;
B
Bruce Momjian 已提交
281
			case SSL_ERROR_SYSCALL:
B
Bruce Momjian 已提交
282 283 284 285 286
				{
					char		sebuf[256];

					if (n == -1)
						printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
287 288
									libpq_gettext("SSL SYSCALL error: %s\n"),
							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
B
Bruce Momjian 已提交
289
					else
290
					{
B
Bruce Momjian 已提交
291
						printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
292
						 libpq_gettext("SSL SYSCALL error: EOF detected\n"));
293

294
						SOCK_ERRNO_SET(ECONNRESET);
295 296
						n = -1;
					}
B
Bruce Momjian 已提交
297 298
					break;
				}
B
Bruce Momjian 已提交
299
			case SSL_ERROR_SSL:
300
				{
B
Bruce Momjian 已提交
301 302
					char	   *err = SSLerrmessage();

303
					printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
304
									  libpq_gettext("SSL error: %s\n"), err);
305 306
					SSLerrfree(err);
				}
B
Bruce Momjian 已提交
307 308
				/* fall through */
			case SSL_ERROR_ZERO_RETURN:
309
				SOCK_ERRNO_SET(ECONNRESET);
B
Bruce Momjian 已提交
310 311
				n = -1;
				break;
312 313
			default:
				printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
314
						  libpq_gettext("unrecognized SSL error code: %d\n"),
315
								  err);
316
				n = -1;
317
				break;
B
Bruce Momjian 已提交
318 319 320 321
		}
	}
	else
#endif
B
Bruce Momjian 已提交
322
		n = recv(conn->sock, ptr, len, 0);
B
Bruce Momjian 已提交
323 324 325 326 327 328 329 330

	return n;
}

/*
 *	Write data to a secure connection.
 */
ssize_t
B
Bruce Momjian 已提交
331
pqsecure_write(PGconn *conn, const void *ptr, size_t len)
B
Bruce Momjian 已提交
332
{
B
Bruce Momjian 已提交
333
	ssize_t		n;
B
Bruce Momjian 已提交
334 335

#ifndef WIN32
336
#ifdef ENABLE_THREAD_SAFETY
337 338
	sigset_t	osigmask;
	bool		sigpipe_pending;
339
	bool		got_epipe = false;
B
Bruce Momjian 已提交
340

341

342 343
	if (pq_block_sigpipe(&osigmask, &sigpipe_pending) < 0)
		return -1;
344
#else
B
Bruce Momjian 已提交
345
	pqsigfunc	oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
B
Bruce Momjian 已提交
346 347 348
#endif   /* ENABLE_THREAD_SAFETY */
#endif   /* WIN32 */

B
Bruce Momjian 已提交
349 350 351
#ifdef USE_SSL
	if (conn->ssl)
	{
352 353
		int			err;

B
Bruce Momjian 已提交
354
		n = SSL_write(conn->ssl, ptr, len);
355 356
		err = SSL_get_error(conn->ssl, n);
		switch (err)
B
Bruce Momjian 已提交
357
		{
B
Bruce Momjian 已提交
358 359
			case SSL_ERROR_NONE:
				break;
360
			case SSL_ERROR_WANT_READ:
B
Bruce Momjian 已提交
361

362 363
				/*
				 * Returning 0 here causes caller to wait for write-ready,
B
Bruce Momjian 已提交
364 365
				 * which is not really the right thing, but it's the best we
				 * can do.
366 367 368
				 */
				n = 0;
				break;
B
Bruce Momjian 已提交
369
			case SSL_ERROR_WANT_WRITE:
370 371
				n = 0;
				break;
B
Bruce Momjian 已提交
372
			case SSL_ERROR_SYSCALL:
B
Bruce Momjian 已提交
373 374
				{
					char		sebuf[256];
375

B
Bruce Momjian 已提交
376
					if (n == -1)
377
					{
378
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
379 380
						if (SOCK_ERRNO == EPIPE)
							got_epipe = true;
381
#endif
B
Bruce Momjian 已提交
382
						printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
383 384
									libpq_gettext("SSL SYSCALL error: %s\n"),
							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
385
					}
B
Bruce Momjian 已提交
386
					else
387
					{
B
Bruce Momjian 已提交
388
						printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
389
						 libpq_gettext("SSL SYSCALL error: EOF detected\n"));
390
						SOCK_ERRNO_SET(ECONNRESET);
391 392
						n = -1;
					}
B
Bruce Momjian 已提交
393 394
					break;
				}
B
Bruce Momjian 已提交
395
			case SSL_ERROR_SSL:
396
				{
B
Bruce Momjian 已提交
397 398
					char	   *err = SSLerrmessage();

399
					printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
400
									  libpq_gettext("SSL error: %s\n"), err);
401 402
					SSLerrfree(err);
				}
B
Bruce Momjian 已提交
403 404
				/* fall through */
			case SSL_ERROR_ZERO_RETURN:
405
				SOCK_ERRNO_SET(ECONNRESET);
B
Bruce Momjian 已提交
406 407
				n = -1;
				break;
408 409
			default:
				printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
410
						  libpq_gettext("unrecognized SSL error code: %d\n"),
411
								  err);
412
				n = -1;
413
				break;
B
Bruce Momjian 已提交
414 415 416 417
		}
	}
	else
#endif
418
	{
B
Bruce Momjian 已提交
419
		n = send(conn->sock, ptr, len, 0);
420
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
421 422 423 424
		if (n < 0 && SOCK_ERRNO == EPIPE)
			got_epipe = true;
#endif
	}
B
Bruce Momjian 已提交
425

426
#ifndef WIN32
427
#ifdef ENABLE_THREAD_SAFETY
428
	pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe);
429
#else
B
Bruce Momjian 已提交
430
	pqsignal(SIGPIPE, oldsighandler);
B
Bruce Momjian 已提交
431 432
#endif   /* ENABLE_THREAD_SAFETY */
#endif   /* WIN32 */
B
Bruce Momjian 已提交
433 434 435 436 437

	return n;
}

/* ------------------------------------------------------------ */
B
Bruce Momjian 已提交
438
/*						  SSL specific code						*/
B
Bruce Momjian 已提交
439 440
/* ------------------------------------------------------------ */
#ifdef USE_SSL
441

B
Bruce Momjian 已提交
442 443 444 445 446 447 448 449 450 451 452 453
/*
 *	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
B
Bruce Momjian 已提交
454
verify_cb(int ok, X509_STORE_CTX *ctx)
B
Bruce Momjian 已提交
455 456 457 458
{
	return ok;
}

B
Bruce Momjian 已提交
459
#ifdef NOT_USED
B
Bruce Momjian 已提交
460 461 462 463
/*
 *	Verify that common name resolves to peer.
 */
static int
B
Bruce Momjian 已提交
464
verify_peer(PGconn *conn)
B
Bruce Momjian 已提交
465 466 467 468
{
	struct hostent *h = NULL;
	struct sockaddr addr;
	struct sockaddr_in *sin;
469
	ACCEPT_TYPE_ARG3 len;
B
Bruce Momjian 已提交
470
	char	  **s;
B
Bruce Momjian 已提交
471 472 473 474 475 476
	unsigned long l;

	/* get the address on the other side of the socket */
	len = sizeof(addr);
	if (getpeername(conn->sock, &addr, &len) == -1)
	{
B
Bruce Momjian 已提交
477 478
		char		sebuf[256];

B
Bruce Momjian 已提交
479
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
480
						  libpq_gettext("error querying socket: %s\n"),
B
Bruce Momjian 已提交
481
						  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
B
Bruce Momjian 已提交
482 483 484 485 486 487 488
		return -1;
	}

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

489 490
	{
		struct hostent hpstr;
491
		char		buf[BUFSIZ];
B
Bruce Momjian 已提交
492
		int			herrno = 0;
B
Bruce Momjian 已提交
493

494
		/*
B
Bruce Momjian 已提交
495 496 497
		 * 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().
498
		 */
499
		pqGethostbyname(conn->peer_cn, &hpstr, buf, sizeof(buf),
B
Bruce Momjian 已提交
500
						&h, &herrno);
501
	}
B
Bruce Momjian 已提交
502

B
Bruce Momjian 已提交
503
	/* what do we know about the peer's common name? */
504
	if (h == NULL)
B
Bruce Momjian 已提交
505 506
	{
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
507
		  libpq_gettext("could not get information about host \"%s\": %s\n"),
B
Bruce Momjian 已提交
508
						  conn->peer_cn, hstrerror(h_errno));
B
Bruce Momjian 已提交
509 510 511 512 513 514
		return -1;
	}

	/* does the address match? */
	switch (addr.sa_family)
	{
B
Bruce Momjian 已提交
515 516 517 518 519 520 521 522
		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;
B
Bruce Momjian 已提交
523

B
Bruce Momjian 已提交
524 525
		default:
			printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
526
							  libpq_gettext("unsupported protocol\n"));
B
Bruce Momjian 已提交
527
			return -1;
B
Bruce Momjian 已提交
528 529
	}

B
Bruce Momjian 已提交
530 531 532 533
	/*
	 * the prior test should be definitive, but in practice it sometimes
	 * fails.  So we also check the aliases.
	 */
B
Bruce Momjian 已提交
534 535
	for (s = h->h_aliases; *s != NULL; s++)
	{
536
		if (pg_strcasecmp(conn->peer_cn, *s) == 0)
B
Bruce Momjian 已提交
537 538 539 540 541 542
			return 0;
	}

	/* generate protocol-aware error message */
	switch (addr.sa_family)
	{
B
Bruce Momjian 已提交
543 544 545 546 547
		case AF_INET:
			sin = (struct sockaddr_in *) & addr;
			l = ntohl(sin->sin_addr.s_addr);
			printfPQExpBuffer(&conn->errorMessage,
							  libpq_gettext(
548
											"server common name \"%s\" does not resolve to %ld.%ld.%ld.%ld\n"),
B
Bruce Momjian 已提交
549
						 conn->peer_cn, (l >> 24) % 0x100, (l >> 16) % 0x100,
B
Bruce Momjian 已提交
550 551 552 553 554
							  (l >> 8) % 0x100, l % 0x100);
			break;
		default:
			printfPQExpBuffer(&conn->errorMessage,
							  libpq_gettext(
B
Bruce Momjian 已提交
555
			 "server common name \"%s\" does not resolve to peer address\n"),
B
Bruce Momjian 已提交
556
							  conn->peer_cn);
B
Bruce Momjian 已提交
557 558 559 560
	}

	return -1;
}
B
Bruce Momjian 已提交
561
#endif   /* NOT_USED */
B
Bruce Momjian 已提交
562

563 564 565 566 567
/*
 *	Callback used by SSL to load client cert and key.
 *	This callback is only called when the server wants a
 *	client cert.
 *
568
 *	Must return 1 on success, 0 on no data or error.
569 570
 */
static int
B
Bruce Momjian 已提交
571
client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
572
{
573
	char		homedir[MAXPGPATH];
574
	struct stat buf;
B
Bruce Momjian 已提交
575

576 577 578
#ifndef WIN32
	struct stat buf2;
#endif
579
	char		fnbuf[MAXPGPATH];
B
Bruce Momjian 已提交
580 581 582
	FILE	   *fp;
	PGconn	   *conn = (PGconn *) SSL_get_app_data(ssl);
	int			(*cb) () = NULL;	/* how to read user password */
B
Bruce Momjian 已提交
583
	char		sebuf[256];
584

585
	if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
586
	{
B
Bruce Momjian 已提交
587
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
588
						  libpq_gettext("could not get user information\n"));
589
		return 0;
590 591 592
	}

	/* read the user certificate */
593
	snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
594 595
	if ((fp = fopen(fnbuf, "r")) == NULL)
	{
B
Bruce Momjian 已提交
596
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
597
			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
598
						  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
599
		return 0;
600 601 602
	}
	if (PEM_read_X509(fp, x509, NULL, NULL) == NULL)
	{
B
Bruce Momjian 已提交
603 604
		char	   *err = SSLerrmessage();

B
Bruce Momjian 已提交
605
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
606
			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
607 608
						  fnbuf, err);
		SSLerrfree(err);
609
		fclose(fp);
610
		return 0;
611 612 613 614
	}
	fclose(fp);

	/* read the user key */
615
	snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
616 617
	if (stat(fnbuf, &buf) == -1)
	{
B
Bruce Momjian 已提交
618
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
619
						  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
B
Bruce Momjian 已提交
620
						  fnbuf);
621 622
		return 0;
	}
623
#ifndef WIN32
624
	if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) ||
625
		buf.st_uid != geteuid())
626
	{
B
Bruce Momjian 已提交
627
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
628
			libpq_gettext("private key file \"%s\" has wrong permissions\n"),
629 630
						  fnbuf);
		return 0;
631
	}
632
#endif
633 634
	if ((fp = fopen(fnbuf, "r")) == NULL)
	{
B
Bruce Momjian 已提交
635
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
636
			   libpq_gettext("could not open private key file \"%s\": %s\n"),
637
						  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
638
		return 0;
639
	}
640
#ifndef WIN32
641 642 643
	if (fstat(fileno(fp), &buf2) == -1 ||
		buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
	{
B
Bruce Momjian 已提交
644
		printfPQExpBuffer(&conn->errorMessage,
645 646
						  libpq_gettext("private key file \"%s\" changed during execution\n"), fnbuf);
		return 0;
647
	}
648
#endif
649 650
	if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL)
	{
B
Bruce Momjian 已提交
651 652
		char	   *err = SSLerrmessage();

B
Bruce Momjian 已提交
653
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
654
			   libpq_gettext("could not read private key file \"%s\": %s\n"),
655 656
						  fnbuf, err);
		SSLerrfree(err);
657
		fclose(fp);
658
		return 0;
659 660 661
	}
	fclose(fp);

662 663 664
	/* verify that the cert and key go together */
	if (!X509_check_private_key(*x509, *pkey))
	{
B
Bruce Momjian 已提交
665 666
		char	   *err = SSLerrmessage();

B
Bruce Momjian 已提交
667
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
668
						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
669 670
						  fnbuf, err);
		SSLerrfree(err);
671
		return 0;
672 673
	}

674 675 676
	return 1;
}

677 678 679 680 681
#ifdef ENABLE_THREAD_SAFETY

static unsigned long
pq_threadidcallback(void)
{
682
	/*
B
Bruce Momjian 已提交
683
	 * This is not standards-compliant.  pthread_self() returns pthread_t, and
B
Bruce Momjian 已提交
684 685
	 * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
	 * it, so we have to do it.
686
	 */
B
Bruce Momjian 已提交
687
	return (unsigned long) pthread_self();
688 689 690
}

static pthread_mutex_t *pq_lockarray;
691

692 693 694
static void
pq_lockingcallback(int mode, int n, const char *file, int line)
{
B
Bruce Momjian 已提交
695
	if (mode & CRYPTO_LOCK)
696
		pthread_mutex_lock(&pq_lockarray[n]);
B
Bruce Momjian 已提交
697
	else
698 699
		pthread_mutex_unlock(&pq_lockarray[n]);
}
B
Bruce Momjian 已提交
700
#endif   /* ENABLE_THREAD_SAFETY */
701 702 703 704 705

static int
init_ssl_system(PGconn *conn)
{
#ifdef ENABLE_THREAD_SAFETY
706
#ifndef WIN32
B
Bruce Momjian 已提交
707
	static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
708
#else
709 710 711
	static pthread_mutex_t init_mutex = NULL;
	static long mutex_initlock = 0;

B
Bruce Momjian 已提交
712 713 714 715
	if (init_mutex == NULL)
	{
		while (InterlockedExchange(&mutex_initlock, 1) == 1)
			 /* loop, another thread own the lock */ ;
716 717
		if (init_mutex == NULL)
			pthread_mutex_init(&init_mutex, NULL);
B
Bruce Momjian 已提交
718
		InterlockedExchange(&mutex_initlock, 0);
719
	}
720
#endif
721
	pthread_mutex_lock(&init_mutex);
B
Bruce Momjian 已提交
722 723 724 725 726

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

727 728
		CRYPTO_set_id_callback(pq_threadidcallback);

B
Bruce Momjian 已提交
729 730 731
		pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
		if (!pq_lockarray)
		{
732 733 734
			pthread_mutex_unlock(&init_mutex);
			return -1;
		}
B
Bruce Momjian 已提交
735
		for (i = 0; i < CRYPTO_num_locks(); i++)
736 737 738 739 740 741 742
			pthread_mutex_init(&pq_lockarray[i], NULL);

		CRYPTO_set_locking_callback(pq_lockingcallback);
	}
#endif
	if (!SSL_context)
	{
B
Bruce Momjian 已提交
743 744
		if (pq_initssllib)
		{
745 746 747 748 749 750
			SSL_library_init();
			SSL_load_error_strings();
		}
		SSL_context = SSL_CTX_new(TLSv1_method());
		if (!SSL_context)
		{
B
Bruce Momjian 已提交
751 752
			char	   *err = SSLerrmessage();

753
			printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
754
						 libpq_gettext("could not create SSL context: %s\n"),
755 756 757 758 759 760 761 762 763 764 765 766 767
							  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;
}
B
Bruce Momjian 已提交
768

B
Bruce Momjian 已提交
769 770 771 772
/*
 *	Initialize global SSL context.
 */
static int
B
Bruce Momjian 已提交
773
initialize_SSL(PGconn *conn)
B
Bruce Momjian 已提交
774 775
{
	struct stat buf;
776
	char		homedir[MAXPGPATH];
777
	char		fnbuf[MAXPGPATH];
B
Bruce Momjian 已提交
778

B
Bruce Momjian 已提交
779
	if (init_ssl_system(conn))
780
		return -1;
B
Bruce Momjian 已提交
781

782
	/* Set up to verify server cert, if root.crt is present */
783
	if (pqGetHomeDirectory(homedir, sizeof(homedir)))
B
Bruce Momjian 已提交
784
	{
785
		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
786
		if (stat(fnbuf, &buf) == 0)
B
Bruce Momjian 已提交
787
		{
788 789
			X509_STORE *cvstore;
			
790 791 792
			if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL))
			{
				char	   *err = SSLerrmessage();
B
Bruce Momjian 已提交
793

794 795 796 797 798 799
				printfPQExpBuffer(&conn->errorMessage,
								  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
								  fnbuf, err);
				SSLerrfree(err);
				return -1;
			}
B
Bruce Momjian 已提交
800

801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822
			if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
			{
				/* setting the flags to check against the complete CRL chain */
				if (X509_STORE_load_locations(cvstore, ROOT_CRL_FILE, NULL) != 0)
/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
#ifdef X509_V_FLAG_CRL_CHECK
				   X509_STORE_set_flags(cvstore,
								X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
				/* if not found, silently ignore;  we do not require CRL */
#else
				{
					char	   *err = SSLerrmessage();
	
					printfPQExpBuffer(&conn->errorMessage,
									  libpq_gettext("Installed SSL library does not support CRL certificates, file \"%s\"\n"),
									  fnbuf);
					SSLerrfree(err);
					return -1;
				}
#endif
			}
	
823
			SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER, verify_cb);
B
Bruce Momjian 已提交
824 825 826
		}
	}

827 828 829
	/* set up mechanism to provide client certificate, if available */
	SSL_CTX_set_client_cert_cb(SSL_context, client_cert_cb);

B
Bruce Momjian 已提交
830 831 832 833 834 835 836
	return 0;
}

/*
 *	Destroy global SSL context.
 */
static void
B
Bruce Momjian 已提交
837
destroy_SSL(void)
B
Bruce Momjian 已提交
838 839 840 841 842 843 844 845 846 847 848
{
	if (SSL_context)
	{
		SSL_CTX_free(SSL_context);
		SSL_context = NULL;
	}
}

/*
 *	Attempt to negotiate SSL connection.
 */
849
static PostgresPollingStatusType
B
Bruce Momjian 已提交
850
open_client_SSL(PGconn *conn)
B
Bruce Momjian 已提交
851
{
B
Bruce Momjian 已提交
852
	int			r;
B
Bruce Momjian 已提交
853

854 855
	r = SSL_connect(conn->ssl);
	if (r <= 0)
B
Bruce Momjian 已提交
856
	{
B
Bruce Momjian 已提交
857
		int			err = SSL_get_error(conn->ssl, r);
858

859
		switch (err)
860 861 862
		{
			case SSL_ERROR_WANT_READ:
				return PGRES_POLLING_READING;
B
Bruce Momjian 已提交
863

864 865 866 867
			case SSL_ERROR_WANT_WRITE:
				return PGRES_POLLING_WRITING;

			case SSL_ERROR_SYSCALL:
B
Bruce Momjian 已提交
868 869 870 871 872
				{
					char		sebuf[256];

					if (r == -1)
						printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
873 874
									libpq_gettext("SSL SYSCALL error: %s\n"),
							SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
B
Bruce Momjian 已提交
875 876
					else
						printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
877
						 libpq_gettext("SSL SYSCALL error: EOF detected\n"));
B
Bruce Momjian 已提交
878 879 880
					close_SSL(conn);
					return PGRES_POLLING_FAILED;
				}
881
			case SSL_ERROR_SSL:
882
				{
883 884 885 886 887
					/*
					 * If there are problems with the local certificate files,
					 * these will be detected by client_cert_cb() which is
					 * called from SSL_connect().  We want to return that
					 * error message and not the rather unhelpful error that
B
Bruce Momjian 已提交
888
					 * OpenSSL itself returns.	So check to see if an error
889 890 891 892 893
					 * message was already stored.
					 */
					if (conn->errorMessage.len == 0)
					{
						char	   *err = SSLerrmessage();
B
Bruce Momjian 已提交
894

895 896 897 898 899
						printfPQExpBuffer(&conn->errorMessage,
										  libpq_gettext("SSL error: %s\n"),
										  err);
						SSLerrfree(err);
					}
900 901 902
					close_SSL(conn);
					return PGRES_POLLING_FAILED;
				}
903 904 905

			default:
				printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
906
						  libpq_gettext("unrecognized SSL error code: %d\n"),
907
								  err);
908 909 910
				close_SSL(conn);
				return PGRES_POLLING_FAILED;
		}
B
Bruce Momjian 已提交
911 912 913
	}

	/* check the certificate chain of the server */
B
Bruce Momjian 已提交
914

915 916
#ifdef NOT_USED
	/* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
B
Bruce Momjian 已提交
917

B
Bruce Momjian 已提交
918 919 920 921
	/*
	 * this eliminates simple man-in-the-middle attacks and simple
	 * impersonations
	 */
B
Bruce Momjian 已提交
922 923 924 925
	r = SSL_get_verify_result(conn->ssl);
	if (r != X509_V_OK)
	{
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
926
				   libpq_gettext("certificate could not be validated: %s\n"),
B
Bruce Momjian 已提交
927
						  X509_verify_cert_error_string(r));
B
Bruce Momjian 已提交
928
		close_SSL(conn);
929
		return PGRES_POLLING_FAILED;
B
Bruce Momjian 已提交
930
	}
931
#endif
B
Bruce Momjian 已提交
932 933 934 935 936

	/* pull out server distinguished and common names */
	conn->peer = SSL_get_peer_certificate(conn->ssl);
	if (conn->peer == NULL)
	{
B
Bruce Momjian 已提交
937 938
		char	   *err = SSLerrmessage();

B
Bruce Momjian 已提交
939
		printfPQExpBuffer(&conn->errorMessage,
B
Bruce Momjian 已提交
940
					libpq_gettext("certificate could not be obtained: %s\n"),
941 942
						  err);
		SSLerrfree(err);
B
Bruce Momjian 已提交
943
		close_SSL(conn);
944
		return PGRES_POLLING_FAILED;
B
Bruce Momjian 已提交
945 946 947
	}

	X509_NAME_oneline(X509_get_subject_name(conn->peer),
B
Bruce Momjian 已提交
948 949
					  conn->peer_dn, sizeof(conn->peer_dn));
	conn->peer_dn[sizeof(conn->peer_dn) - 1] = '\0';
B
Bruce Momjian 已提交
950 951

	X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
B
Bruce Momjian 已提交
952
							  NID_commonName, conn->peer_cn, SM_USER);
B
Bruce Momjian 已提交
953 954 955
	conn->peer_cn[SM_USER] = '\0';

	/* verify that the common name resolves to peer */
B
Bruce Momjian 已提交
956

957 958
#ifdef NOT_USED
	/* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
B
Bruce Momjian 已提交
959

B
Bruce Momjian 已提交
960 961
	/*
	 * this is necessary to eliminate man-in-the-middle attacks and
B
Bruce Momjian 已提交
962 963
	 * impersonations where the attacker somehow learned the server's private
	 * key
B
Bruce Momjian 已提交
964
	 */
B
Bruce Momjian 已提交
965 966 967
	if (verify_peer(conn) == -1)
	{
		close_SSL(conn);
968
		return PGRES_POLLING_FAILED;
B
Bruce Momjian 已提交
969
	}
970
#endif
B
Bruce Momjian 已提交
971

972 973
	/* SSL handshake is complete */
	return PGRES_POLLING_OK;
B
Bruce Momjian 已提交
974 975 976 977 978 979
}

/*
 *	Close SSL connection.
 */
static void
B
Bruce Momjian 已提交
980
close_SSL(PGconn *conn)
B
Bruce Momjian 已提交
981 982 983 984 985 986 987
{
	if (conn->ssl)
	{
		SSL_shutdown(conn->ssl);
		SSL_free(conn->ssl);
		conn->ssl = NULL;
	}
988 989 990 991 992 993

	if (conn->peer)
	{
		X509_free(conn->peer);
		conn->peer = NULL;
	}
B
Bruce Momjian 已提交
994 995 996 997 998 999 1000 1001 1002
}

/*
 * 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.
 */
1003
static char ssl_nomem[] = "out of memory allocating error description";
B
Bruce Momjian 已提交
1004 1005

#define SSL_ERR_LEN 128
1006 1007

static char *
B
Bruce Momjian 已提交
1008 1009
SSLerrmessage(void)
{
B
Bruce Momjian 已提交
1010 1011
	unsigned long errcode;
	const char *errreason;
B
Bruce Momjian 已提交
1012
	char	   *errbuf;
B
Bruce Momjian 已提交
1013

1014 1015 1016
	errbuf = malloc(SSL_ERR_LEN);
	if (!errbuf)
		return ssl_nomem;
B
Bruce Momjian 已提交
1017
	errcode = ERR_get_error();
B
Bruce Momjian 已提交
1018 1019
	if (errcode == 0)
	{
1020
		snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
1021 1022
		return errbuf;
	}
B
Bruce Momjian 已提交
1023
	errreason = ERR_reason_error_string(errcode);
B
Bruce Momjian 已提交
1024 1025 1026 1027
	if (errreason != NULL)
	{
		strncpy(errbuf, errreason, SSL_ERR_LEN - 1);
		errbuf[SSL_ERR_LEN - 1] = '\0';
1028 1029
		return errbuf;
	}
1030
	snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
B
Bruce Momjian 已提交
1031 1032 1033
	return errbuf;
}

1034 1035 1036 1037 1038 1039
static void
SSLerrfree(char *buf)
{
	if (buf != ssl_nomem)
		free(buf);
}
B
Bruce Momjian 已提交
1040

B
Bruce Momjian 已提交
1041
/*
1042
 *	Return pointer to OpenSSL object.
B
Bruce Momjian 已提交
1043
 */
1044
void *
B
Bruce Momjian 已提交
1045 1046 1047 1048 1049 1050
PQgetssl(PGconn *conn)
{
	if (!conn)
		return NULL;
	return conn->ssl;
}
B
Bruce Momjian 已提交
1051
#else							/* !USE_SSL */
1052

1053 1054 1055 1056 1057
void *
PQgetssl(PGconn *conn)
{
	return NULL;
}
B
Bruce Momjian 已提交
1058
#endif   /* USE_SSL */
1059

1060
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
1061

1062
/*
B
Bruce Momjian 已提交
1063
 *	Block SIGPIPE for this thread.	This prevents send()/write() from exiting
1064
 *	the application.
1065
 */
1066 1067
int
pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
1068
{
B
Bruce Momjian 已提交
1069 1070 1071
	sigset_t	sigpipe_sigset;
	sigset_t	sigset;

1072 1073 1074 1075
	sigemptyset(&sigpipe_sigset);
	sigaddset(&sigpipe_sigset, SIGPIPE);

	/* Block SIGPIPE and save previous mask for later reset */
1076 1077 1078
	SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset));
	if (SOCK_ERRNO)
		return -1;
1079 1080 1081

	/* We can have a pending SIGPIPE only if it was blocked before */
	if (sigismember(osigset, SIGPIPE))
1082
	{
1083 1084 1085
		/* Is there a pending SIGPIPE? */
		if (sigpending(&sigset) != 0)
			return -1;
B
Bruce Momjian 已提交
1086

1087 1088
		if (sigismember(&sigset, SIGPIPE))
			*sigpipe_pending = true;
1089
		else
1090
			*sigpipe_pending = false;
1091
	}
1092 1093
	else
		*sigpipe_pending = false;
B
Bruce Momjian 已提交
1094

1095
	return 0;
1096
}
B
Bruce Momjian 已提交
1097

1098
/*
1099
 *	Discard any pending SIGPIPE and reset the signal mask.
1100 1101
 *
 * Note: we are effectively assuming here that the C library doesn't queue
B
Bruce Momjian 已提交
1102
 * up multiple SIGPIPE events.	If it did, then we'd accidentally leave
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
 * ours in the queue when an event was already pending and we got another.
 * As long as it doesn't queue multiple events, we're OK because the caller
 * can't tell the difference.
 *
 * The caller should say got_epipe = FALSE if it is certain that it
 * didn't get an EPIPE error; in that case we'll skip the clear operation
 * and things are definitely OK, queuing or no.  If it got one or might have
 * gotten one, pass got_epipe = TRUE.
 *
 * We do not want this to change errno, since if it did that could lose
B
Bruce Momjian 已提交
1113
 * the error code from a preceding send().	We essentially assume that if
1114
 * we were able to do pq_block_sigpipe(), this can't fail.
1115
 */
1116 1117
void
pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
1118
{
B
Bruce Momjian 已提交
1119 1120 1121
	int			save_errno = SOCK_ERRNO;
	int			signo;
	sigset_t	sigset;
B
Bruce Momjian 已提交
1122

1123
	/* Clear SIGPIPE only if none was pending */
1124
	if (got_epipe && !sigpipe_pending)
1125
	{
1126 1127
		if (sigpending(&sigset) == 0 &&
			sigismember(&sigset, SIGPIPE))
1128
		{
B
Bruce Momjian 已提交
1129 1130
			sigset_t	sigpipe_sigset;

1131 1132
			sigemptyset(&sigpipe_sigset);
			sigaddset(&sigpipe_sigset, SIGPIPE);
1133

1134 1135 1136
			sigwait(&sigpipe_sigset, &signo);
		}
	}
B
Bruce Momjian 已提交
1137

1138
	/* Restore saved block mask */
1139 1140 1141
	pthread_sigmask(SIG_SETMASK, osigset, NULL);

	SOCK_ERRNO_SET(save_errno);
1142
}
1143

B
Bruce Momjian 已提交
1144
#endif   /* ENABLE_THREAD_SAFETY */