Fix assorted issues in backend's GSSAPI encryption support.
authorTom Lane
Mon, 28 Dec 2020 22:44:17 +0000 (17:44 -0500)
committerTom Lane
Mon, 28 Dec 2020 22:44:17 +0000 (17:44 -0500)
Unrecoverable errors detected by GSSAPI encryption can't just be
reported with elog(ERROR) or elog(FATAL), because attempting to
send the error report to the client is likely to lead to infinite
recursion or loss of protocol sync.  Instead make this code do what
the SSL encryption code has long done, which is to just report any
such failure to the server log (with elevel COMMERROR), then pretend
we've lost the connection by returning errno = ECONNRESET.

Along the way, fix confusion about whether message translation is done
by pg_GSS_error() or its callers (the latter should do it), and make
the backend version of that function work more like the frontend
version.

Avoid allocating the port->gss struct until it's needed; we surely
don't need to allocate it in the postmaster.

Improve logging of "connection authorized" messages with GSS enabled.
(As part of this, I back-patched the code changes from dc11f31a1.)

Make BackendStatusShmemSize() account for the GSS-related space that
will be allocated by CreateSharedBackendStatus().  This omission
could possibly cause out-of-shared-memory problems with very high
max_connections settings.

Remove arbitrary, pointless restriction that only GSS authentication
can be used on a GSS-encrypted connection.

Improve documentation; notably, document the fact that libpq now
prefers GSS encryption over SSL encryption if both are possible.

Per report from Mikael Gustavsson.  Back-patch to v12 where
this code was introduced.

Discussion: https://postgr.es/m/e5b0b6ed05764324a2f3fe7acfc766d5@smhi.se

15 files changed:
doc/src/sgml/client-auth.sgml
doc/src/sgml/libpq.sgml
doc/src/sgml/protocol.sgml
doc/src/sgml/runtime.sgml
src/backend/libpq/auth.c
src/backend/libpq/be-gssapi-common.c
src/backend/libpq/be-secure-gssapi.c
src/backend/libpq/be-secure.c
src/backend/libpq/hba.c
src/backend/libpq/pqcomm.c
src/backend/postmaster/pgstat.c
src/backend/postmaster/postmaster.c
src/backend/utils/init/postinit.c
src/include/libpq/be-gssapi-common.h
src/include/libpq/libpq-be.h

index a0d584fb34eef2193cd4d0aae00e3d374631dacc..92f474e8e6bd48c012268a7102b8a6f856089484 100644 (file)
@@ -196,14 +196,6 @@ hostnogssenc  database  user
        the hostgssenc record is ignored except for logging
        a warning that it cannot match any connections.
       
-
-      
-        Note that the only supported
-        authentication methods for use
-        with GSSAPI encryption
-        are gssreject,
-        and trust.
-      
      
     
 
@@ -1196,15 +1188,15 @@ omicron         bryanh                  guest1
 
    
     GSSAPI is an industry-standard protocol
-    for secure authentication defined in RFC 2743.
-
+    for secure authentication defined in
+    RFC 2743.
     PostgreSQL
-    supports GSSAPI for use as either an encrypted,
-    authenticated layer, or for authentication only.
+    supports GSSAPI for authentication,
+    communications encryption, or both.
     GSSAPI provides automatic authentication
     (single sign-on) for systems that support it. The authentication itself is
     secure.  If GSSAPI encryption
-    (see hostgssenc) or SSL encryption are
+    or SSL encryption is
     used, the data sent along the database connection will be encrypted;
     otherwise, it will not.
    
@@ -1216,37 +1208,46 @@ omicron         bryanh                  guest1
 
    
     When GSSAPI uses
-    Kerberos, it uses a standard principal
-    in the format
+    Kerberos, it uses a standard service
+    principal (authentication identity) name in the format
     servicename/hostname@realm.
-    The PostgreSQL server will accept any principal that is included in the keytab used by
-    the server, but care needs to be taken to specify the correct principal details when
-    making the connection from the client using the krbsrvname connection parameter. (See
-    also .) The installation default can be
-    changed from the default postgres at build time using
-    ./configure --with-krb-srvnam=whatever.
-    In most environments,
-    this parameter never needs to be changed.
-    Some Kerberos implementations might require a different service name,
-    such as Microsoft Active Directory which requires the service name
-    to be in upper case (POSTGRES).
-   
-   
-    hostname is the fully qualified host name of the
-    server machine. The service principal's realm is the preferred realm
-    of the server machine.
-   
-
-   
-    Client principals can be mapped to different PostgreSQL
-    database user names with pg_ident.conf.  For example,
+    The principal name used by a particular installation is not encoded in
+    the PostgreSQL server in any way; rather it
+    is specified in the keytab file that the server
+    reads to determine its identity.  If multiple principals are listed in
+    the keytab file, the server will accept any one of them.
+    The server's realm name is the preferred realm specified in the Kerberos
+    configuration file(s) accessible to the server.
+   
+
+   
+    When connecting, the client must know the principal name of the server
+    it intends to connect to.  The servicename
+    part of the principal is ordinarily postgres,
+    but another value can be selected via libpq's
+     connection parameter.
+    The hostname part is the fully qualified
+    host name that libpq is told to connect to.
+    The realm name is the preferred realm specified in the Kerberos
+    configuration file(s) accessible to the client.
+   
+
+   
+    The client will also have a principal name for its own identity
+    (and it must have a valid ticket for this principal).  To
+    use GSSAPI for authentication, the client
+    principal must be associated with
+    a PostgreSQL database user name.
+    The pg_ident.conf configuration file can be used
+    to map principals to user names; for example,
     pgusername@realm could be mapped to just pgusername.
     Alternatively, you can use the full username@realm principal as
     the role name in PostgreSQL without any mapping.
    
 
    
-    PostgreSQL also supports a parameter to strip the realm from
+    PostgreSQL also supports mapping
+    client principals to user names by just stripping the realm from
     the principal.  This method is supported for backwards compatibility and is
     strongly discouraged as it is then impossible to distinguish different users
     with the same user name but coming from different realms.  To enable this,
@@ -1260,39 +1261,34 @@ omicron         bryanh                  guest1
    
 
    
-    Make sure that your server keytab file is readable (and preferably
-    only readable, not writable) by the PostgreSQL
-    server account.  (See also .) The location
-    of the key file is specified by the 
+    The location of the server's keytab file is specified by the 
     linkend="guc-krb-server-keyfile"/> configuration
     parameter. The default is
-    /usr/local/pgsql/etc/krb5.keytab (or whatever
-    directory was specified as sysconfdir at build time).
+    FILE:/usr/local/pgsql/etc/krb5.keytab
+    (where the directory part is whatever was specified
+    as sysconfdir at build time).
     For security reasons, it is recommended to use a separate keytab
     just for the PostgreSQL server rather
-    than opening up permissions on the system keytab file.
+    than allowing the server to read the system keytab file.
+    Make sure that your server keytab file is readable (and preferably
+    only readable, not writable) by the PostgreSQL
+    server account.  (See also .)
    
+
    
-    The keytab file is generated by the Kerberos software; see the
-    Kerberos documentation for details. The following example is
-   for MIT-compatible Kerberos 5 implementations:
+    The keytab file is generated using the Kerberos software; see the
+    Kerberos documentation for details. The following example shows
+    doing this using the kadmin tool of
+    MIT-compatible Kerberos 5 implementations:
 
-kadmin% ank -randkey postgres/server.my.domain.org
+kadmin% addprinc -randkey postgres/server.my.domain.org
 kadmin% ktadd -k krb5.keytab postgres/server.my.domain.org
 
    
 
    
-    When connecting to the database make sure you have a ticket for a
-    principal matching the requested database user name. For example, for
-    database user name fred, principal
-    [email protected] would be able to connect. To also allow
-    principal fred/[email protected], use a user name
-    map, as described in .
-   
-
-   
-    The following configuration options are supported for GSSAPI:
+    The following authentication options are supported for
+    the GSSAPI authentication method:
     
      
       include_realm
@@ -1315,7 +1311,7 @@ omicron         bryanh                  guest1
       map
       
        
-        Allows for mapping between system and database user names. See
+        Allows mapping from client principals to database user names. See
          for details.  For a GSSAPI/Kerberos
         principal, such as [email protected] (or, less
         commonly, username/[email protected]), the
@@ -1342,6 +1338,15 @@ omicron         bryanh                  guest1
      
     
    
+
+   
+    In addition to these settings, which can be different for
+    different pg_hba.conf entries, there is the
+    server-wide  configuration
+    parameter.  If that is set to true, client principals are matched to
+    user map entries case-insensitively.  krb_realm, if
+    set, is also matched case-insensitively.
+   
   
 
   
index aa246e64479a92591ffdb4ea742a4cfbffc5f573..e6b96b21f6c8f98318442014fb9e46ce42a18553 100644 (file)
@@ -1577,6 +1577,16 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
         connection.SSL
         sortas="libpq">with libpq
        
+
+       
+        Note that if GSSAPI encryption is possible,
+        that will be used in preference to SSL
+        encryption, regardless of the value of sslmode.
+        To force use of SSL encryption in an
+        environment that has working GSSAPI
+        infrastructure (such as a Kerberos server), also
+        set gssencmode to disable.
+       
       
      
 
@@ -1778,6 +1788,15 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
         This must match the service name specified in the server
         configuration for Kerberos authentication to succeed. (See also
         .)
+        The default value is normally postgres,
+        but that can be changed when
+        building PostgreSQL via
+        the  option
+        of configure.
+        In most environments, this parameter never needs to be changed.
+        Some Kerberos implementations might require a different service name,
+        such as Microsoft Active Directory which requires the service name
+        to be in upper case (POSTGRES).
        
       
      
index 2a0197eeb3640ff50a3bad3ff00dfebc3e98f2df..6efcab3734202baf1e47c76e47e967d17d027552 100644 (file)
@@ -1454,6 +1454,10 @@ SELCT 1/0;
     SSL-encrypted.  To continue after
     N, send the usual StartupMessage and proceed without
     encryption.
+    (Alternatively, it is permissible to issue a GSSENCRequest message
+    after an N response to try to
+    use GSSAPI encryption instead
+    of SSL.)
    
 
    
@@ -1508,20 +1512,14 @@ SELCT 1/0;
     result from the server, until it returns no output.  When sending the
     results of gss_init_sec_context() to the server,
     prepend the length of the message as a four byte integer in network byte
-    order.  If this is successful, then use gss_wrap() to
-    encrypt the usual StartupMessage and all subsequent data, prepending the
-    length of the result from gss_wrap() as a four byte
-    integer in network byte order to the actual encrypted payload.  Note that
-    the server will only accept encrypted packets from the client which are less
-    than 16kB; gss_wrap_size_limit() should be used by the
-    client to determine the size of the unencrypted message which will fit
-    within this limit and larger messages should be broken up into multiple
-    gss_wrap() calls.  Typical segments are 8kB of
-    unencrypted data, resulting in encrypted packets of slightly larger than 8kB
-    but well within the 16kB maximum.  The server can be expected to not send
-    encrypted packets of larger than 16kB to the client.  To continue after
+    order.
+    To continue after
     N, send the usual StartupMessage and proceed without
     encryption.
+    (Alternatively, it is permissible to issue an SSLRequest message
+    after an N response to try to
+    use SSL encryption instead
+    of GSSAPI.)
    
 
    
@@ -1531,9 +1529,7 @@ SELCT 1/0;
     support to PostgreSQL.  In this case the
     connection must be closed, but the frontend might choose to open a fresh
     connection and proceed without requesting GSSAPI
-    encryption.  Given the length limits specified above, the ErrorMessage can
-    not be confused with a proper response from the server with an appropriate
-    length.
+    encryption.
    
 
    
@@ -1541,6 +1537,22 @@ SELCT 1/0;
     opened to send a CancelRequest message.
    
 
+   
+    Once GSSAPI encryption has been successfully
+    established, use gss_wrap() to
+    encrypt the usual StartupMessage and all subsequent data, prepending the
+    length of the result from gss_wrap() as a four byte
+    integer in network byte order to the actual encrypted payload.  Note that
+    the server will only accept encrypted packets from the client which are less
+    than 16kB; gss_wrap_size_limit() should be used by the
+    client to determine the size of the unencrypted message which will fit
+    within this limit and larger messages should be broken up into multiple
+    gss_wrap() calls.  Typical segments are 8kB of
+    unencrypted data, resulting in encrypted packets of slightly larger than 8kB
+    but well within the 16kB maximum.  The server can be expected to not send
+    encrypted packets of larger than 16kB to the client.
+   
+
    
     While the protocol itself does not provide a way for the server to
     force GSSAPI encryption, the administrator can
index f9cc8503c07ad2ab23e19661d713cc4c633dd164..3e7db2cf6d35445fe6b0fea0fc96f6a75e36fe45 100644 (file)
@@ -2549,7 +2549,7 @@ openssl x509 -req -in server.csr -text -days 365 \
    PostgreSQL also has native support for
    using GSSAPI to encrypt client/server communications for
    increased security.  Support requires that a GSSAPI
-   implementation (such as MIT krb5) is installed on both client and server
+   implementation (such as MIT Kerberos) is installed on both client and server
    systems, and that support in PostgreSQL is
    enabled at build time (see ).
   
@@ -2568,6 +2568,16 @@ openssl x509 -req -in server.csr -text -days 365 \
     some or all connections.
    
 
+   
+    When using GSSAPI for encryption, it is common to
+    use GSSAPI for authentication as well, since the
+    underlying mechanism will determine both client and server identities
+    (according to the GSSAPI implementation) in any
+    case.  But this is not required;
+    another PostgreSQL authentication method
+    can be chosen to perform additional verification.
+   
+
    
     Other than configuration of the negotiation
     behavior, GSSAPI encryption requires no setup beyond
index 02b6c3f127c6756116a725f595008b3edb9c1ebe..c8d212461f6074737b420dc5b018a514995db256 100644 (file)
@@ -382,17 +382,6 @@ ClientAuthentication(Port *port)
                     errmsg("connection requires a valid client certificate")));
    }
 
-#ifdef ENABLE_GSS
-   if (port->gss->enc && port->hba->auth_method != uaReject &&
-       port->hba->auth_method != uaImplicitReject &&
-       port->hba->auth_method != uaTrust &&
-       port->hba->auth_method != uaGSS)
-   {
-       ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
-                       errmsg("GSSAPI encryption can only be used with gss, trust, or reject authentication methods")));
-   }
-#endif
-
    /*
     * Now proceed to do the actual authentication check
     */
@@ -533,7 +522,17 @@ ClientAuthentication(Port *port)
 
        case uaGSS:
 #ifdef ENABLE_GSS
+           /* We might or might not have the gss workspace already */
+           if (port->gss == NULL)
+               port->gss = (pg_gssinfo *)
+                   MemoryContextAllocZero(TopMemoryContext,
+                                          sizeof(pg_gssinfo));
            port->gss->auth = true;
+
+           /*
+            * If GSS state was set up while enabling encryption, we can just
+            * check the client's principal.  Otherwise, ask for it.
+            */
            if (port->gss->enc)
                status = pg_GSS_checkauth(port);
            else
@@ -548,6 +547,10 @@ ClientAuthentication(Port *port)
 
        case uaSSPI:
 #ifdef ENABLE_SSPI
+           if (port->gss == NULL)
+               port->gss = (pg_gssinfo *)
+                   MemoryContextAllocZero(TopMemoryContext,
+                                          sizeof(pg_gssinfo));
            sendAuthRequest(port, AUTH_REQ_SSPI, NULL, 0);
            status = pg_SSPI_recvauth(port);
 #else
@@ -1185,9 +1188,9 @@ pg_GSS_recvauth(Port *port)
        if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
        {
            gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
-           pg_GSS_error(ERROR,
-                        _("accepting GSS security context failed"),
+           pg_GSS_error(_("accepting GSS security context failed"),
                         maj_stat, min_stat);
+           return STATUS_ERROR;
        }
 
        if (maj_stat == GSS_S_CONTINUE_NEEDED)
@@ -1224,9 +1227,11 @@ pg_GSS_checkauth(Port *port)
     */
    maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
    if (maj_stat != GSS_S_COMPLETE)
-       pg_GSS_error(ERROR,
-                    _("retrieving GSS user name failed"),
+   {
+       pg_GSS_error(_("retrieving GSS user name failed"),
                     maj_stat, min_stat);
+       return STATUS_ERROR;
+   }
 
    /*
     * Copy the original name of the authenticated principal into our backend
index ef9b8ebd6d42ecc2df38bbda7db3c256b0dee8e9..be5d051c2027ad58e5cf8d5f49c1cff28872ae9b 100644 (file)
@@ -17,8 +17,9 @@
 #include "libpq/be-gssapi-common.h"
 
 /*
- * Helper function for getting all strings of a GSSAPI error (of specified
- * stat).  Call once for GSS_CODE and once for MECH_CODE.
+ * Fetch all errors of a specific type and append to "s" (buffer of size len).
+ * If we obtain more than one string, separate them with spaces.
+ * Call once for GSS_CODE and once for MECH_CODE.
  */
 static void
 pg_GSS_error_int(char *s, size_t len, OM_uint32 stat, int type)
@@ -28,31 +29,49 @@ pg_GSS_error_int(char *s, size_t len, OM_uint32 stat, int type)
    OM_uint32   lmin_s,
                msg_ctx = 0;
 
-   gmsg.value = NULL;
-   gmsg.length = 0;
+   s[0] = '\0';                /* just in case gss_display_status fails */
 
    do
    {
-       gss_display_status(&lmin_s, stat, type,
-                          GSS_C_NO_OID, &msg_ctx, &gmsg);
-       strlcpy(s + i, gmsg.value, len - i);
+       if (gss_display_status(&lmin_s, stat, type, GSS_C_NO_OID,
+                              &msg_ctx, &gmsg) != GSS_S_COMPLETE)
+           break;
+       if (i > 0)
+       {
+           if (i < len)
+               s[i] = ' ';
+           i++;
+       }
+       if (i < len)
+           strlcpy(s + i, gmsg.value, len - i);
        i += gmsg.length;
        gss_release_buffer(&lmin_s, &gmsg);
    }
-   while (msg_ctx && i < len);
+   while (msg_ctx);
 
-   if (msg_ctx || i == len)
-       ereport(WARNING,
-               (errmsg_internal("incomplete GSS error report")));
+   if (i >= len)
+   {
+       elog(COMMERROR, "incomplete GSS error report");
+       s[len - 1] = '\0';      /* ensure string is nul-terminated */
+   }
 }
 
 /*
- * Fetch and report all error messages from GSSAPI.  To avoid allocation,
- * total error size is capped (at 128 bytes for each of major and minor).  No
- * known mechanisms will produce error messages beyond this cap.
+ * Report the GSSAPI error described by maj_stat/min_stat.
+ *
+ * errmsg should be an already-translated primary error message.
+ * The GSSAPI info is appended as errdetail.
+ *
+ * The error is always reported with elevel COMMERROR; we daren't try to
+ * send it to the client, as that'd likely lead to infinite recursion
+ * when elog.c tries to write to the client.
+ *
+ * To avoid memory allocation, total error size is capped (at 128 bytes for
+ * each of major and minor).  No known mechanisms will produce error messages
+ * beyond this cap.
  */
 void
-pg_GSS_error(int severity, const char *errmsg,
+pg_GSS_error(const char *errmsg,
             OM_uint32 maj_stat, OM_uint32 min_stat)
 {
    char        msg_major[128],
@@ -68,7 +87,7 @@ pg_GSS_error(int severity, const char *errmsg,
     * errmsg_internal, since translation of the first part must be done
     * before calling this function anyway.
     */
-   ereport(severity,
+   ereport(COMMERROR,
            (errmsg_internal("%s", errmsg),
             errdetail_internal("%s: %s", msg_major, msg_minor)));
 }
index 64427f185bb765328f48fc088df6436c04ae2187..36a28254bd31d206d74c9f0cbcbbeb4d70caca86 100644 (file)
@@ -21,6 +21,7 @@
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "utils/memutils.h"
 
 
 /*
@@ -81,10 +82,14 @@ static uint32 PqGSSMaxPktSize;  /* Maximum size we can encrypt and fit the
  * transport negotiation is complete).
  *
  * On success, returns the number of data bytes consumed (possibly less than
- * len).  On failure, returns -1 with errno set appropriately.  (For fatal
- * errors, we may just elog and exit, if errno wouldn't be sufficient to
- * describe the error.)  For retryable errors, caller should call again
- * (passing the same data) once the socket is ready.
+ * len).  On failure, returns -1 with errno set appropriately.  For retryable
+ * errors, caller should call again (passing the same data) once the socket
+ * is ready.
+ *
+ * Dealing with fatal errors here is a bit tricky: we can't invoke elog(FATAL)
+ * since it would try to write to the client, probably resulting in infinite
+ * recursion.  Instead, use elog(COMMERROR) to log extra info about the
+ * failure if necessary, and then return an errno indicating connection loss.
  */
 ssize_t
 be_gssapi_write(Port *port, void *ptr, size_t len)
@@ -108,8 +113,11 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
     * again, so if it offers a len less than that, something is wrong.
     */
    if (len < PqGSSSendConsumed)
-       elog(FATAL, "GSSAPI caller failed to retransmit all data needing to be retried");
-
+   {
+       elog(COMMERROR, "GSSAPI caller failed to retransmit all data needing to be retried");
+       errno = ECONNRESET;
+       return -1;
+   }
    /* Discount whatever source data we already encrypted. */
    bytes_to_encrypt = len - PqGSSSendConsumed;
    bytes_encrypted = PqGSSSendConsumed;
@@ -192,17 +200,27 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
        major = gss_wrap(&minor, gctx, 1, GSS_C_QOP_DEFAULT,
                         &input, &conf_state, &output);
        if (major != GSS_S_COMPLETE)
-           pg_GSS_error(FATAL, gettext_noop("GSSAPI wrap error"), major, minor);
-
+       {
+           pg_GSS_error(_("GSSAPI wrap error"), major, minor);
+           errno = ECONNRESET;
+           return -1;
+       }
        if (conf_state == 0)
-           ereport(FATAL,
+       {
+           ereport(COMMERROR,
                    (errmsg("outgoing GSSAPI message would not use confidentiality")));
-
+           errno = ECONNRESET;
+           return -1;
+       }
        if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
-           ereport(FATAL,
+       {
+           ereport(COMMERROR,
                    (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
                            (size_t) output.length,
                            PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
+           errno = ECONNRESET;
+           return -1;
+       }
 
        bytes_encrypted += input.length;
        bytes_to_encrypt -= input.length;
@@ -234,9 +252,11 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
  * transport negotiation is complete).
  *
  * Returns the number of data bytes read, or on failure, returns -1
- * with errno set appropriately.  (For fatal errors, we may just elog and
- * exit, if errno wouldn't be sufficient to describe the error.)  For
- * retryable errors, caller should call again once the socket is ready.
+ * with errno set appropriately.  For retryable errors, caller should call
+ * again once the socket is ready.
+ *
+ * We treat fatal errors the same as in be_gssapi_write(), even though the
+ * argument about infinite recursion doesn't apply here.
  */
 ssize_t
 be_gssapi_read(Port *port, void *ptr, size_t len)
@@ -326,10 +346,14 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
        input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);
 
        if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
-           ereport(FATAL,
+       {
+           ereport(COMMERROR,
                    (errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
                            (size_t) input.length,
                            PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))));
+           errno = ECONNRESET;
+           return -1;
+       }
 
        /*
         * Read as much of the packet as we are able to on this call into
@@ -361,12 +385,18 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
 
        major = gss_unwrap(&minor, gctx, &input, &output, &conf_state, NULL);
        if (major != GSS_S_COMPLETE)
-           pg_GSS_error(FATAL, gettext_noop("GSSAPI unwrap error"),
-                        major, minor);
-
+       {
+           pg_GSS_error(_("GSSAPI unwrap error"), major, minor);
+           errno = ECONNRESET;
+           return -1;
+       }
        if (conf_state == 0)
-           ereport(FATAL,
+       {
+           ereport(COMMERROR,
                    (errmsg("incoming GSSAPI message did not use confidentiality")));
+           errno = ECONNRESET;
+           return -1;
+       }
 
        memcpy(PqGSSResultBuffer, output.value, output.length);
        PqGSSResultLength = output.length;
@@ -468,6 +498,12 @@ secure_open_gssapi(Port *port)
    OM_uint32   major,
                minor;
 
+   /*
+    * Allocate subsidiary Port data for GSSAPI operations.
+    */
+   port->gss = (pg_gssinfo *)
+       MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo));
+
    /*
     * Allocate buffers and initialize state variables.  By malloc'ing the
     * buffers at this point, we avoid wasting static data space in processes
@@ -521,10 +557,13 @@ secure_open_gssapi(Port *port)
         * Verify on our side that the client doesn't do something funny.
         */
        if (input.length > PQ_GSS_RECV_BUFFER_SIZE)
-           ereport(FATAL,
+       {
+           ereport(COMMERROR,
                    (errmsg("oversize GSSAPI packet sent by the client (%zu > %d)",
                            (size_t) input.length,
                            PQ_GSS_RECV_BUFFER_SIZE)));
+           return -1;
+       }
 
        /*
         * Get the rest of the packet so we can pass it to GSSAPI to accept
@@ -544,7 +583,7 @@ secure_open_gssapi(Port *port)
                                       NULL, NULL);
        if (GSS_ERROR(major))
        {
-           pg_GSS_error(ERROR, gettext_noop("could not accept GSSAPI security context"),
+           pg_GSS_error(_("could not accept GSSAPI security context"),
                         major, minor);
            gss_release_buffer(&minor, &output);
            return -1;
@@ -570,10 +609,14 @@ secure_open_gssapi(Port *port)
            uint32      netlen = htonl(output.length);
 
            if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
-               ereport(FATAL,
+           {
+               ereport(COMMERROR,
                        (errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
                                (size_t) output.length,
                                PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
+               gss_release_buffer(&minor, &output);
+               return -1;
+           }
 
            memcpy(PqGSSSendBuffer, (char *) &netlen, sizeof(uint32));
            PqGSSSendLength += sizeof(uint32);
@@ -634,8 +677,10 @@ secure_open_gssapi(Port *port)
                                &PqGSSMaxPktSize);
 
    if (GSS_ERROR(major))
-       pg_GSS_error(FATAL, gettext_noop("GSSAPI size check error"),
-                    major, minor);
+   {
+       pg_GSS_error(_("GSSAPI size check error"), major, minor);
+       return -1;
+   }
 
    port->gss->enc = true;
 
@@ -667,12 +712,13 @@ be_gssapi_get_enc(Port *port)
 }
 
 /*
- * Return the GSSAPI principal used for authentication on this connection.
+ * Return the GSSAPI principal used for authentication on this connection
+ * (NULL if we did not perform GSSAPI authentication).
  */
 const char *
 be_gssapi_get_princ(Port *port)
 {
-   if (!port || !port->gss->auth)
+   if (!port || !port->gss)
        return NULL;
 
    return port->gss->princ;
index 2ae507a90255a4f3f61faed8eb6d7f7b5bfdaf18..59bc02e79c68c8ce48e800ce5f97aaa2b1ec3d27 100644 (file)
@@ -160,7 +160,7 @@ retry:
    else
 #endif
 #ifdef ENABLE_GSS
-   if (port->gss->enc)
+   if (port->gss && port->gss->enc)
    {
        n = be_gssapi_read(port, ptr, len);
        waitfor = WL_SOCKET_READABLE;
@@ -273,7 +273,7 @@ retry:
    else
 #endif
 #ifdef ENABLE_GSS
-   if (port->gss->enc)
+   if (port->gss && port->gss->enc)
    {
        n = be_gssapi_write(port, ptr, len);
        waitfor = WL_SOCKET_WRITEABLE;
index 5c2b3c8681d01b7b102b3f63ab196d307f117283..031c460d13fca2372f890f3c9d5e46f13836306b 100644 (file)
@@ -1424,19 +1424,6 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
        *err_msg = "gssapi authentication is not supported on local sockets";
        return NULL;
    }
-   if (parsedline->conntype == ctHostGSS &&
-       parsedline->auth_method != uaGSS &&
-       parsedline->auth_method != uaReject &&
-       parsedline->auth_method != uaTrust)
-   {
-       ereport(elevel,
-               (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                errmsg("GSSAPI encryption only supports gss, trust, or reject authentication"),
-                errcontext("line %d of configuration file \"%s\"",
-                           line_num, HbaFileName)));
-       *err_msg = "GSSAPI encryption only supports gss, trust, or reject authentication";
-       return NULL;
-   }
 
    if (parsedline->conntype != ctLocal &&
        parsedline->auth_method == uaPeer)
@@ -2113,9 +2100,11 @@ check_hba(hbaPort *port)
 
            /* Check GSSAPI state */
 #ifdef ENABLE_GSS
-           if (port->gss->enc && hba->conntype == ctHostNoGSS)
+           if (port->gss && port->gss->enc &&
+               hba->conntype == ctHostNoGSS)
                continue;
-           else if (!port->gss->enc && hba->conntype == ctHostGSS)
+           else if (!(port->gss && port->gss->enc) &&
+                    hba->conntype == ctHostGSS)
                continue;
 #else
            if (hba->conntype == ctHostGSS)
index 7717bb27195e15295358e8c62e8b1bf86226ca13..ee2cd86866da9f71dec0708ecba09d014bc33fb9 100644 (file)
@@ -256,29 +256,26 @@ socket_close(int code, Datum arg)
    /* Nothing to do in a standalone backend, where MyProcPort is NULL. */
    if (MyProcPort != NULL)
    {
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 #ifdef ENABLE_GSS
-       OM_uint32   min_s;
-
        /*
         * Shutdown GSSAPI layer.  This section does nothing when interrupting
         * BackendInitialize(), because pg_GSS_recvauth() makes first use of
         * "ctx" and "cred".
+        *
+        * Note that we don't bother to free MyProcPort->gss, since we're
+        * about to exit anyway.
         */
-       if (MyProcPort->gss->ctx != GSS_C_NO_CONTEXT)
-           gss_delete_sec_context(&min_s, &MyProcPort->gss->ctx, NULL);
+       if (MyProcPort->gss)
+       {
+           OM_uint32   min_s;
 
-       if (MyProcPort->gss->cred != GSS_C_NO_CREDENTIAL)
-           gss_release_cred(&min_s, &MyProcPort->gss->cred);
-#endif                         /* ENABLE_GSS */
+           if (MyProcPort->gss->ctx != GSS_C_NO_CONTEXT)
+               gss_delete_sec_context(&min_s, &MyProcPort->gss->ctx, NULL);
 
-       /*
-        * GSS and SSPI share the port->gss struct.  Since nowhere else does a
-        * postmaster child free this, doing so is safe when interrupting
-        * BackendInitialize().
-        */
-       free(MyProcPort->gss);
-#endif                         /* ENABLE_GSS || ENABLE_SSPI */
+           if (MyProcPort->gss->cred != GSS_C_NO_CREDENTIAL)
+               gss_release_cred(&min_s, &MyProcPort->gss->cred);
+       }
+#endif                         /* ENABLE_GSS */
 
        /*
         * Cleanly shut down SSL layer.  Nowhere else does a postmaster child
index 309378ae54e4e033b33f762b9b8f8fea349ad1c3..24e54d6766f59dc160c7f4de78656d79ce22c44e 100644 (file)
@@ -2738,6 +2738,11 @@ BackendStatusShmemSize(void)
    /* BackendSslStatusBuffer: */
    size = add_size(size,
                    mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots));
+#endif
+#ifdef ENABLE_GSS
+   /* BackendGssStatusBuffer: */
+   size = add_size(size,
+                   mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots));
 #endif
    return size;
 }
@@ -3015,12 +3020,13 @@ pgstat_bestart(void)
 #ifdef ENABLE_GSS
    if (MyProcPort && MyProcPort->gss != NULL)
    {
+       const char *princ = be_gssapi_get_princ(MyProcPort);
+
        lbeentry.st_gss = true;
        lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
        lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
-
-       if (lgssstatus.gss_auth)
-           strlcpy(lgssstatus.gss_princ, be_gssapi_get_princ(MyProcPort), NAMEDATALEN);
+       if (princ)
+           strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
    }
    else
    {
index b39413c6858f200710815b416fe4c42de2163136..5775fc0c0910a8ec091319ba52ff4e6921eab1d9 100644 (file)
@@ -2059,6 +2059,7 @@ retry1:
    else if (proto == NEGOTIATE_GSS_CODE && !gss_done)
    {
        char        GSSok = 'N';
+
 #ifdef ENABLE_GSS
        /* No GSSAPI encryption when on Unix socket */
        if (!IS_AF_UNIX(port->laddr.addr.ss_family))
@@ -2527,37 +2528,19 @@ ConnCreate(int serverFd)
        return NULL;
    }
 
-   /*
-    * Allocate GSSAPI specific state struct
-    */
-#ifndef EXEC_BACKEND
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
-   port->gss = (pg_gssinfo *) calloc(1, sizeof(pg_gssinfo));
-   if (!port->gss)
-   {
-       ereport(LOG,
-               (errcode(ERRCODE_OUT_OF_MEMORY),
-                errmsg("out of memory")));
-       ExitPostmaster(1);
-   }
-#endif
-#endif
-
    return port;
 }
 
 
 /*
  * ConnFree -- free a local connection data structure
+ *
+ * Caller has already closed the socket if any, so there's not much
+ * to do here.
  */
 static void
 ConnFree(Port *conn)
 {
-#ifdef USE_SSL
-   secure_close(conn);
-#endif
-   if (conn->gss)
-       free(conn->gss);
    free(conn);
 }
 
@@ -4933,18 +4916,6 @@ SubPostmasterMain(int argc, char *argv[])
    /* Close the postmaster's sockets (as soon as we know them) */
    ClosePostmasterPorts(strcmp(argv[1], "--forklog") == 0);
 
-   /*
-    * Set up memory area for GSS information. Mirrors the code in ConnCreate
-    * for the non-exec case.
-    */
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
-   port.gss = (pg_gssinfo *) calloc(1, sizeof(pg_gssinfo));
-   if (!port.gss)
-       ereport(FATAL,
-               (errcode(ERRCODE_OUT_OF_MEMORY),
-                errmsg("out of memory")));
-#endif
-
    /*
     * If appropriate, physically re-attach to shared memory segment. We want
     * to do this before going any further to ensure that we can attach at the
index 6cf0cef6f30d04a5a02bad8d764be8772d64963b..01463af22f8d67abd35a50218c797cfd6d8f5d5d 100644 (file)
@@ -246,62 +246,51 @@ PerformAuthentication(Port *port)
 
    if (Log_connections)
    {
+       StringInfoData logmsg;
+
+       initStringInfo(&logmsg);
        if (am_walsender)
-       {
+           appendStringInfo(&logmsg, _("replication connection authorized: user=%s"),
+                            port->user_name);
+       else
+           appendStringInfo(&logmsg, _("connection authorized: user=%s"),
+                            port->user_name);
+       if (!am_walsender)
+           appendStringInfo(&logmsg, _(" database=%s"), port->database_name);
+
+       if (port->application_name != NULL)
+           appendStringInfo(&logmsg, _(" application_name=%s"),
+                            port->application_name);
+
 #ifdef USE_SSL
-           if (port->ssl_in_use)
-               ereport(LOG,
-                       (port->application_name != NULL
-                        ? errmsg("replication connection authorized: user=%s application_name=%s SSL enabled (protocol=%s, cipher=%s, bits=%d, compression=%s)",
-                                 port->user_name,
-                                 port->application_name,
-                                 be_tls_get_version(port),
-                                 be_tls_get_cipher(port),
-                                 be_tls_get_cipher_bits(port),
-                                 be_tls_get_compression(port) ? _("on") : _("off"))
-                        : errmsg("replication connection authorized: user=%s SSL enabled (protocol=%s, cipher=%s, bits=%d, compression=%s)",
-                                 port->user_name,
-                                 be_tls_get_version(port),
-                                 be_tls_get_cipher(port),
-                                 be_tls_get_cipher_bits(port),
-                                 be_tls_get_compression(port) ? _("on") : _("off"))));
-           else
+       if (port->ssl_in_use)
+           appendStringInfo(&logmsg, _(" SSL enabled (protocol=%s, cipher=%s, bits=%d, compression=%s)"),
+                            be_tls_get_version(port),
+                            be_tls_get_cipher(port),
+                            be_tls_get_cipher_bits(port),
+                            be_tls_get_compression(port) ? _("on") : _("off"));
 #endif
-               ereport(LOG,
-                       (port->application_name != NULL
-                        ? errmsg("replication connection authorized: user=%s application_name=%s",
-                                 port->user_name,
-                                 port->application_name)
-                        : errmsg("replication connection authorized: user=%s",
-                                 port->user_name)));
-       }
-       else
+#ifdef ENABLE_GSS
+       if (port->gss)
        {
-#ifdef USE_SSL
-           if (port->ssl_in_use)
-               ereport(LOG,
-                       (port->application_name != NULL
-                        ? errmsg("connection authorized: user=%s database=%s application_name=%s SSL enabled (protocol=%s, cipher=%s, bits=%d, compression=%s)",
-                                 port->user_name, port->database_name, port->application_name,
-                                 be_tls_get_version(port),
-                                 be_tls_get_cipher(port),
-                                 be_tls_get_cipher_bits(port),
-                                 be_tls_get_compression(port) ? _("on") : _("off"))
-                        : errmsg("connection authorized: user=%s database=%s SSL enabled (protocol=%s, cipher=%s, bits=%d, compression=%s)",
-                                 port->user_name, port->database_name,
-                                 be_tls_get_version(port),
-                                 be_tls_get_cipher(port),
-                                 be_tls_get_cipher_bits(port),
-                                 be_tls_get_compression(port) ? _("on") : _("off"))));
+           const char *princ = be_gssapi_get_princ(port);
+
+           if (princ)
+               appendStringInfo(&logmsg,
+                                _(" GSS (authenticated=%s, encrypted=%s, principal=%s)"),
+                                be_gssapi_get_auth(port) ? _("yes") : _("no"),
+                                be_gssapi_get_enc(port) ? _("yes") : _("no"),
+                                princ);
            else
-#endif
-               ereport(LOG,
-                       (port->application_name != NULL
-                        ? errmsg("connection authorized: user=%s database=%s application_name=%s",
-                                 port->user_name, port->database_name, port->application_name)
-                        : errmsg("connection authorized: user=%s database=%s",
-                                 port->user_name, port->database_name)));
+               appendStringInfo(&logmsg,
+                                _(" GSS (authenticated=%s, encrypted=%s)"),
+                                be_gssapi_get_auth(port) ? _("yes") : _("no"),
+                                be_gssapi_get_enc(port) ? _("yes") : _("no"));
        }
+#endif
+
+       ereport(LOG, errmsg_internal("%s", logmsg.data));
+       pfree(logmsg.data);
    }
 
    set_ps_display("startup");
index edf89675cf7710e8fde0cb289f9ff3dba2bc72d0..e1c23ac54593727fa4bf2550ce5591918f9743c4 100644 (file)
@@ -20,7 +20,7 @@
 #include 
 #endif
 
-void       pg_GSS_error(int severity, const char *errmsg,
+extern void pg_GSS_error(const char *errmsg,
                         OM_uint32 maj_stat, OM_uint32 min_stat);
 
 #endif                         /* BE_GSSAPI_COMMON_H */
index 0a23281ad59b511d6cbdddd138e8513d58c2d8e4..fa778e11921bc1f73c54099461eae28d22e42578 100644 (file)
@@ -176,8 +176,9 @@ typedef struct Port
 #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 
    /*
-    * If GSSAPI is supported, store GSSAPI information. Otherwise, store a
-    * NULL pointer to make sure offsets in the struct remain the same.
+    * If GSSAPI is supported and used on this connection, store GSSAPI
+    * information.  Even when GSSAPI is not compiled in, store a NULL pointer
+    * to keep struct offsets the same (for extension ABI compatibility).
     */
    pg_gssinfo *gss;
 #else