From: Bruce Momjian Date: Mon, 27 Sep 1999 03:13:16 +0000 (+0000) Subject: Lots of patches coming in from me today :-) X-Git-Tag: REL7_0~1455 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e0e7daef6daf9a760c9c90bc7fe5b9ba4349267f;p=postgresql 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 --- diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index d42503f3c1..97d21cb68b 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 5012d22263..1429156032 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 e53f505b4a..ced8df757c 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 60b2f2e32b..38ddcd28d3 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 160944cc5f..591867e0a9 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 67ac6ce9c3..a44fe726bf 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 b4e03c08e0..d1dc7ec121 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 9b38673d41..1d99bc31b2 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 +#include +#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 181428265a..2ee74e9212 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 4e86a8db91..bb61cdfd5b 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 85879847e6..9c87a93018 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 d22d403e01..2b3db3fe7f 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 */