]> granicus.if.org Git - postgresql/commitdiff
Rearrange the code in auth.c so that all functions for a single authentication
authorMagnus Hagander <magnus@hagander.net>
Fri, 1 Aug 2008 11:41:12 +0000 (11:41 +0000)
committerMagnus Hagander <magnus@hagander.net>
Fri, 1 Aug 2008 11:41:12 +0000 (11:41 +0000)
method is grouped together in a reasonably similar way, keeping the "global
shared functions" together in their own section as well. Makes it a lot easier
to find your way around the code.

src/backend/libpq/auth.c

index 3470417f241b7ad0da5268f4a37ff19372e8e701..1c50b8e588273259a571be936fa6affdbf2160ad 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.166 2008/08/01 09:09:49 mha Exp $
+ *       $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.167 2008/08/01 11:41:12 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "libpq/pqformat.h"
 #include "storage/ipc.h"
 
-
+/*----------------------------------------------------------------
+ * Global authentication functions 
+ *----------------------------------------------------------------
+ */
 static void sendAuthRequest(Port *port, AuthRequest areq);
 static void auth_failed(Port *port, int status);
 static char *recv_password_packet(Port *port);
 static int     recv_and_check_password_packet(Port *port);
-static int  authident(hbaPort *port);
 
-char      *pg_krb_server_keyfile;
-char      *pg_krb_srvnam;
-bool           pg_krb_caseins_users;
-char      *pg_krb_server_hostname = NULL;
-char      *pg_krb_realm = NULL;
 
+/*----------------------------------------------------------------
+ * Ident authentication
+ *----------------------------------------------------------------
+ */
 /* Max size of username ident server can return */
 #define IDENT_USERNAME_MAX 512
 
 /* Standard TCP port number for Ident service. Assigned by IANA */
 #define IDENT_PORT 113
 
+static int  authident(hbaPort *port);
+
+
+/*----------------------------------------------------------------
+ * PAM authentication
+ *----------------------------------------------------------------
+ */
 #ifdef USE_PAM
 #ifdef HAVE_PAM_PAM_APPL_H
 #include <pam/pam_appl.h>
@@ -75,6 +83,11 @@ static Port *pam_port_cludge;        /* Workaround for passing "Port *port" into
                                                                 * pam_passwd_conv_proc */
 #endif   /* USE_PAM */
 
+
+/*----------------------------------------------------------------
+ * LDAP authentication
+ *----------------------------------------------------------------
+ */
 #ifdef USE_LDAP
 #ifndef WIN32
 /* We use a deprecated function to keep the codepath the same as win32. */
@@ -95,21 +108,33 @@ ULONG(*__ldap_start_tls_sA) (
 #endif
 
 static int     CheckLDAPAuth(Port *port);
-#endif
+#endif /* USE_LDAP */
+
+
+/*----------------------------------------------------------------
+ * Kerberos and GSSAPI GUCs
+ *----------------------------------------------------------------
+ */
+char      *pg_krb_server_keyfile;
+char      *pg_krb_srvnam;
+bool           pg_krb_caseins_users;
+char      *pg_krb_server_hostname = NULL;
+char      *pg_krb_realm = NULL;
 
 
-#ifdef KRB5
 /*----------------------------------------------------------------
  * MIT Kerberos authentication system - protocol version 5
  *----------------------------------------------------------------
  */
+static int pg_krb5_recvauth(Port *port);
+
+#ifdef KRB5
 
 #include <krb5.h>
 /* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */
 #if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__)
 #include <com_err.h>
 #endif
-
 /*
  * Various krb5 state which is not connection specfic, and a flag to
  * indicate whether we have initialised it yet.
@@ -118,458 +143,549 @@ static int      pg_krb5_initialised;
 static krb5_context pg_krb5_context;
 static krb5_keytab pg_krb5_keytab;
 static krb5_principal pg_krb5_server;
+#endif /* KRB5 */
 
 
-static int
-pg_krb5_init(void)
-{
-       krb5_error_code retval;
-       char       *khostname;
+/*----------------------------------------------------------------
+ * GSSAPI Authentication
+ *----------------------------------------------------------------
+ */
+static int pg_GSS_recvauth(Port *port);
 
-       if (pg_krb5_initialised)
-               return STATUS_OK;
+#ifdef ENABLE_GSS
+#if defined(HAVE_GSSAPI_H)
+#include <gssapi.h>
+#else
+#include <gssapi/gssapi.h>
+#endif
+#endif /* ENABLE_GSS */
 
-       retval = krb5_init_context(&pg_krb5_context);
-       if (retval)
-       {
-               ereport(LOG,
-                               (errmsg("Kerberos initialization returned error %d",
-                                               retval)));
-               com_err("postgres", retval, "while initializing krb5");
-               return STATUS_ERROR;
-       }
 
-       retval = krb5_kt_resolve(pg_krb5_context, pg_krb_server_keyfile, &pg_krb5_keytab);
-       if (retval)
-       {
-               ereport(LOG,
-                               (errmsg("Kerberos keytab resolving returned error %d",
-                                               retval)));
-               com_err("postgres", retval, "while resolving keytab file \"%s\"",
-                               pg_krb_server_keyfile);
-               krb5_free_context(pg_krb5_context);
-               return STATUS_ERROR;
-       }
+/*----------------------------------------------------------------
+ * SSPI Authentication
+ *----------------------------------------------------------------
+ */
+static int pg_SSPI_recvauth(Port *port);
 
-       /*
-        * If no hostname was specified, pg_krb_server_hostname is already NULL.
-        * If it's set to blank, force it to NULL.
-        */
-       khostname = pg_krb_server_hostname;
-       if (khostname && khostname[0] == '\0')
-               khostname = NULL;
+#ifdef ENABLE_SSPI
+typedef                SECURITY_STATUS
+                       (WINAPI * QUERY_SECURITY_CONTEXT_TOKEN_FN) (
+                                                                                                          PCtxtHandle, void **);
+#endif
 
-       retval = krb5_sname_to_principal(pg_krb5_context,
-                                                                        khostname,
-                                                                        pg_krb_srvnam,
-                                                                        KRB5_NT_SRV_HST,
-                                                                        &pg_krb5_server);
-       if (retval)
-       {
-               ereport(LOG,
-                               (errmsg("Kerberos sname_to_principal(\"%s\", \"%s\") returned error %d",
-                khostname ? khostname : "server hostname", pg_krb_srvnam, retval)));
-               com_err("postgres", retval,
-               "while getting server principal for server \"%s\" for service \"%s\"",
-                               khostname ? khostname : "server hostname", pg_krb_srvnam);
-               krb5_kt_close(pg_krb5_context, pg_krb5_keytab);
-               krb5_free_context(pg_krb5_context);
-               return STATUS_ERROR;
-       }
 
-       pg_krb5_initialised = 1;
-       return STATUS_OK;
-}
+
+/*----------------------------------------------------------------
+ * Global authentication functions
+ *----------------------------------------------------------------
+ */
 
 
 /*
- * pg_krb5_recvauth -- server routine to receive authentication information
- *                                        from the client
- *
- * We still need to compare the username obtained from the client's setup
- * packet to the authenticated name.
+ * Tell the user the authentication failed, but not (much about) why.
  *
- * We have our own keytab file because postgres is unlikely to run as root,
- * and so cannot read the default keytab.
+ * There is a tradeoff here between security concerns and making life
+ * unnecessarily difficult for legitimate users.  We would not, for example,
+ * want to report the password we were expecting to receive...
+ * But it seems useful to report the username and authorization method
+ * in use, and these are items that must be presumed known to an attacker
+ * anyway.
+ * Note that many sorts of failure report additional information in the
+ * postmaster log, which we hope is only readable by good guys.
  */
-static int
-pg_krb5_recvauth(Port *port)
+static void
+auth_failed(Port *port, int status)
 {
-       krb5_error_code retval;
-       int                     ret;
-       krb5_auth_context auth_context = NULL;
-       krb5_ticket *ticket;
-       char       *kusername;
-       char       *cp;
-
-       if (get_role_line(port->user_name) == NULL)
-               return STATUS_ERROR;
+       const char *errstr;
 
-       ret = pg_krb5_init();
-       if (ret != STATUS_OK)
-               return ret;
+       /*
+        * If we failed due to EOF from client, just quit; there's no point in
+        * trying to send a message to the client, and not much point in logging
+        * the failure in the postmaster log.  (Logging the failure might be
+        * desirable, were it not for the fact that libpq closes the connection
+        * unceremoniously if challenged for a password when it hasn't got one to
+        * send.  We'll get a useless log entry for every psql connection under
+        * password auth, even if it's perfectly successful, if we log STATUS_EOF
+        * events.)
+        */
+       if (status == STATUS_EOF)
+               proc_exit(0);
 
-       retval = krb5_recvauth(pg_krb5_context, &auth_context,
-                                                  (krb5_pointer) & port->sock, pg_krb_srvnam,
-                                                  pg_krb5_server, 0, pg_krb5_keytab, &ticket);
-       if (retval)
+       switch (port->auth_method)
        {
-               ereport(LOG,
-                               (errmsg("Kerberos recvauth returned error %d",
-                                               retval)));
-               com_err("postgres", retval, "from krb5_recvauth");
-               return STATUS_ERROR;
+               case uaReject:
+                       errstr = gettext_noop("authentication failed for user \"%s\": host rejected");
+                       break;
+               case uaKrb5:
+                       errstr = gettext_noop("Kerberos 5 authentication failed for user \"%s\"");
+                       break;
+               case uaGSS:
+                       errstr = gettext_noop("GSSAPI authentication failed for user \"%s\"");
+                       break;
+               case uaSSPI:
+                       errstr = gettext_noop("SSPI authentication failed for user \"%s\"");
+                       break;
+               case uaTrust:
+                       errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
+                       break;
+               case uaIdent:
+                       errstr = gettext_noop("Ident authentication failed for user \"%s\"");
+                       break;
+               case uaMD5:
+               case uaCrypt:
+               case uaPassword:
+                       errstr = gettext_noop("password authentication failed for user \"%s\"");
+                       break;
+#ifdef USE_PAM
+               case uaPAM:
+                       errstr = gettext_noop("PAM authentication failed for user \"%s\"");
+                       break;
+#endif   /* USE_PAM */
+#ifdef USE_LDAP
+               case uaLDAP:
+                       errstr = gettext_noop("LDAP authentication failed for user \"%s\"");
+                       break;
+#endif   /* USE_LDAP */
+               default:
+                       errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
+                       break;
        }
 
+       ereport(FATAL,
+                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                        errmsg(errstr, port->user_name)));
+       /* doesn't return */
+}
+
+
+/*
+ * Client authentication starts here.  If there is an error, this
+ * function does not return and the backend process is terminated.
+ */
+void
+ClientAuthentication(Port *port)
+{
+       int                     status = STATUS_ERROR;
+
        /*
-        * The "client" structure comes out of the ticket and is therefore
-        * authenticated.  Use it to check the username obtained from the
-        * postmaster startup packet.
+        * Get the authentication method to use for this frontend/database
+        * combination.  Note: a failure return indicates a problem with the hba
+        * config file, not with the request.  hba.c should have dropped an error
+        * message into the postmaster logfile if it failed.
         */
-#if defined(HAVE_KRB5_TICKET_ENC_PART2)
-       retval = krb5_unparse_name(pg_krb5_context,
-                                                          ticket->enc_part2->client, &kusername);
-#elif defined(HAVE_KRB5_TICKET_CLIENT)
-       retval = krb5_unparse_name(pg_krb5_context,
-                                                          ticket->client, &kusername);
-#else
-#error "bogus configuration"
-#endif
-       if (retval)
-       {
-               ereport(LOG,
-                               (errmsg("Kerberos unparse_name returned error %d",
-                                               retval)));
-               com_err("postgres", retval, "while unparsing client name");
-               krb5_free_ticket(pg_krb5_context, ticket);
-               krb5_auth_con_free(pg_krb5_context, auth_context);
-               return STATUS_ERROR;
-       }
+       if (hba_getauthmethod(port) != STATUS_OK)
+               ereport(FATAL,
+                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                errmsg("missing or erroneous pg_hba.conf file"),
+                                errhint("See server log for details.")));
 
-       cp = strchr(kusername, '@');
-       if (cp)
+       switch (port->auth_method)
        {
-               *cp = '\0';
-               cp++;
-
-               if (pg_krb_realm != NULL && strlen(pg_krb_realm))
-               {
-                       /* Match realm against configured */
-                       if (pg_krb_caseins_users)
-                               ret = pg_strcasecmp(pg_krb_realm, cp);
-                       else
-                               ret = strcmp(pg_krb_realm, cp);
+               case uaReject:
 
-                       if (ret)
+                       /*
+                        * This could have come from an explicit "reject" entry in
+                        * pg_hba.conf, but more likely it means there was no matching
+                        * entry.  Take pity on the poor user and issue a helpful error
+                        * message.  NOTE: this is not a security breach, because all the
+                        * info reported here is known at the frontend and must be assumed
+                        * known to bad guys. We're merely helping out the less clueful
+                        * good guys.
+                        */
                        {
-                               elog(DEBUG2,
-                                        "krb5 realm (%s) and configured realm (%s) don't match",
-                                        cp, pg_krb_realm);
+                               char            hostinfo[NI_MAXHOST];
 
-                               krb5_free_ticket(pg_krb5_context, ticket);
-                               krb5_auth_con_free(pg_krb5_context, auth_context);
-                               return STATUS_ERROR;
+                               pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
+                                                                  hostinfo, sizeof(hostinfo),
+                                                                  NULL, 0,
+                                                                  NI_NUMERICHOST);
+
+#ifdef USE_SSL
+                               ereport(FATAL,
+                                               (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                                                errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
+                                                         hostinfo, port->user_name, port->database_name,
+                                                               port->ssl ? _("SSL on") : _("SSL off"))));
+#else
+                               ereport(FATAL,
+                                               (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                                                errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
+                                                  hostinfo, port->user_name, port->database_name)));
+#endif
+                               break;
                        }
-               }
-       }
-       else if (pg_krb_realm && strlen(pg_krb_realm))
-       {
-               elog(DEBUG2,
-                        "krb5 did not return realm but realm matching was requested");
 
-               krb5_free_ticket(pg_krb5_context, ticket);
-               krb5_auth_con_free(pg_krb5_context, auth_context);
-               return STATUS_ERROR;
-       }
+               case uaKrb5:
+                       sendAuthRequest(port, AUTH_REQ_KRB5);
+                       status = pg_krb5_recvauth(port);
+                       break;
 
-       if (pg_krb_caseins_users)
-               ret = pg_strncasecmp(port->user_name, kusername, SM_DATABASE_USER);
-       else
-               ret = strncmp(port->user_name, kusername, SM_DATABASE_USER);
-       if (ret)
-       {
-               ereport(LOG,
-                               (errmsg("unexpected Kerberos user name received from client (received \"%s\", expected \"%s\")",
-                                               port->user_name, kusername)));
-               ret = STATUS_ERROR;
-       }
-       else
-               ret = STATUS_OK;
+               case uaGSS:
+                       sendAuthRequest(port, AUTH_REQ_GSS);
+                       status = pg_GSS_recvauth(port);
+                       break;
 
-       krb5_free_ticket(pg_krb5_context, ticket);
-       krb5_auth_con_free(pg_krb5_context, auth_context);
-       free(kusername);
+               case uaSSPI:
+                       sendAuthRequest(port, AUTH_REQ_SSPI);
+                       status = pg_SSPI_recvauth(port);
+                       break;
 
-       return ret;
-}
-#else
+               case uaIdent:
 
-static int
-pg_krb5_recvauth(Port *port)
-{
-       ereport(LOG,
-                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                        errmsg("Kerberos 5 not implemented on this server")));
-       return STATUS_ERROR;
-}
-#endif   /* KRB5 */
+                       /*
+                        * If we are doing ident on unix-domain sockets, use SCM_CREDS
+                        * only if it is defined and SO_PEERCRED isn't.
+                        */
+#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && \
+       (defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
+        (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS)))
+                       if (port->raddr.addr.ss_family == AF_UNIX)
+                       {
+#if defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
 
-/*----------------------------------------------------------------
- * GSSAPI authentication system
- *----------------------------------------------------------------
- */
+                               /*
+                                * Receive credentials on next message receipt, BSD/OS,
+                                * NetBSD. We need to set this before the client sends the
+                                * next packet.
+                                */
+                               int                     on = 1;
 
-#ifdef ENABLE_GSS
+                               if (setsockopt(port->sock, 0, LOCAL_CREDS, &on, sizeof(on)) < 0)
+                                       ereport(FATAL,
+                                                       (errcode_for_socket_access(),
+                                          errmsg("could not enable credential reception: %m")));
+#endif
 
-#if defined(HAVE_GSSAPI_H)
-#include <gssapi.h>
-#else
-#include <gssapi/gssapi.h>
+                               sendAuthRequest(port, AUTH_REQ_SCM_CREDS);
+                       }
 #endif
+                       status = authident(port);
+                       break;
 
-#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
-/*
- * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
- * that contain the OIDs required. Redefine here, values copied
- * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
- */
-static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
-{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
-static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
+               case uaMD5:
+                       sendAuthRequest(port, AUTH_REQ_MD5);
+                       status = recv_and_check_password_packet(port);
+                       break;
+
+               case uaCrypt:
+                       sendAuthRequest(port, AUTH_REQ_CRYPT);
+                       status = recv_and_check_password_packet(port);
+                       break;
+
+               case uaPassword:
+                       sendAuthRequest(port, AUTH_REQ_PASSWORD);
+                       status = recv_and_check_password_packet(port);
+                       break;
+
+#ifdef USE_PAM
+               case uaPAM:
+                       pam_port_cludge = port;
+                       status = CheckPAMAuth(port, port->user_name, "");
+                       break;
+#endif   /* USE_PAM */
+
+#ifdef USE_LDAP
+               case uaLDAP:
+                       status = CheckLDAPAuth(port);
+                       break;
 #endif
 
+               case uaTrust:
+                       status = STATUS_OK;
+                       break;
+       }
+
+       if (status == STATUS_OK)
+               sendAuthRequest(port, AUTH_REQ_OK);
+       else
+               auth_failed(port, status);
+}
+
 
+/*
+ * Send an authentication request packet to the frontend.
+ */
 static void
-pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
+sendAuthRequest(Port *port, AuthRequest areq)
 {
-       gss_buffer_desc gmsg;
-       OM_uint32       lmaj_s,
-                               lmin_s,
-                               msg_ctx;
-       char            msg_major[128],
-                               msg_minor[128];
+       StringInfoData buf;
 
-       /* Fetch major status message */
-       msg_ctx = 0;
-       lmaj_s = gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
-                                                               GSS_C_NO_OID, &msg_ctx, &gmsg);
-       strlcpy(msg_major, gmsg.value, sizeof(msg_major));
-       gss_release_buffer(&lmin_s, &gmsg);
+       pq_beginmessage(&buf, 'R');
+       pq_sendint(&buf, (int32) areq, sizeof(int32));
 
-       if (msg_ctx)
+       /* Add the salt for encrypted passwords. */
+       if (areq == AUTH_REQ_MD5)
+               pq_sendbytes(&buf, port->md5Salt, 4);
+       else if (areq == AUTH_REQ_CRYPT)
+               pq_sendbytes(&buf, port->cryptSalt, 2);
 
-               /*
-                * More than one message available. XXX: Should we loop and read all
-                * messages? (same below)
-                */
-               ereport(WARNING,
-                               (errmsg_internal("incomplete GSS error report")));
+#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 
-       /* Fetch mechanism minor status message */
-       msg_ctx = 0;
-       lmaj_s = gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
-                                                               GSS_C_NO_OID, &msg_ctx, &gmsg);
-       strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
-       gss_release_buffer(&lmin_s, &gmsg);
+       /*
+        * Add the authentication data for the next step of the GSSAPI or SSPI
+        * negotiation.
+        */
+       else if (areq == AUTH_REQ_GSS_CONT)
+       {
+               if (port->gss->outbuf.length > 0)
+               {
+                       elog(DEBUG4, "sending GSS token of length %u",
+                                (unsigned int) port->gss->outbuf.length);
 
-       if (msg_ctx)
-               ereport(WARNING,
-                               (errmsg_internal("incomplete GSS minor error report")));
+                       pq_sendbytes(&buf, port->gss->outbuf.value, port->gss->outbuf.length);
+               }
+       }
+#endif
+
+       pq_endmessage(&buf);
 
        /*
-        * errmsg_internal, since translation of the first part must be done
-        * before calling this function anyway.
+        * Flush message so client will see it, except for AUTH_REQ_OK, which need
+        * not be sent until we are ready for queries.
         */
-       ereport(severity,
-                       (errmsg_internal("%s", errmsg),
-                        errdetail("%s: %s", msg_major, msg_minor)));
+       if (areq != AUTH_REQ_OK)
+               pq_flush();
 }
 
-static int
-pg_GSS_recvauth(Port *port)
+/*
+ * Collect password response packet from frontend.
+ *
+ * Returns NULL if couldn't get password, else palloc'd string.
+ */
+static char *
+recv_password_packet(Port *port)
 {
-       OM_uint32       maj_stat,
-                               min_stat,
-                               lmin_s,
-                               gflags;
-       int                     mtype;
-       int                     ret;
        StringInfoData buf;
-       gss_buffer_desc gbuf;
-
-       /*
-        * GSS auth is not supported for protocol versions before 3, because it
-        * relies on the overall message length word to determine the GSS payload
-        * size in AuthenticationGSSContinue and PasswordMessage messages.
-        * (This is, in fact, a design error in our GSS support, because protocol
-        * messages are supposed to be parsable without relying on the length
-        * word; but it's not worth changing it now.)
-        */
-       if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-               ereport(FATAL,
-                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("GSSAPI is not supported in protocol version 2")));
 
-       if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
+       if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
        {
-               /*
-                * Set default Kerberos keytab file for the Krb5 mechanism.
-                *
-                * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv()
-                * not always available.
-                */
-               if (getenv("KRB5_KTNAME") == NULL)
-               {
-                       size_t  kt_len = strlen(pg_krb_server_keyfile) + 14;
-                       char   *kt_path = malloc(kt_len);
+               /* Expect 'p' message type */
+               int                     mtype;
 
-                       if (!kt_path)
-                       {
-                               ereport(LOG,
-                                               (errcode(ERRCODE_OUT_OF_MEMORY),
-                                                errmsg("out of memory")));
-                               return STATUS_ERROR;
-                       }
-                       snprintf(kt_path, kt_len, "KRB5_KTNAME=%s", pg_krb_server_keyfile);
-                       putenv(kt_path);
+               mtype = pq_getbyte();
+               if (mtype != 'p')
+               {
+                       /*
+                        * If the client just disconnects without offering a password,
+                        * don't make a log entry.  This is legal per protocol spec and in
+                        * fact commonly done by psql, so complaining just clutters the
+                        * log.
+                        */
+                       if (mtype != EOF)
+                               ereport(COMMERROR,
+                                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                       errmsg("expected password response, got message type %d",
+                                                  mtype)));
+                       return NULL;            /* EOF or bad message type */
                }
        }
+       else
+       {
+               /* For pre-3.0 clients, avoid log entry if they just disconnect */
+               if (pq_peekbyte() == EOF)
+                       return NULL;            /* EOF */
+       }
 
-       /*
-        * We accept any service principal that's present in our keytab. This
-        * increases interoperability between kerberos implementations that see
-        * for example case sensitivity differently, while not really opening up
-        * any vector of attack.
-        */
-       port->gss->cred = GSS_C_NO_CREDENTIAL;
+       initStringInfo(&buf);
+       if (pq_getmessage(&buf, 1000))          /* receive password */
+       {
+               /* EOF - pq_getmessage already logged a suitable message */
+               pfree(buf.data);
+               return NULL;
+       }
 
        /*
-        * Initialize sequence with an empty context
+        * Apply sanity check: password packet length should agree with length of
+        * contained string.  Note it is safe to use strlen here because
+        * StringInfo is guaranteed to have an appended '\0'.
         */
-       port->gss->ctx = GSS_C_NO_CONTEXT;
+       if (strlen(buf.data) + 1 != buf.len)
+               ereport(COMMERROR,
+                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                errmsg("invalid password packet size")));
+
+       /* Do not echo password to logs, for security. */
+       ereport(DEBUG5,
+                       (errmsg("received password packet")));
 
        /*
-        * Loop through GSSAPI message exchange. This exchange can consist of
-        * multiple messags sent in both directions. First message is always from
-        * the client. All messages from client to server are password packets
-        * (type 'p').
+        * Return the received string.  Note we do not attempt to do any
+        * character-set conversion on it; since we don't yet know the client's
+        * encoding, there wouldn't be much point.
         */
-       do
-       {
-               mtype = pq_getbyte();
-               if (mtype != 'p')
-               {
-                       /* Only log error if client didn't disconnect. */
-                       if (mtype != EOF)
-                               ereport(COMMERROR,
-                                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                                errmsg("expected GSS response, got message type %d",
-                                                               mtype)));
-                       return STATUS_ERROR;
-               }
-
-               /* Get the actual GSS token */
-               initStringInfo(&buf);
-               if (pq_getmessage(&buf, 2000))
-               {
-                       /* EOF - pq_getmessage already logged error */
-                       pfree(buf.data);
-                       return STATUS_ERROR;
-               }
+       return buf.data;
+}
 
-               /* Map to GSSAPI style buffer */
-               gbuf.length = buf.len;
-               gbuf.value = buf.data;
 
-               elog(DEBUG4, "Processing received GSS token of length %u",
-                        (unsigned int) gbuf.length);
+/*----------------------------------------------------------------
+ * MD5 and crypt authentication
+ *----------------------------------------------------------------
+ */
 
-               maj_stat = gss_accept_sec_context(
-                                                                                 &min_stat,
-                                                                                 &port->gss->ctx,
-                                                                                 port->gss->cred,
-                                                                                 &gbuf,
-                                                                                 GSS_C_NO_CHANNEL_BINDINGS,
-                                                                                 &port->gss->name,
-                                                                                 NULL,
-                                                                                 &port->gss->outbuf,
-                                                                                 &gflags,
-                                                                                 NULL,
-                                                                                 NULL);
+/*
+ * Called when we have sent an authorization request for a password.
+ * Get the response and check it.
+ */
+static int
+recv_and_check_password_packet(Port *port)
+{
+       char       *passwd;
+       int                     result;
 
-               /* gbuf no longer used */
-               pfree(buf.data);
+       passwd = recv_password_packet(port);
 
-               elog(DEBUG5, "gss_accept_sec_context major: %d, "
-                        "minor: %d, outlen: %u, outflags: %x",
-                        maj_stat, min_stat,
-                        (unsigned int) port->gss->outbuf.length, gflags);
+       if (passwd == NULL)
+               return STATUS_EOF;              /* client wouldn't send password */
 
-               if (port->gss->outbuf.length != 0)
-               {
-                       /*
-                        * Negotiation generated data to be sent to the client.
-                        */
-                       OM_uint32       lmin_s;
+       result = md5_crypt_verify(port, port->user_name, passwd);
 
-                       elog(DEBUG4, "sending GSS response token of length %u",
-                                (unsigned int) port->gss->outbuf.length);
+       pfree(passwd);
 
-                       sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+       return result;
+}
 
-                       gss_release_buffer(&lmin_s, &port->gss->outbuf);
-               }
 
-               if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
-               {
-                       OM_uint32       lmin_s;
+/*----------------------------------------------------------------
+ * MIT Kerberos authentication system - protocol version 5
+ *----------------------------------------------------------------
+ */
+#ifdef KRB5
 
-                       gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
-                       pg_GSS_error(ERROR,
-                                          gettext_noop("accepting GSS security context failed"),
-                                                maj_stat, min_stat);
-               }
+static int
+pg_krb5_init(void)
+{
+       krb5_error_code retval;
+       char       *khostname;
 
-               if (maj_stat == GSS_S_CONTINUE_NEEDED)
-                       elog(DEBUG4, "GSS continue needed");
+       if (pg_krb5_initialised)
+               return STATUS_OK;
 
-       } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+       retval = krb5_init_context(&pg_krb5_context);
+       if (retval)
+       {
+               ereport(LOG,
+                               (errmsg("Kerberos initialization returned error %d",
+                                               retval)));
+               com_err("postgres", retval, "while initializing krb5");
+               return STATUS_ERROR;
+       }
 
-       if (port->gss->cred != GSS_C_NO_CREDENTIAL)
+       retval = krb5_kt_resolve(pg_krb5_context, pg_krb_server_keyfile, &pg_krb5_keytab);
+       if (retval)
        {
-               /*
-                * Release service principal credentials
-                */
-               gss_release_cred(&min_stat, &port->gss->cred);
+               ereport(LOG,
+                               (errmsg("Kerberos keytab resolving returned error %d",
+                                               retval)));
+               com_err("postgres", retval, "while resolving keytab file \"%s\"",
+                               pg_krb_server_keyfile);
+               krb5_free_context(pg_krb5_context);
+               return STATUS_ERROR;
        }
 
        /*
-        * GSS_S_COMPLETE indicates that authentication is now complete.
-        *
-        * Get the name of the user that authenticated, and compare it to the pg
-        * username that was specified for the connection.
+        * If no hostname was specified, pg_krb_server_hostname is already NULL.
+        * If it's set to blank, force it to NULL.
         */
-       maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
-       if (maj_stat != GSS_S_COMPLETE)
-               pg_GSS_error(ERROR,
-                                        gettext_noop("retrieving GSS user name failed"),
-                                        maj_stat, min_stat);
+       khostname = pg_krb_server_hostname;
+       if (khostname && khostname[0] == '\0')
+               khostname = NULL;
 
-       /*
-        * Split the username at the realm separator
-        */
-       if (strchr(gbuf.value, '@'))
+       retval = krb5_sname_to_principal(pg_krb5_context,
+                                                                        khostname,
+                                                                        pg_krb_srvnam,
+                                                                        KRB5_NT_SRV_HST,
+                                                                        &pg_krb5_server);
+       if (retval)
        {
-               char       *cp = strchr(gbuf.value, '@');
+               ereport(LOG,
+                               (errmsg("Kerberos sname_to_principal(\"%s\", \"%s\") returned error %d",
+                khostname ? khostname : "server hostname", pg_krb_srvnam, retval)));
+               com_err("postgres", retval,
+               "while getting server principal for server \"%s\" for service \"%s\"",
+                               khostname ? khostname : "server hostname", pg_krb_srvnam);
+               krb5_kt_close(pg_krb5_context, pg_krb5_keytab);
+               krb5_free_context(pg_krb5_context);
+               return STATUS_ERROR;
+       }
 
-               *cp = '\0';
-               cp++;
+       pg_krb5_initialised = 1;
+       return STATUS_OK;
+}
+
+
+/*
+ * pg_krb5_recvauth -- server routine to receive authentication information
+ *                                        from the client
+ *
+ * We still need to compare the username obtained from the client's setup
+ * packet to the authenticated name.
+ *
+ * We have our own keytab file because postgres is unlikely to run as root,
+ * and so cannot read the default keytab.
+ */
+static int
+pg_krb5_recvauth(Port *port)
+{
+       krb5_error_code retval;
+       int                     ret;
+       krb5_auth_context auth_context = NULL;
+       krb5_ticket *ticket;
+       char       *kusername;
+       char       *cp;
+
+       if (get_role_line(port->user_name) == NULL)
+               return STATUS_ERROR;
+
+       ret = pg_krb5_init();
+       if (ret != STATUS_OK)
+               return ret;
+
+       retval = krb5_recvauth(pg_krb5_context, &auth_context,
+                                                  (krb5_pointer) & port->sock, pg_krb_srvnam,
+                                                  pg_krb5_server, 0, pg_krb5_keytab, &ticket);
+       if (retval)
+       {
+               ereport(LOG,
+                               (errmsg("Kerberos recvauth returned error %d",
+                                               retval)));
+               com_err("postgres", retval, "from krb5_recvauth");
+               return STATUS_ERROR;
+       }
+
+       /*
+        * The "client" structure comes out of the ticket and is therefore
+        * authenticated.  Use it to check the username obtained from the
+        * postmaster startup packet.
+        */
+#if defined(HAVE_KRB5_TICKET_ENC_PART2)
+       retval = krb5_unparse_name(pg_krb5_context,
+                                                          ticket->enc_part2->client, &kusername);
+#elif defined(HAVE_KRB5_TICKET_CLIENT)
+       retval = krb5_unparse_name(pg_krb5_context,
+                                                          ticket->client, &kusername);
+#else
+#error "bogus configuration"
+#endif
+       if (retval)
+       {
+               ereport(LOG,
+                               (errmsg("Kerberos unparse_name returned error %d",
+                                               retval)));
+               com_err("postgres", retval, "while unparsing client name");
+               krb5_free_ticket(pg_krb5_context, ticket);
+               krb5_auth_con_free(pg_krb5_context, auth_context);
+               return STATUS_ERROR;
+       }
+
+       cp = strchr(kusername, '@');
+       if (cp)
+       {
+               *cp = '\0';
+               cp++;
 
                if (pg_krb_realm != NULL && strlen(pg_krb_realm))
                {
-                       /*
-                        * Match the realm part of the name first
-                        */
+                       /* Match realm against configured */
                        if (pg_krb_caseins_users)
                                ret = pg_strcasecmp(pg_krb_realm, cp);
                        else
@@ -577,11 +693,12 @@ pg_GSS_recvauth(Port *port)
 
                        if (ret)
                        {
-                               /* GSS realm does not match */
                                elog(DEBUG2,
-                                  "GSSAPI realm (%s) and configured realm (%s) don't match",
+                                        "krb5 realm (%s) and configured realm (%s) don't match",
                                         cp, pg_krb_realm);
-                               gss_release_buffer(&lmin_s, &gbuf);
+
+                               krb5_free_ticket(pg_krb5_context, ticket);
+                               krb5_auth_con_free(pg_krb5_context, auth_context);
                                return STATUS_ERROR;
                        }
                }
@@ -589,116 +706,419 @@ pg_GSS_recvauth(Port *port)
        else if (pg_krb_realm && strlen(pg_krb_realm))
        {
                elog(DEBUG2,
-                        "GSSAPI did not return realm but realm matching was requested");
+                        "krb5 did not return realm but realm matching was requested");
 
-               gss_release_buffer(&lmin_s, &gbuf);
+               krb5_free_ticket(pg_krb5_context, ticket);
+               krb5_auth_con_free(pg_krb5_context, auth_context);
                return STATUS_ERROR;
        }
 
        if (pg_krb_caseins_users)
-               ret = pg_strcasecmp(port->user_name, gbuf.value);
+               ret = pg_strncasecmp(port->user_name, kusername, SM_DATABASE_USER);
        else
-               ret = strcmp(port->user_name, gbuf.value);
-
+               ret = strncmp(port->user_name, kusername, SM_DATABASE_USER);
        if (ret)
        {
-               /* GSS name and PGUSER are not equivalent */
-               elog(DEBUG2,
-                        "provided username (%s) and GSSAPI username (%s) don't match",
-                        port->user_name, (char *) gbuf.value);
-
-               gss_release_buffer(&lmin_s, &gbuf);
-               return STATUS_ERROR;
+               ereport(LOG,
+                               (errmsg("unexpected Kerberos user name received from client (received \"%s\", expected \"%s\")",
+                                               port->user_name, kusername)));
+               ret = STATUS_ERROR;
        }
+       else
+               ret = STATUS_OK;
 
-       gss_release_buffer(&lmin_s, &gbuf);
+       krb5_free_ticket(pg_krb5_context, ticket);
+       krb5_auth_con_free(pg_krb5_context, auth_context);
+       free(kusername);
 
-       return STATUS_OK;
+       return ret;
 }
-
-#else                                                  /* no ENABLE_GSS */
+#else
 
 static int
-pg_GSS_recvauth(Port *port)
+pg_krb5_recvauth(Port *port)
 {
        ereport(LOG,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                        errmsg("GSSAPI not implemented on this server")));
+                        errmsg("Kerberos 5 not implemented on this server")));
        return STATUS_ERROR;
 }
+#endif   /* KRB5 */
 
-#endif   /* ENABLE_GSS */
 
 /*----------------------------------------------------------------
- * SSPI authentication system
+ * GSSAPI authentication system
  *----------------------------------------------------------------
  */
+#ifdef ENABLE_GSS
 
-#ifdef ENABLE_SSPI
+#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
+{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
+static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
+#endif
 
-typedef                SECURITY_STATUS
-                       (WINAPI * QUERY_SECURITY_CONTEXT_TOKEN_FN) (
-                                                                                                          PCtxtHandle, void **);
 
 static void
-pg_SSPI_error(int severity, char *errmsg, SECURITY_STATUS r)
+pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
 {
-       char            sysmsg[256];
+       gss_buffer_desc gmsg;
+       OM_uint32       lmaj_s,
+                               lmin_s,
+                               msg_ctx;
+       char            msg_major[128],
+                               msg_minor[128];
 
-       if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, r, 0, sysmsg, sizeof(sysmsg), NULL) == 0)
-               ereport(severity,
-                               (errmsg_internal("%s", errmsg),
-                                errdetail("SSPI error %x", (unsigned int) r)));
-       else
-               ereport(severity,
-                               (errmsg_internal("%s", errmsg),
-                                errdetail("%s (%x)", sysmsg, (unsigned int) r)));
+       /* Fetch major status message */
+       msg_ctx = 0;
+       lmaj_s = gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
+                                                               GSS_C_NO_OID, &msg_ctx, &gmsg);
+       strlcpy(msg_major, gmsg.value, sizeof(msg_major));
+       gss_release_buffer(&lmin_s, &gmsg);
+
+       if (msg_ctx)
+
+               /*
+                * More than one message available. XXX: Should we loop and read all
+                * messages? (same below)
+                */
+               ereport(WARNING,
+                               (errmsg_internal("incomplete GSS error report")));
+
+       /* Fetch mechanism minor status message */
+       msg_ctx = 0;
+       lmaj_s = gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
+                                                               GSS_C_NO_OID, &msg_ctx, &gmsg);
+       strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
+       gss_release_buffer(&lmin_s, &gmsg);
+
+       if (msg_ctx)
+               ereport(WARNING,
+                               (errmsg_internal("incomplete GSS minor error report")));
+
+       /*
+        * errmsg_internal, since translation of the first part must be done
+        * before calling this function anyway.
+        */
+       ereport(severity,
+                       (errmsg_internal("%s", errmsg),
+                        errdetail("%s: %s", msg_major, msg_minor)));
 }
 
 static int
-pg_SSPI_recvauth(Port *port)
+pg_GSS_recvauth(Port *port)
 {
+       OM_uint32       maj_stat,
+                               min_stat,
+                               lmin_s,
+                               gflags;
        int                     mtype;
+       int                     ret;
        StringInfoData buf;
-       SECURITY_STATUS r;
-       CredHandle      sspicred;
-       CtxtHandle *sspictx = NULL,
-                               newctx;
-       TimeStamp       expiry;
-       ULONG           contextattr;
-       SecBufferDesc inbuf;
-       SecBufferDesc outbuf;
-       SecBuffer       OutBuffers[1];
-       SecBuffer       InBuffers[1];
-       HANDLE          token;
-       TOKEN_USER *tokenuser;
-       DWORD           retlen;
-       char            accountname[MAXPGPATH];
-       char            domainname[MAXPGPATH];
-       DWORD           accountnamesize = sizeof(accountname);
-       DWORD           domainnamesize = sizeof(domainname);
-       SID_NAME_USE accountnameuse;
-       HMODULE         secur32;
-       QUERY_SECURITY_CONTEXT_TOKEN_FN _QuerySecurityContextToken;
+       gss_buffer_desc gbuf;
 
        /*
-        * SSPI auth is not supported for protocol versions before 3, because it
-        * relies on the overall message length word to determine the SSPI payload
+        * GSS auth is not supported for protocol versions before 3, because it
+        * relies on the overall message length word to determine the GSS payload
         * size in AuthenticationGSSContinue and PasswordMessage messages.
-        * (This is, in fact, a design error in our SSPI support, because protocol
+        * (This is, in fact, a design error in our GSS support, because protocol
         * messages are supposed to be parsable without relying on the length
         * word; but it's not worth changing it now.)
         */
        if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
                ereport(FATAL,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("SSPI is not supported in protocol version 2")));
+                                errmsg("GSSAPI is not supported in protocol version 2")));
+
+       if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
+       {
+               /*
+                * Set default Kerberos keytab file for the Krb5 mechanism.
+                *
+                * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv()
+                * not always available.
+                */
+               if (getenv("KRB5_KTNAME") == NULL)
+               {
+                       size_t  kt_len = strlen(pg_krb_server_keyfile) + 14;
+                       char   *kt_path = malloc(kt_len);
+
+                       if (!kt_path)
+                       {
+                               ereport(LOG,
+                                               (errcode(ERRCODE_OUT_OF_MEMORY),
+                                                errmsg("out of memory")));
+                               return STATUS_ERROR;
+                       }
+                       snprintf(kt_path, kt_len, "KRB5_KTNAME=%s", pg_krb_server_keyfile);
+                       putenv(kt_path);
+               }
+       }
 
        /*
-        * Acquire a handle to the server credentials.
+        * We accept any service principal that's present in our keytab. This
+        * increases interoperability between kerberos implementations that see
+        * for example case sensitivity differently, while not really opening up
+        * any vector of attack.
         */
-       r = AcquireCredentialsHandle(NULL,
-                                                                "negotiate",
+       port->gss->cred = GSS_C_NO_CREDENTIAL;
+
+       /*
+        * Initialize sequence with an empty context
+        */
+       port->gss->ctx = GSS_C_NO_CONTEXT;
+
+       /*
+        * Loop through GSSAPI message exchange. This exchange can consist of
+        * multiple messags sent in both directions. First message is always from
+        * the client. All messages from client to server are password packets
+        * (type 'p').
+        */
+       do
+       {
+               mtype = pq_getbyte();
+               if (mtype != 'p')
+               {
+                       /* Only log error if client didn't disconnect. */
+                       if (mtype != EOF)
+                               ereport(COMMERROR,
+                                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                                errmsg("expected GSS response, got message type %d",
+                                                               mtype)));
+                       return STATUS_ERROR;
+               }
+
+               /* Get the actual GSS token */
+               initStringInfo(&buf);
+               if (pq_getmessage(&buf, 2000))
+               {
+                       /* EOF - pq_getmessage already logged error */
+                       pfree(buf.data);
+                       return STATUS_ERROR;
+               }
+
+               /* Map to GSSAPI style buffer */
+               gbuf.length = buf.len;
+               gbuf.value = buf.data;
+
+               elog(DEBUG4, "Processing received GSS token of length %u",
+                        (unsigned int) gbuf.length);
+
+               maj_stat = gss_accept_sec_context(
+                                                                                 &min_stat,
+                                                                                 &port->gss->ctx,
+                                                                                 port->gss->cred,
+                                                                                 &gbuf,
+                                                                                 GSS_C_NO_CHANNEL_BINDINGS,
+                                                                                 &port->gss->name,
+                                                                                 NULL,
+                                                                                 &port->gss->outbuf,
+                                                                                 &gflags,
+                                                                                 NULL,
+                                                                                 NULL);
+
+               /* gbuf no longer used */
+               pfree(buf.data);
+
+               elog(DEBUG5, "gss_accept_sec_context major: %d, "
+                        "minor: %d, outlen: %u, outflags: %x",
+                        maj_stat, min_stat,
+                        (unsigned int) port->gss->outbuf.length, gflags);
+
+               if (port->gss->outbuf.length != 0)
+               {
+                       /*
+                        * Negotiation generated data to be sent to the client.
+                        */
+                       OM_uint32       lmin_s;
+
+                       elog(DEBUG4, "sending GSS response token of length %u",
+                                (unsigned int) port->gss->outbuf.length);
+
+                       sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+
+                       gss_release_buffer(&lmin_s, &port->gss->outbuf);
+               }
+
+               if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+               {
+                       OM_uint32       lmin_s;
+
+                       gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
+                       pg_GSS_error(ERROR,
+                                          gettext_noop("accepting GSS security context failed"),
+                                                maj_stat, min_stat);
+               }
+
+               if (maj_stat == GSS_S_CONTINUE_NEEDED)
+                       elog(DEBUG4, "GSS continue needed");
+
+       } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+       if (port->gss->cred != GSS_C_NO_CREDENTIAL)
+       {
+               /*
+                * Release service principal credentials
+                */
+               gss_release_cred(&min_stat, &port->gss->cred);
+       }
+
+       /*
+        * GSS_S_COMPLETE indicates that authentication is now complete.
+        *
+        * Get the name of the user that authenticated, and compare it to the pg
+        * username that was specified for the connection.
+        */
+       maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
+       if (maj_stat != GSS_S_COMPLETE)
+               pg_GSS_error(ERROR,
+                                        gettext_noop("retrieving GSS user name failed"),
+                                        maj_stat, min_stat);
+
+       /*
+        * Split the username at the realm separator
+        */
+       if (strchr(gbuf.value, '@'))
+       {
+               char       *cp = strchr(gbuf.value, '@');
+
+               *cp = '\0';
+               cp++;
+
+               if (pg_krb_realm != NULL && strlen(pg_krb_realm))
+               {
+                       /*
+                        * Match the realm part of the name first
+                        */
+                       if (pg_krb_caseins_users)
+                               ret = pg_strcasecmp(pg_krb_realm, cp);
+                       else
+                               ret = strcmp(pg_krb_realm, cp);
+
+                       if (ret)
+                       {
+                               /* GSS realm does not match */
+                               elog(DEBUG2,
+                                  "GSSAPI realm (%s) and configured realm (%s) don't match",
+                                        cp, pg_krb_realm);
+                               gss_release_buffer(&lmin_s, &gbuf);
+                               return STATUS_ERROR;
+                       }
+               }
+       }
+       else if (pg_krb_realm && strlen(pg_krb_realm))
+       {
+               elog(DEBUG2,
+                        "GSSAPI did not return realm but realm matching was requested");
+
+               gss_release_buffer(&lmin_s, &gbuf);
+               return STATUS_ERROR;
+       }
+
+       if (pg_krb_caseins_users)
+               ret = pg_strcasecmp(port->user_name, gbuf.value);
+       else
+               ret = strcmp(port->user_name, gbuf.value);
+
+       if (ret)
+       {
+               /* GSS name and PGUSER are not equivalent */
+               elog(DEBUG2,
+                        "provided username (%s) and GSSAPI username (%s) don't match",
+                        port->user_name, (char *) gbuf.value);
+
+               gss_release_buffer(&lmin_s, &gbuf);
+               return STATUS_ERROR;
+       }
+
+       gss_release_buffer(&lmin_s, &gbuf);
+
+       return STATUS_OK;
+}
+
+#else                                                  /* no ENABLE_GSS */
+
+static int
+pg_GSS_recvauth(Port *port)
+{
+       ereport(LOG,
+                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                        errmsg("GSSAPI not implemented on this server")));
+       return STATUS_ERROR;
+}
+
+#endif   /* ENABLE_GSS */
+
+
+/*----------------------------------------------------------------
+ * SSPI authentication system
+ *----------------------------------------------------------------
+ */
+#ifdef ENABLE_SSPI
+static void
+pg_SSPI_error(int severity, char *errmsg, SECURITY_STATUS r)
+{
+       char            sysmsg[256];
+
+       if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, r, 0, sysmsg, sizeof(sysmsg), NULL) == 0)
+               ereport(severity,
+                               (errmsg_internal("%s", errmsg),
+                                errdetail("SSPI error %x", (unsigned int) r)));
+       else
+               ereport(severity,
+                               (errmsg_internal("%s", errmsg),
+                                errdetail("%s (%x)", sysmsg, (unsigned int) r)));
+}
+
+static int
+pg_SSPI_recvauth(Port *port)
+{
+       int                     mtype;
+       StringInfoData buf;
+       SECURITY_STATUS r;
+       CredHandle      sspicred;
+       CtxtHandle *sspictx = NULL,
+                               newctx;
+       TimeStamp       expiry;
+       ULONG           contextattr;
+       SecBufferDesc inbuf;
+       SecBufferDesc outbuf;
+       SecBuffer       OutBuffers[1];
+       SecBuffer       InBuffers[1];
+       HANDLE          token;
+       TOKEN_USER *tokenuser;
+       DWORD           retlen;
+       char            accountname[MAXPGPATH];
+       char            domainname[MAXPGPATH];
+       DWORD           accountnamesize = sizeof(accountname);
+       DWORD           domainnamesize = sizeof(domainname);
+       SID_NAME_USE accountnameuse;
+       HMODULE         secur32;
+       QUERY_SECURITY_CONTEXT_TOKEN_FN _QuerySecurityContextToken;
+
+       /*
+        * SSPI auth is not supported for protocol versions before 3, because it
+        * relies on the overall message length word to determine the SSPI payload
+        * size in AuthenticationGSSContinue and PasswordMessage messages.
+        * (This is, in fact, a design error in our SSPI support, because protocol
+        * messages are supposed to be parsable without relying on the length
+        * word; but it's not worth changing it now.)
+        */
+       if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+               ereport(FATAL,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("SSPI is not supported in protocol version 2")));
+
+       /*
+        * Acquire a handle to the server credentials.
+        */
+       r = AcquireCredentialsHandle(NULL,
+                                                                "negotiate",
                                                                 SECPKG_CRED_INBOUND,
                                                                 NULL,
                                                                 NULL,
@@ -816,391 +1236,126 @@ pg_SSPI_recvauth(Port *port)
        } while (r == SEC_I_CONTINUE_NEEDED);
 
 
-       /*
-        * Release service principal credentials
-        */
-       FreeCredentialsHandle(&sspicred);
-
-
-       /*
-        * SEC_E_OK indicates that authentication is now complete.
-        *
-        * Get the name of the user that authenticated, and compare it to the pg
-        * username that was specified for the connection.
-        *
-        * MingW is missing the export for QuerySecurityContextToken in the
-        * secur32 library, so we have to load it dynamically.
-        */
-
-       secur32 = LoadLibrary("SECUR32.DLL");
-       if (secur32 == NULL)
-               ereport(ERROR,
-                               (errmsg_internal("could not load secur32.dll: %d",
-                                                                (int) GetLastError())));
-
-       _QuerySecurityContextToken = (QUERY_SECURITY_CONTEXT_TOKEN_FN)
-               GetProcAddress(secur32, "QuerySecurityContextToken");
-       if (_QuerySecurityContextToken == NULL)
-       {
-               FreeLibrary(secur32);
-               ereport(ERROR,
-                               (errmsg_internal("could not locate QuerySecurityContextToken in secur32.dll: %d",
-                                                                (int) GetLastError())));
-       }
-
-       r = (_QuerySecurityContextToken) (sspictx, &token);
-       if (r != SEC_E_OK)
-       {
-               FreeLibrary(secur32);
-               pg_SSPI_error(ERROR,
-                          gettext_noop("could not get security token from context"), r);
-       }
-
-       FreeLibrary(secur32);
-
-       /*
-        * No longer need the security context, everything from here on uses the
-        * token instead.
-        */
-       DeleteSecurityContext(sspictx);
-       free(sspictx);
-
-       if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
-               ereport(ERROR,
-                        (errmsg_internal("could not get token user size: error code %d",
-                                                         (int) GetLastError())));
-
-       tokenuser = malloc(retlen);
-       if (tokenuser == NULL)
-               ereport(ERROR,
-                               (errmsg("out of memory")));
-
-       if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
-               ereport(ERROR,
-                               (errmsg_internal("could not get user token: error code %d",
-                                                                (int) GetLastError())));
-
-       if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
-                                                 domainname, &domainnamesize, &accountnameuse))
-               ereport(ERROR,
-                         (errmsg_internal("could not lookup acconut sid: error code %d",
-                                                          (int) GetLastError())));
-
-       free(tokenuser);
-
-       /*
-        * Compare realm/domain if requested. In SSPI, always compare case
-        * insensitive.
-        */
-       if (pg_krb_realm && strlen(pg_krb_realm))
-       {
-               if (pg_strcasecmp(pg_krb_realm, domainname))
-               {
-                       elog(DEBUG2,
-                                "SSPI domain (%s) and configured domain (%s) don't match",
-                                domainname, pg_krb_realm);
-
-                       return STATUS_ERROR;
-               }
-       }
-
-       /*
-        * We have the username (without domain/realm) in accountname, compare to
-        * the supplied value. In SSPI, always compare case insensitive.
-        */
-       if (pg_strcasecmp(port->user_name, accountname))
-       {
-               /* GSS name and PGUSER are not equivalent */
-               elog(DEBUG2,
-                        "provided username (%s) and SSPI username (%s) don't match",
-                        port->user_name, accountname);
-
-               return STATUS_ERROR;
-       }
-
-       return STATUS_OK;
-}
-
-#else                                                  /* no ENABLE_SSPI */
-
-static int
-pg_SSPI_recvauth(Port *port)
-{
-       ereport(LOG,
-                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                        errmsg("SSPI not implemented on this server")));
-       return STATUS_ERROR;
-}
-
-#endif   /* ENABLE_SSPI */
-
-
-/*
- * Tell the user the authentication failed, but not (much about) why.
- *
- * There is a tradeoff here between security concerns and making life
- * unnecessarily difficult for legitimate users.  We would not, for example,
- * want to report the password we were expecting to receive...
- * But it seems useful to report the username and authorization method
- * in use, and these are items that must be presumed known to an attacker
- * anyway.
- * Note that many sorts of failure report additional information in the
- * postmaster log, which we hope is only readable by good guys.
- */
-static void
-auth_failed(Port *port, int status)
-{
-       const char *errstr;
-
-       /*
-        * If we failed due to EOF from client, just quit; there's no point in
-        * trying to send a message to the client, and not much point in logging
-        * the failure in the postmaster log.  (Logging the failure might be
-        * desirable, were it not for the fact that libpq closes the connection
-        * unceremoniously if challenged for a password when it hasn't got one to
-        * send.  We'll get a useless log entry for every psql connection under
-        * password auth, even if it's perfectly successful, if we log STATUS_EOF
-        * events.)
-        */
-       if (status == STATUS_EOF)
-               proc_exit(0);
-
-       switch (port->auth_method)
-       {
-               case uaReject:
-                       errstr = gettext_noop("authentication failed for user \"%s\": host rejected");
-                       break;
-               case uaKrb5:
-                       errstr = gettext_noop("Kerberos 5 authentication failed for user \"%s\"");
-                       break;
-               case uaGSS:
-                       errstr = gettext_noop("GSSAPI authentication failed for user \"%s\"");
-                       break;
-               case uaSSPI:
-                       errstr = gettext_noop("SSPI authentication failed for user \"%s\"");
-                       break;
-               case uaTrust:
-                       errstr = gettext_noop("\"trust\" authentication failed for user \"%s\"");
-                       break;
-               case uaIdent:
-                       errstr = gettext_noop("Ident authentication failed for user \"%s\"");
-                       break;
-               case uaMD5:
-               case uaCrypt:
-               case uaPassword:
-                       errstr = gettext_noop("password authentication failed for user \"%s\"");
-                       break;
-#ifdef USE_PAM
-               case uaPAM:
-                       errstr = gettext_noop("PAM authentication failed for user \"%s\"");
-                       break;
-#endif   /* USE_PAM */
-#ifdef USE_LDAP
-               case uaLDAP:
-                       errstr = gettext_noop("LDAP authentication failed for user \"%s\"");
-                       break;
-#endif   /* USE_LDAP */
-               default:
-                       errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
-                       break;
-       }
-
-       ereport(FATAL,
-                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
-                        errmsg(errstr, port->user_name)));
-       /* doesn't return */
-}
-
-
-/*
- * Client authentication starts here.  If there is an error, this
- * function does not return and the backend process is terminated.
- */
-void
-ClientAuthentication(Port *port)
-{
-       int                     status = STATUS_ERROR;
-
-       /*
-        * Get the authentication method to use for this frontend/database
-        * combination.  Note: a failure return indicates a problem with the hba
-        * config file, not with the request.  hba.c should have dropped an error
-        * message into the postmaster logfile if it failed.
-        */
-       if (hba_getauthmethod(port) != STATUS_OK)
-               ereport(FATAL,
-                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                                errmsg("missing or erroneous pg_hba.conf file"),
-                                errhint("See server log for details.")));
-
-       switch (port->auth_method)
-       {
-               case uaReject:
-
-                       /*
-                        * This could have come from an explicit "reject" entry in
-                        * pg_hba.conf, but more likely it means there was no matching
-                        * entry.  Take pity on the poor user and issue a helpful error
-                        * message.  NOTE: this is not a security breach, because all the
-                        * info reported here is known at the frontend and must be assumed
-                        * known to bad guys. We're merely helping out the less clueful
-                        * good guys.
-                        */
-                       {
-                               char            hostinfo[NI_MAXHOST];
-
-                               pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen,
-                                                                  hostinfo, sizeof(hostinfo),
-                                                                  NULL, 0,
-                                                                  NI_NUMERICHOST);
-
-#ifdef USE_SSL
-                               ereport(FATAL,
-                                               (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
-                                                errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
-                                                         hostinfo, port->user_name, port->database_name,
-                                                               port->ssl ? _("SSL on") : _("SSL off"))));
-#else
-                               ereport(FATAL,
-                                               (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
-                                                errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"",
-                                                  hostinfo, port->user_name, port->database_name)));
-#endif
-                               break;
-                       }
-
-               case uaKrb5:
-                       sendAuthRequest(port, AUTH_REQ_KRB5);
-                       status = pg_krb5_recvauth(port);
-                       break;
-
-               case uaGSS:
-                       sendAuthRequest(port, AUTH_REQ_GSS);
-                       status = pg_GSS_recvauth(port);
-                       break;
-
-               case uaSSPI:
-                       sendAuthRequest(port, AUTH_REQ_SSPI);
-                       status = pg_SSPI_recvauth(port);
-                       break;
-
-               case uaIdent:
-
-                       /*
-                        * If we are doing ident on unix-domain sockets, use SCM_CREDS
-                        * only if it is defined and SO_PEERCRED isn't.
-                        */
-#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && \
-       (defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
-        (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS)))
-                       if (port->raddr.addr.ss_family == AF_UNIX)
-                       {
-#if defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
-
-                               /*
-                                * Receive credentials on next message receipt, BSD/OS,
-                                * NetBSD. We need to set this before the client sends the
-                                * next packet.
-                                */
-                               int                     on = 1;
-
-                               if (setsockopt(port->sock, 0, LOCAL_CREDS, &on, sizeof(on)) < 0)
-                                       ereport(FATAL,
-                                                       (errcode_for_socket_access(),
-                                          errmsg("could not enable credential reception: %m")));
-#endif
-
-                               sendAuthRequest(port, AUTH_REQ_SCM_CREDS);
-                       }
-#endif
-                       status = authident(port);
-                       break;
-
-               case uaMD5:
-                       sendAuthRequest(port, AUTH_REQ_MD5);
-                       status = recv_and_check_password_packet(port);
-                       break;
+       /*
+        * Release service principal credentials
+        */
+       FreeCredentialsHandle(&sspicred);
 
-               case uaCrypt:
-                       sendAuthRequest(port, AUTH_REQ_CRYPT);
-                       status = recv_and_check_password_packet(port);
-                       break;
 
-               case uaPassword:
-                       sendAuthRequest(port, AUTH_REQ_PASSWORD);
-                       status = recv_and_check_password_packet(port);
-                       break;
+       /*
+        * SEC_E_OK indicates that authentication is now complete.
+        *
+        * Get the name of the user that authenticated, and compare it to the pg
+        * username that was specified for the connection.
+        *
+        * MingW is missing the export for QuerySecurityContextToken in the
+        * secur32 library, so we have to load it dynamically.
+        */
 
-#ifdef USE_PAM
-               case uaPAM:
-                       pam_port_cludge = port;
-                       status = CheckPAMAuth(port, port->user_name, "");
-                       break;
-#endif   /* USE_PAM */
+       secur32 = LoadLibrary("SECUR32.DLL");
+       if (secur32 == NULL)
+               ereport(ERROR,
+                               (errmsg_internal("could not load secur32.dll: %d",
+                                                                (int) GetLastError())));
 
-#ifdef USE_LDAP
-               case uaLDAP:
-                       status = CheckLDAPAuth(port);
-                       break;
-#endif
+       _QuerySecurityContextToken = (QUERY_SECURITY_CONTEXT_TOKEN_FN)
+               GetProcAddress(secur32, "QuerySecurityContextToken");
+       if (_QuerySecurityContextToken == NULL)
+       {
+               FreeLibrary(secur32);
+               ereport(ERROR,
+                               (errmsg_internal("could not locate QuerySecurityContextToken in secur32.dll: %d",
+                                                                (int) GetLastError())));
+       }
 
-               case uaTrust:
-                       status = STATUS_OK;
-                       break;
+       r = (_QuerySecurityContextToken) (sspictx, &token);
+       if (r != SEC_E_OK)
+       {
+               FreeLibrary(secur32);
+               pg_SSPI_error(ERROR,
+                          gettext_noop("could not get security token from context"), r);
        }
 
-       if (status == STATUS_OK)
-               sendAuthRequest(port, AUTH_REQ_OK);
-       else
-               auth_failed(port, status);
-}
+       FreeLibrary(secur32);
 
+       /*
+        * No longer need the security context, everything from here on uses the
+        * token instead.
+        */
+       DeleteSecurityContext(sspictx);
+       free(sspictx);
 
-/*
- * Send an authentication request packet to the frontend.
- */
-static void
-sendAuthRequest(Port *port, AuthRequest areq)
-{
-       StringInfoData buf;
+       if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
+               ereport(ERROR,
+                        (errmsg_internal("could not get token user size: error code %d",
+                                                         (int) GetLastError())));
 
-       pq_beginmessage(&buf, 'R');
-       pq_sendint(&buf, (int32) areq, sizeof(int32));
+       tokenuser = malloc(retlen);
+       if (tokenuser == NULL)
+               ereport(ERROR,
+                               (errmsg("out of memory")));
 
-       /* Add the salt for encrypted passwords. */
-       if (areq == AUTH_REQ_MD5)
-               pq_sendbytes(&buf, port->md5Salt, 4);
-       else if (areq == AUTH_REQ_CRYPT)
-               pq_sendbytes(&buf, port->cryptSalt, 2);
+       if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
+               ereport(ERROR,
+                               (errmsg_internal("could not get user token: error code %d",
+                                                                (int) GetLastError())));
 
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
+       if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
+                                                 domainname, &domainnamesize, &accountnameuse))
+               ereport(ERROR,
+                         (errmsg_internal("could not lookup acconut sid: error code %d",
+                                                          (int) GetLastError())));
+
+       free(tokenuser);
 
        /*
-        * Add the authentication data for the next step of the GSSAPI or SSPI
-        * negotiation.
+        * Compare realm/domain if requested. In SSPI, always compare case
+        * insensitive.
         */
-       else if (areq == AUTH_REQ_GSS_CONT)
+       if (pg_krb_realm && strlen(pg_krb_realm))
        {
-               if (port->gss->outbuf.length > 0)
+               if (pg_strcasecmp(pg_krb_realm, domainname))
                {
-                       elog(DEBUG4, "sending GSS token of length %u",
-                                (unsigned int) port->gss->outbuf.length);
+                       elog(DEBUG2,
+                                "SSPI domain (%s) and configured domain (%s) don't match",
+                                domainname, pg_krb_realm);
 
-                       pq_sendbytes(&buf, port->gss->outbuf.value, port->gss->outbuf.length);
+                       return STATUS_ERROR;
                }
        }
-#endif
-
-       pq_endmessage(&buf);
 
        /*
-        * Flush message so client will see it, except for AUTH_REQ_OK, which need
-        * not be sent until we are ready for queries.
+        * We have the username (without domain/realm) in accountname, compare to
+        * the supplied value. In SSPI, always compare case insensitive.
         */
-       if (areq != AUTH_REQ_OK)
-               pq_flush();
+       if (pg_strcasecmp(port->user_name, accountname))
+       {
+               /* GSS name and PGUSER are not equivalent */
+               elog(DEBUG2,
+                        "provided username (%s) and SSPI username (%s) don't match",
+                        port->user_name, accountname);
+
+               return STATUS_ERROR;
+       }
+
+       return STATUS_OK;
+}
+
+#else                                                  /* no ENABLE_SSPI */
+
+static int
+pg_SSPI_recvauth(Port *port)
+{
+       ereport(LOG,
+                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                        errmsg("SSPI not implemented on this server")));
+       return STATUS_ERROR;
 }
 
+#endif   /* ENABLE_SSPI */
+
+
+
 /*----------------------------------------------------------------
  * Ident authentication system
  *----------------------------------------------------------------
@@ -1655,7 +1810,6 @@ authident(hbaPort *port)
  * PAM authentication system
  *----------------------------------------------------------------
  */
-
 #ifdef USE_PAM
 
 /*
@@ -1835,6 +1989,11 @@ CheckPAMAuth(Port *port, char *user, char *password)
 #endif   /* USE_PAM */
 
 
+
+/*----------------------------------------------------------------
+ * LDAP authentication system
+ *----------------------------------------------------------------
+ */
 #ifdef USE_LDAP
 
 static int
@@ -2014,94 +2173,3 @@ CheckLDAPAuth(Port *port)
 }
 #endif   /* USE_LDAP */
 
-/*
- * Collect password response packet from frontend.
- *
- * Returns NULL if couldn't get password, else palloc'd string.
- */
-static char *
-recv_password_packet(Port *port)
-{
-       StringInfoData buf;
-
-       if (PG_PROTOCOL_MAJOR(port->proto) >= 3)
-       {
-               /* Expect 'p' message type */
-               int                     mtype;
-
-               mtype = pq_getbyte();
-               if (mtype != 'p')
-               {
-                       /*
-                        * If the client just disconnects without offering a password,
-                        * don't make a log entry.  This is legal per protocol spec and in
-                        * fact commonly done by psql, so complaining just clutters the
-                        * log.
-                        */
-                       if (mtype != EOF)
-                               ereport(COMMERROR,
-                                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                       errmsg("expected password response, got message type %d",
-                                                  mtype)));
-                       return NULL;            /* EOF or bad message type */
-               }
-       }
-       else
-       {
-               /* For pre-3.0 clients, avoid log entry if they just disconnect */
-               if (pq_peekbyte() == EOF)
-                       return NULL;            /* EOF */
-       }
-
-       initStringInfo(&buf);
-       if (pq_getmessage(&buf, 1000))          /* receive password */
-       {
-               /* EOF - pq_getmessage already logged a suitable message */
-               pfree(buf.data);
-               return NULL;
-       }
-
-       /*
-        * Apply sanity check: password packet length should agree with length of
-        * contained string.  Note it is safe to use strlen here because
-        * StringInfo is guaranteed to have an appended '\0'.
-        */
-       if (strlen(buf.data) + 1 != buf.len)
-               ereport(COMMERROR,
-                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                errmsg("invalid password packet size")));
-
-       /* Do not echo password to logs, for security. */
-       ereport(DEBUG5,
-                       (errmsg("received password packet")));
-
-       /*
-        * Return the received string.  Note we do not attempt to do any
-        * character-set conversion on it; since we don't yet know the client's
-        * encoding, there wouldn't be much point.
-        */
-       return buf.data;
-}
-
-
-/*
- * Called when we have sent an authorization request for a password.
- * Get the response and check it.
- */
-static int
-recv_and_check_password_packet(Port *port)
-{
-       char       *passwd;
-       int                     result;
-
-       passwd = recv_password_packet(port);
-
-       if (passwd == NULL)
-               return STATUS_EOF;              /* client wouldn't send password */
-
-       result = md5_crypt_verify(port, port->user_name, passwd);
-
-       pfree(passwd);
-
-       return result;
-}