From e0e7daef6daf9a760c9c90bc7fe5b9ba4349267f Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Mon, 27 Sep 1999 03:13:16 +0000
Subject: [PATCH] Lots of patches coming in from me today :-)

When drawing up a very simple "text-drawing" of how the negotiation is done,
I realised I had done this last part (fallback) in a very stupid way. Patch
#4 fixes this, and does it in a much better way.

Included is also the simple text-drawing of how the negotiation is done.

//Magnus
---
 src/backend/libpq/auth.c             |   6 +-
 src/backend/libpq/hba.c              |  77 +++++++-----
 src/backend/libpq/pg_hba.conf.sample |  12 ++
 src/backend/libpq/pqcomm.c           |  22 +++-
 src/backend/libpq/pqpacket.c         |  29 +++--
 src/backend/postmaster/postmaster.c  | 171 +++++++++++++++++++++++----
 src/include/libpq/hba.h              |   7 +-
 src/include/libpq/libpq-be.h         |  18 ++-
 src/include/libpq/pqcomm.h           |   9 +-
 src/interfaces/libpq/fe-connect.c    |  80 ++++++++++++-
 src/interfaces/libpq/fe-misc.c       |  26 +++-
 src/interfaces/libpq/libpq-int.h     |  11 +-
 12 files changed, 390 insertions(+), 78 deletions(-)

diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index d42503f3c18..97d21cb68bc 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.40 1999/07/17 20:17:00 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.41 1999/09/27 03:12:58 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -452,8 +452,7 @@ be_recvauth(Port *port)
 	 * an error message into the postmaster logfile if it failed.
 	 */
 
-	if (hba_getauthmethod(&port->raddr, port->user, port->database,
-						port->auth_arg, &port->auth_method) != STATUS_OK)
+        if (hba_getauthmethod(port) != STATUS_OK) 
 		PacketSendError(&port->pktInfo,
 						"Missing or erroneous pg_hba.conf file, see postmaster log for details");
 
@@ -470,7 +469,6 @@ be_recvauth(Port *port)
 
 		AuthRequest areq = AUTH_REQ_OK;
 		PacketDoneProc auth_handler = NULL;
-
 		switch (port->auth_method)
 		{
 			case uaReject:
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 5012d22263a..14291560322 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -5,7 +5,7 @@
  *	  wherein you authenticate a user by seeing what IP address the system
  *	  says he comes from and possibly using ident).
  *
- *	$Id: hba.c,v 1.47 1999/07/17 20:17:02 momjian Exp $
+ *	$Id: hba.c,v 1.48 1999/09/27 03:12:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -146,9 +146,7 @@ read_hba_entry2(FILE *file, UserAuth *userauth_p, char *auth_arg,
 
 
 static void
-process_hba_record(FILE *file, SockAddr *raddr, const char *user,
-				   const char *database, bool *matches_p, bool *error_p,
-				   UserAuth *userauth_p, char *auth_arg)
+process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p)
 {
 /*---------------------------------------------------------------------------
   Process the non-comment record in the config file that is next on the file.
@@ -182,16 +180,16 @@ process_hba_record(FILE *file, SockAddr *raddr, const char *user,
 
 		/* Read the rest of the line. */
 
-		read_hba_entry2(file, userauth_p, auth_arg, error_p);
+		read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p);
 
 		/*
 		 * For now, disallow methods that need AF_INET sockets to work.
 		 */
 
 		if (!*error_p &&
-			(*userauth_p == uaIdent ||
-			 *userauth_p == uaKrb4 ||
-			 *userauth_p == uaKrb5))
+			(port->auth_method == uaIdent ||
+			 port->auth_method == uaKrb4 ||
+			 port->auth_method == uaKrb5))
 			*error_p = true;
 
 		if (*error_p)
@@ -202,15 +200,33 @@ process_hba_record(FILE *file, SockAddr *raddr, const char *user,
 		 * sort of connection, ignore it.
 		 */
 
-		if ((strcmp(db, database) != 0 && strcmp(db, "all") != 0 &&
-		 (strcmp(db, "sameuser") != 0 || strcmp(database, user) != 0)) ||
-			raddr->sa.sa_family != AF_UNIX)
+		if ((strcmp(db, port->database) != 0 && strcmp(db, "all") != 0 &&
+		 (strcmp(db, "sameuser") != 0 || strcmp(port->database, port->user) != 0)) ||
+		        port->raddr.sa.sa_family != AF_UNIX)
 			return;
 	}
-	else if (strcmp(buf, "host") == 0)
+	else if (strcmp(buf, "host") == 0 || strcmp(buf, "hostssl") == 0)
 	{
 		struct in_addr file_ip_addr,
 					mask;
+		bool discard = 0; /* Discard this entry */
+
+#ifdef USE_SSL
+		/* If SSL, then check that we are on SSL */
+		if (strcmp(buf, "hostssl") == 0) {
+		  if (!port->ssl) 
+		    discard = 1; 
+		  
+		  /* Placeholder to require specific SSL level, perhaps? */
+		  /* Or a client certificate */
+
+		  /* Since we were on SSL, proceed as with normal 'host' mode */
+		}
+#else
+		/* If not SSL, we don't support this */
+		if (strcmp(buf,"hostssl") == 0) 
+		  goto syntax;
+#endif
 
 		/* Get the database. */
 
@@ -252,20 +268,27 @@ process_hba_record(FILE *file, SockAddr *raddr, const char *user,
 		 * info from it.
 		 */
 
-		read_hba_entry2(file, userauth_p, auth_arg, error_p);
+		read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p);
 
 		if (*error_p)
 			goto syntax;
 
+		/*
+		 * If told to discard earlier. Moved down here so we don't get
+		 * "out of sync" with the file.
+		 */
+		if (discard)
+		  return;
+
 		/*
 		 * If this record isn't for our database, or this is the wrong
 		 * sort of connection, ignore it.
 		 */
 
-		if ((strcmp(db, database) != 0 && strcmp(db, "all") != 0 &&
-		 (strcmp(db, "sameuser") != 0 || strcmp(database, user) != 0)) ||
-			raddr->sa.sa_family != AF_INET ||
-			((file_ip_addr.s_addr ^ raddr->in.sin_addr.s_addr) & mask.s_addr) != 0x0000)
+		if ((strcmp(db, port->database) != 0 && strcmp(db, "all") != 0 &&
+		 (strcmp(db, "sameuser") != 0 || strcmp(port->database, port->user) != 0)) ||
+		        port->raddr.sa.sa_family != AF_INET ||
+			((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0x0000)
 			return;
 	}
 	else
@@ -291,9 +314,7 @@ syntax:
 
 
 static void
-process_open_config_file(FILE *file, SockAddr *raddr, const char *user,
-						 const char *database, bool *hba_ok_p,
-						 UserAuth *userauth_p, char *auth_arg)
+process_open_config_file(FILE *file, hbaPort *port, bool *hba_ok_p)
 {
 /*---------------------------------------------------------------------------
   This function does the same thing as find_hba_entry, only with
@@ -316,8 +337,7 @@ process_open_config_file(FILE *file, SockAddr *raddr, const char *user,
 			if (c == '#')
 				read_through_eol(file);
 			else
-				process_hba_record(file, raddr, user, database,
-							 &found_entry, &error, userauth_p, auth_arg);
+				process_hba_record(file, port, &found_entry, &error);
 		}
 	}
 
@@ -326,7 +346,7 @@ process_open_config_file(FILE *file, SockAddr *raddr, const char *user,
 		/* If no matching entry was found, synthesize 'reject' entry. */
 
 		if (!found_entry)
-			*userauth_p = uaReject;
+		        port->auth_method = uaReject;
 
 		*hba_ok_p = true;
 	}
@@ -335,8 +355,7 @@ process_open_config_file(FILE *file, SockAddr *raddr, const char *user,
 
 
 static void
-find_hba_entry(SockAddr *raddr, const char *user, const char *database,
-			   bool *hba_ok_p, UserAuth *userauth_p, char *auth_arg)
+find_hba_entry(hbaPort *port, bool *hba_ok_p)
 {
 /*
  * Read the config file and find an entry that allows connection from
@@ -412,8 +431,7 @@ find_hba_entry(SockAddr *raddr, const char *user, const char *database,
 		}
 		else
 		{
-			process_open_config_file(file, raddr, user, database, hba_ok_p,
-									 userauth_p, auth_arg);
+			process_open_config_file(file, port, hba_ok_p);
 			FreeFile(file);
 		}
 		pfree(conf_file);
@@ -1057,8 +1075,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir)
 #endif
 
 int
-hba_getauthmethod(SockAddr *raddr, char *user, char *database,
-				  char *auth_arg, UserAuth *auth_method)
+hba_getauthmethod(hbaPort *port)
 {
 /*---------------------------------------------------------------------------
   Determine what authentication method should be used when accessing database
@@ -1070,7 +1087,7 @@ hba_getauthmethod(SockAddr *raddr, char *user, char *database,
 ----------------------------------------------------------------------------*/
 	bool		hba_ok = false;
 
-	find_hba_entry(raddr, user, database, &hba_ok, auth_method, auth_arg);
+	find_hba_entry(port, &hba_ok);
 
 	return hba_ok ? STATUS_OK : STATUS_ERROR;
 }
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index e53f505b4a3..ced8df757cc 100644
--- a/src/backend/libpq/pg_hba.conf.sample
+++ b/src/backend/libpq/pg_hba.conf.sample
@@ -79,6 +79,18 @@
 #
 #   krb5:   Kerberos V5 authentication is used.
 
+# Record type "hostssl"
+# ---------------------
+#
+# This record identifies the authentication to use when connecting to a
+# particular database via TCP/IP sockets over SSL. Note that normal
+# "host" records are also matched - "hostssl" records can be used to
+# require a SSL connection.
+# This keyword is only available if the server is compiled with SSL support
+# enabled.
+#
+# The format of this record is identical to that of "host".
+
 # Record type "local"
 # ------------------
 # 
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 60b2f2e32b5..38ddcd28d3f 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -28,7 +28,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: pqcomm.c,v 1.83 1999/09/08 22:57:12 tgl Exp $
+ *	$Id: pqcomm.c,v 1.84 1999/09/27 03:12:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -436,8 +436,16 @@ pq_recvbuf(void)
 	/* Can fill buffer from PqRecvLength and upwards */
 	for (;;)
 	{
-		int			r = recv(MyProcPort->sock, PqRecvBuffer + PqRecvLength,
-							 PQ_BUFFER_SIZE - PqRecvLength, 0);
+		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);
 
 		if (r < 0)
 		{
@@ -604,7 +612,13 @@ pq_flush(void)
 
 	while (bufptr < bufend)
 	{
-		int			r = send(MyProcPort->sock, bufptr, bufend - bufptr, 0);
+		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);
 
 		if (r <= 0)
 		{
diff --git a/src/backend/libpq/pqpacket.c b/src/backend/libpq/pqpacket.c
index 160944cc5fb..591867e0a9a 100644
--- a/src/backend/libpq/pqpacket.c
+++ b/src/backend/libpq/pqpacket.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.22 1999/07/17 20:17:03 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.23 1999/09/27 03:12:59 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,13 +50,20 @@ PacketReceiveSetup(Packet *pkt, PacketDoneProc iodone, void *arg)
  */
 
 int
-PacketReceiveFragment(Packet *pkt, int sock)
+PacketReceiveFragment(Port *port)
 {
 	int			got;
-
-	if ((got = read(sock, pkt->ptr, pkt->nrtodo)) > 0)
+	Packet                  *pkt = &port->pktInfo;
+
+#ifdef USE_SSL
+	if (port->ssl) 
+	  got = SSL_read(port->ssl, pkt->ptr, pkt->nrtodo);
+	else
+#endif
+  	  got = read(port->sock, pkt->ptr, pkt->nrtodo);
+	if (got > 0)
 	{
-		pkt->nrtodo -= got;
+	        pkt->nrtodo -= got;
 		pkt->ptr += got;
 
 		/* See if we have got what we need for the packet length. */
@@ -132,11 +139,19 @@ PacketSendSetup(Packet *pkt, int nbytes, PacketDoneProc iodone, void *arg)
  */
 
 int
-PacketSendFragment(Packet *pkt, int sock)
+PacketSendFragment(Port *port)
 {
 	int			done;
+        Packet                  *pkt = &port->pktInfo;
+
+#ifdef USE_SSL
+	if (port->ssl) 
+	  done = SSL_write(port->ssl, pkt->ptr, pkt->nrtodo);
+	else
+#endif
+	  done = write(port->sock, pkt->ptr, pkt->nrtodo);
 
-	if ((done = write(sock, pkt->ptr, pkt->nrtodo)) > 0)
+	if (done > 0)
 	{
 		pkt->nrtodo -= done;
 		pkt->ptr += done;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 67ac6ce9c30..a44fe726bfd 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.116 1999/09/21 20:58:19 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.117 1999/09/27 03:13:05 momjian Exp $
  *
  * NOTES
  *
@@ -183,6 +183,10 @@ 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
  */
@@ -200,6 +204,8 @@ static int	SendStop = false;
 
 static bool NetServer = false;	/* if not zero, postmaster listen for
 								 * non-local connections */
+static bool SecureNetServer = false; /* if not zero, postmaster listens for only SSL
+                                      * non-local connections */
 
 
 /*
@@ -233,6 +239,7 @@ extern int	optind,
  */
 static void pmdaemonize(void);
 static Port *ConnCreate(int serverFd);
+static void ConnFree(Port *port);
 static void reset_shared(unsigned short port);
 static void pmdie(SIGNAL_ARGS);
 static void reaper(SIGNAL_ARGS);
@@ -250,6 +257,9 @@ static long PostmasterRandom(void);
 static void RandomSalt(char *salt);
 static void SignalChildren(SIGNAL_ARGS);
 static int	CountChildren(void);
+#ifdef USE_SSL
+static void InitSSL(void);
+#endif
 
 #ifdef CYR_RECODE
 void		GetCharSetByHost(char *, int, char *);
@@ -393,7 +403,7 @@ PostmasterMain(int argc, char *argv[])
 	DataDir = getenv("PGDATA"); /* default value */
 
 	opterr = 0;
-	while ((opt = getopt(nonblank_argc, argv, "A:a:B:b:D:dim:MN:no:p:Ss")) != EOF)
+	while ((opt = getopt(nonblank_argc, argv, "A:a:B:b:D:i::dm:MN:no:p:Ss")) != EOF)
 	{
 		switch (opt)
 		{
@@ -456,6 +466,10 @@ PostmasterMain(int argc, char *argv[])
 				break;
 			case 'i':
 				NetServer = true;
+#ifdef USE_SSL
+				if (optarg && !strcasecmp(optarg,"s")) 
+				  SecureNetServer = true;
+#endif
 				break;
 			case 'm':
 				/* Multiplexed backends no longer supported. */
@@ -557,16 +571,21 @@ PostmasterMain(int argc, char *argv[])
 		exit(1);
 	}
 
+#ifdef USE_SSL
+	InitSSL();
+#endif
+
 	if (NetServer)
 	{
-		status = StreamServerPort(hostName, PostPortName, &ServerSock_INET);
-		if (status != STATUS_OK)
-		{
-			fprintf(stderr, "%s: cannot create INET stream port\n",
-					progname);
-			exit(1);
-		}
+	  status = StreamServerPort(hostName, PostPortName, &ServerSock_INET);
+	  if (status != STATUS_OK)
+	    {
+	      fprintf(stderr, "%s: cannot create INET stream port\n",
+		      progname);
+	      exit(1);
+	    }
 	}
+
 #ifndef __CYGWIN32__
 	status = StreamServerPort(NULL, PostPortName, &ServerSock_UNIX);
 	if (status != STATUS_OK)
@@ -655,6 +674,9 @@ usage(const char *progname)
 	fprintf(stderr, "\t-b backend\tuse a specific backend server executable\n");
 	fprintf(stderr, "\t-d [1|2|3]\tset debugging level\n");
 	fprintf(stderr, "\t-i \t\tlisten on TCP/IP sockets as well as Unix domain socket\n");
+#ifdef USE_SSL
+	fprintf(stderr," \t-is\t\tlisten on TCP/IP sockets as above, but only SSL connections\n");
+#endif
 	fprintf(stderr, "\t-N nprocs\tset max number of backends (1..%d, default %d)\n",
 			MAXBACKENDS, DEF_MAXBACKENDS);
 	fprintf(stderr, "\t-n \t\tdon't reinitialize shared memory after abnormal exit\n");
@@ -690,6 +712,9 @@ ServerLoop(void)
 		Port	   *port;
 		fd_set		rmask,
 					wmask;
+#ifdef USE_SSL
+		int no_select = 0;
+#endif
 
 #ifdef HAVE_SIGPROCMASK
 		sigprocmask(SIG_SETMASK, &oldsigmask, 0);
@@ -699,6 +724,18 @@ ServerLoop(void)
 
 		memmove((char *) &rmask, (char *) &readmask, sizeof(fd_set));
 		memmove((char *) &wmask, (char *) &writemask, sizeof(fd_set));
+
+#ifdef USE_SSL
+		for (curr = DLGetHead(PortList); curr; curr = DLGetSucc(curr))
+		  if (((Port *)DLE_VAL(curr))->ssl &&
+		      SSL_pending(((Port *)DLE_VAL(curr))->ssl) > 0) {
+		    no_select = 1;
+		    break;
+		  }
+		if (no_select) 
+		  FD_ZERO(&rmask); /* So we don't accept() anything below */
+		else
+#endif
 		if (select(nSockets, &rmask, &wmask, (fd_set *) NULL,
 				   (struct timeval *) NULL) < 0)
 		{
@@ -743,18 +780,20 @@ ServerLoop(void)
 #ifndef __CYGWIN32__
 		if (ServerSock_UNIX != INVALID_SOCK &&
 			FD_ISSET(ServerSock_UNIX, &rmask) &&
-			(port = ConnCreate(ServerSock_UNIX)) != NULL)
-			PacketReceiveSetup(&port->pktInfo,
+   		        (port = ConnCreate(ServerSock_UNIX)) != NULL) {
+		        PacketReceiveSetup(&port->pktInfo,
 							   readStartupPacket,
 							   (void *) port);
+		}
 #endif
 
 		if (ServerSock_INET != INVALID_SOCK &&
-			FD_ISSET(ServerSock_INET, &rmask) &&
-			(port = ConnCreate(ServerSock_INET)) != NULL)
+		    FD_ISSET(ServerSock_INET, &rmask) &&
+		    (port = ConnCreate(ServerSock_INET)) != NULL) {
 			PacketReceiveSetup(&port->pktInfo,
 							   readStartupPacket,
 							   (void *) port);
+		}
 
 		/* Build up new masks for select(). */
 
@@ -767,14 +806,26 @@ ServerLoop(void)
 			Port	   *port = (Port *) DLE_VAL(curr);
 			int			status = STATUS_OK;
 			Dlelem	   *next;
+			int        readyread = 0;
+
+#ifdef USE_SSL
+			if (port->ssl) {
+			  if (SSL_pending(port->ssl) ||
+			      FD_ISSET(port->sock, &rmask))
+			    readyread = 1;
+			}
+			else
+#endif
+			  if (FD_ISSET(port->sock, &rmask))
+			readyread = 1;
 
-			if (FD_ISSET(port->sock, &rmask))
+			if (readyread)
 			{
 				if (DebugLvl > 1)
 					fprintf(stderr, "%s: ServerLoop:\t\thandling reading %d\n",
 							progname, port->sock);
 
-				if (PacketReceiveFragment(&port->pktInfo, port->sock) != STATUS_OK)
+				if (PacketReceiveFragment(port) != STATUS_OK)
 					status = STATUS_ERROR;
 			}
 
@@ -784,7 +835,7 @@ ServerLoop(void)
 					fprintf(stderr, "%s: ServerLoop:\t\thandling writing %d\n",
 							progname, port->sock);
 
-				if (PacketSendFragment(&port->pktInfo, port->sock) != STATUS_OK)
+				if (PacketSendFragment(port) != STATUS_OK)
 					status = STATUS_ERROR;
 			}
 
@@ -827,7 +878,7 @@ ServerLoop(void)
 			{
 				StreamClose(port->sock);
 				DLRemove(curr);
-				free(port);
+				ConnFree(port);
 				DLFreeElem(curr);
 			}
 			else
@@ -896,7 +947,7 @@ readStartupPacket(void *arg, PacketLen len, void *pkt)
 
 	port = (Port *) arg;
 	si = (StartupPacket *) pkt;
-
+	
 	/*
 	 * The first field is either a protocol version number or a special
 	 * request code.
@@ -907,8 +958,45 @@ readStartupPacket(void *arg, PacketLen len, void *pkt)
 	if (port->proto == CANCEL_REQUEST_CODE)
 		return processCancelRequest(port, len, pkt);
 
+	if (port->proto == NEGOTIATE_SSL_CODE) {
+	  char SSLok;
+	  
+#ifdef USE_SSL
+	  SSLok = 'S'; /* Support for SSL */
+#else
+	  SSLok = 'N'; /* No support for SSL */
+#endif
+	  if (send(port->sock, &SSLok, 1, 0) != 1) {
+	    perror("Failed to send SSL negotiation response");
+	    return STATUS_ERROR; /* Close connection */
+	  }
+	  
+#ifdef USE_SSL
+	  if (!(port->ssl = SSL_new(SSL_context)) ||
+	      !SSL_set_fd(port->ssl, port->sock) ||
+	      SSL_accept(port->ssl) <= 0)
+	  {
+	    fprintf(stderr,"Failed to initialize SSL connection: %s, errno: %d (%s)\n",
+		    ERR_reason_error_string(ERR_get_error()), errno, strerror(errno));
+	    return STATUS_ERROR;
+	  }
+#endif
+	  /* ready for the normal startup packet */
+	  PacketReceiveSetup(&port->pktInfo,
+			     readStartupPacket,
+			     (void *)port);
+	  return STATUS_OK; /* Do not close connection */
+	} 
+
 	/* Could add additional special packet types here */
 
+	/* Any SSL negotiation must have taken place here, so drop the connection
+	 * ASAP if we require SSL */
+	if (SecureNetServer && !port->ssl) {
+	  PacketSendError(&port->pktInfo, "Backend requires secure connection.");
+	  return STATUS_OK;
+	}
+
 	/* Check we can handle the protocol the frontend is using. */
 
 	if (PG_PROTOCOL_MAJOR(port->proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
@@ -952,7 +1040,6 @@ readStartupPacket(void *arg, PacketLen len, void *pkt)
 	return STATUS_OK;			/* don't close the connection yet */
 }
 
-
 /*
  * The client has sent a cancel request packet, not a normal
  * start-a-new-backend packet.	Perform the necessary processing.
@@ -1039,6 +1126,20 @@ ConnCreate(int serverFd)
 	return port;
 }
 
+/*
+ * ConnFree -- cree a local connection data structure
+ */
+void
+ConnFree(Port *conn) 
+{
+#ifdef USE_SSL
+        if (conn->ssl) {
+	     SSL_free(conn->ssl);
+	}
+#endif
+	free(conn);
+}
+
 /*
  * reset_shared -- reset shared memory and semaphores
  */
@@ -1502,7 +1603,7 @@ DoBackend(Port *port)
 	sigprocmask(SIG_SETMASK, &oldsigmask, 0);
 
 	/* Close the postmaster sockets */
-	if (NetServer)
+	if (NetServer) 
 		StreamClose(ServerSock_INET);
 #ifndef __CYGWIN32__
 	StreamClose(ServerSock_UNIX);
@@ -1729,3 +1830,33 @@ CountChildren(void)
 	}
 	return cnt;
 }
+
+
+/*
+ * 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) {
+    fprintf(stderr, "Failed to create SSL context: %s\n",ERR_reason_error_string(ERR_get_error()));
+    exit(1);
+  }
+  snprintf(fnbuf,sizeof(fnbuf),"%s/server.crt", DataDir);
+  if (!SSL_CTX_use_certificate_file(SSL_context, fnbuf, SSL_FILETYPE_PEM)) {
+    fprintf(stderr, "Failed to load server certificate (%s): %s\n",fnbuf,ERR_reason_error_string(ERR_get_error()));
+    exit(1);
+  }
+  snprintf(fnbuf,sizeof(fnbuf),"%s/server.key", DataDir);
+  if (!SSL_CTX_use_PrivateKey_file(SSL_context, fnbuf, SSL_FILETYPE_PEM)) {
+    fprintf(stderr, "Failed to load private key file (%s): %s\n",fnbuf,ERR_reason_error_string(ERR_get_error()));
+    exit(1);
+  }
+  if (!SSL_CTX_check_private_key(SSL_context)) {
+    fprintf(stderr, "Check of private key failed: %s\n",ERR_reason_error_string(ERR_get_error()));
+    exit(1);
+  }
+}
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index b4e03c08e07..d1dc7ec121f 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -4,7 +4,7 @@
  *	  Interface to hba.c
  *
  *
- * $Id: hba.h,v 1.14 1999/07/14 01:20:17 momjian Exp $
+ * $Id: hba.h,v 1.15 1999/09/27 03:13:10 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,8 +49,9 @@ typedef enum UserAuth
 	uaCrypt
 } UserAuth;
 
-int hba_getauthmethod(SockAddr *raddr, char *user, char *database,
-				  char *auth_arg, UserAuth *auth_method);
+typedef struct Port hbaPort;
+
+int hba_getauthmethod(hbaPort *port);
 int authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr,
 		  const char *postgres_username, const char *auth_arg);
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 9b38673d41e..1d99bc31b2d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-be.h,v 1.16 1999/07/17 20:18:28 momjian Exp $
+ * $Id: libpq-be.h,v 1.17 1999/09/27 03:13:11 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,11 @@
 
 #include "libpq/hba.h"
 
+#ifdef USE_SSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
 
 /* Protocol v0 password packet. */
 
@@ -126,6 +131,13 @@ typedef struct Port
 	char		tty[SM_TTY + 1];
 	char		auth_arg[MAX_AUTH_ARG];
 	UserAuth	auth_method;
+
+        /*
+	 * SSL structures 
+	 */
+#ifdef USE_SSL
+        SSL             *ssl;
+#endif
 } Port;
 
 
@@ -136,9 +148,9 @@ extern ProtocolVersion FrontendProtocol;
  * prototypes for functions in pqpacket.c
  */
 void		PacketReceiveSetup(Packet *pkt, PacketDoneProc iodone, void *arg);
-int			PacketReceiveFragment(Packet *pkt, int sock);
+int			PacketReceiveFragment(Port *port);
 void		PacketSendSetup(Packet *pkt, int nbytes, PacketDoneProc iodone, void *arg);
-int			PacketSendFragment(Packet *pkt, int sock);
+int			PacketSendFragment(Port *port);
 void		PacketSendError(Packet *pkt, char *errormsg);
 
 #endif	 /* LIBPQ_BE_H */
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 181428265ac..2ee74e92120 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -8,7 +8,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pqcomm.h,v 1.39 1999/08/31 04:26:33 tgl Exp $
+ * $Id: pqcomm.h,v 1.40 1999/09/27 03:13:11 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -152,4 +152,11 @@ typedef struct CancelRequestPacket
 	uint32		cancelAuthCode; /* secret key to authorize cancel */
 } CancelRequestPacket;
 
+
+/*
+ * A client can also start by sending a SSL negotiation request, to get a
+ * secure channel.
+ */
+#define NEGOTIATE_SSL_CODE PG_PROTOCOL(1234,5679)
+
 #endif	 /* PQCOMM_H */
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 4e86a8db91e..bb61cdfd5bd 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.102 1999/08/31 01:37:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.103 1999/09/27 03:13:16 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,10 @@
 #include "mb/pg_wchar.h"
 #endif
 
+#ifdef USE_SSL
+static SSL_CTX *SSL_context = NULL;
+#endif
+
 static ConnStatusType connectDB(PGconn *conn);
 static PGconn *makeEmptyPGconn(void);
 static void freePGconn(PGconn *conn);
@@ -508,6 +512,12 @@ connectDB(PGconn *conn)
 				family;
 	char		beresp;
 	int			on = 1;
+#ifdef USE_SSL
+	StartupPacket           np; /* Used to negotiate SSL connection */
+	char                    SSLok;
+	static int              allow_ssl_try = 1;  /* Allowed to do SSL negotiation */
+	int                     tried_ssl = 0;      /* Set if SSL negotiation was tried */
+#endif
 
 	/*
 	 * parse dbName to get all additional info in it, if any
@@ -591,6 +601,70 @@ connectDB(PGconn *conn)
 		goto connect_errReturn;
 	}
 
+	/* This needs to be done before we set into nonblocking, since SSL negotiation
+	 * does not like that mode */
+
+#ifdef USE_SSL
+	/* Attempt to negotiate SSL usage */
+	if (allow_ssl_try) {
+	  tried_ssl = 1;
+	  memset((char *)&np, 0, sizeof(np));
+	  np.protoVersion = htonl(NEGOTIATE_SSL_CODE);
+	  if (pqPacketSend(conn, (char *) &np, sizeof(StartupPacket)) != STATUS_OK)
+	    {
+	      sprintf(conn->errorMessage,
+		      "connectDB() -- couldn't send SSL negotiation packet: errno=%d\n%s\n",
+		      errno, strerror(errno));
+	      goto connect_errReturn;
+	    }
+	  /* Now receive the backends response */
+	  if (recv(conn->sock, &SSLok, 1, 0) != 1) {
+	    sprintf(conn->errorMessage, "PQconnectDB() -- couldn't read backend response: errno=%d\n%s\n",
+		    errno, strerror(errno));
+	    goto connect_errReturn;
+	  }
+	  if (SSLok == 'S') {
+	    if (!SSL_context) 
+	      {
+		SSL_load_error_strings();
+		SSL_library_init();
+		SSL_context = SSL_CTX_new(SSLv23_method());
+		if (!SSL_context) {
+		  sprintf(conn->errorMessage,
+			  "connectDB() -- couldn't create SSL context: %s\n",
+			  ERR_reason_error_string(ERR_get_error()));
+		  goto connect_errReturn;
+		}
+	      }
+	    if (!(conn->ssl = SSL_new(SSL_context)) ||
+		!SSL_set_fd(conn->ssl, conn->sock) ||
+		SSL_connect(conn->ssl) <= 0) 
+	      {
+		sprintf(conn->errorMessage,
+			"connectDB() -- couldn't establish SSL connection: %s\n",
+			ERR_reason_error_string(ERR_get_error()));
+		goto connect_errReturn;
+	      }
+	    /* SSL connection finished. Continue to send startup packet */
+	  }
+	  else if (SSLok == 'E') {
+	    /* Received error - probably protocol mismatch */
+	    if (conn->Pfdebug)
+	      fprintf(conn->Pfdebug, "Backend reports error, attempting fallback to pre-6.6.\n");
+	    close(conn->sock);
+	    allow_ssl_try = 0;
+	    return connectDB(conn);
+	  }
+	  else if (SSLok != 'N') {
+	    strcpy(conn->errorMessage,
+		   "Received invalid negotiation response.\n");
+	    goto connect_errReturn;
+	  }
+	}
+	else
+	  allow_ssl_try = 1; /* We'll allow an attempt to use SSL next time */
+#endif
+
 	/*
 	 * Set the right options. We need nonblocking I/O, and we don't want
 	 * delay of outgoing data.
@@ -896,6 +970,10 @@ freePGconn(PGconn *conn)
 	if (!conn)
 		return;
 	pqClearAsyncResult(conn);	/* deallocate result and curTuple */
+#ifdef USE_SSL
+	if (conn->ssl)
+	  SSL_free(conn->ssl);
+#endif
 	if (conn->sock >= 0)
 #ifdef WIN32
 		closesocket(conn->sock);
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 85879847e6e..9c87a930186 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -24,7 +24,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.30 1999/09/13 03:00:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.31 1999/09/27 03:13:16 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -351,7 +351,13 @@ pqReadData(PGconn *conn)
 
 	/* OK, try to read some data */
 tryAgain:
-	nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
+#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);
 	if (nread < 0)
 	{
@@ -420,7 +426,13 @@ tryAgain:
 	 * arrived.
 	 */
 tryAgain2:
-	nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
+#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);
 	if (nread < 0)
 	{
@@ -494,7 +506,13 @@ pqFlush(PGconn *conn)
 		pqsigfunc	oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
 #endif
 
-		int			sent = send(conn->sock, ptr, len, 0);
+		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);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index d22d403e016..2b3db3fe7fd 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -11,7 +11,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-int.h,v 1.11 1999/08/31 01:37:37 tgl Exp $
+ * $Id: libpq-int.h,v 1.12 1999/09/27 03:13:16 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,11 @@
 #include "pqexpbuffer.h"
 
 
+#ifdef USE_SSL
+#include "openssl/ssl.h"
+#include "openssl/err.h"
+#endif
+
 /* libpq supports this version of the frontend/backend protocol.
  *
  * NB: we used to use PG_PROTOCOL_LATEST from the backend pqcomm.h file,
@@ -215,6 +220,10 @@ struct pg_conn
 	PGresult   *result;			/* result being constructed */
 	PGresAttValue *curTuple;	/* tuple currently being read */
 
+#ifdef USE_SSL
+        SSL *ssl;
+#endif
+
 	/* Buffer for current error message */
 	PQExpBufferData	errorMessage;	/* expansible string */
 
-- 
GitLab