Add parameter krb_realm used by GSSAPI, SSPI and Kerberos
authorMagnus Hagander <magnus@hagander.net>
Fri, 9 Nov 2007 17:31:07 +0000 (17:31 +0000)
committerMagnus Hagander <magnus@hagander.net>
Fri, 9 Nov 2007 17:31:07 +0000 (17:31 +0000)
to validate the realm of the connecting user. By default
it's empty meaning no verification, which is the way
Kerberos authentication has traditionally worked in
PostgreSQL.

doc/src/sgml/client-auth.sgml
doc/src/sgml/config.sgml
src/backend/libpq/auth.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/libpq/auth.h

index b4a851588eac7be5c04dd27440234d3bc7a07aef..9e3ab2440d9897efa8429d6af3d9f59daff4857c 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.101 2007/09/14 03:53:54 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/client-auth.sgml,v 1.102 2007/11/09 17:31:07 mha Exp $ -->
 
 <chapter id="client-authentication">
  <title>Client Authentication</title>
@@ -773,10 +773,10 @@ local   db1,db2,@demodbs  all                         md5
    <para>
     Client principals must have their <productname>PostgreSQL</> database user
     name as their first component, for example
-    <literal>pgusername/otherstuff@realm</>. At present the realm of
-    the client is not checked by <productname>PostgreSQL</>; so if you
-    have cross-realm authentication enabled, then any principal in any
-    realm that can communicate with yours will be accepted.
+    <literal>pgusername@realm</>. By default, the realm of the client is
+       not checked by <productname>PostgreSQL</>. If you have cross-realm
+       authentication enabled and need to verify the realm, use the
+       <xref linkend="guc-krb-realm"> parameter.
    </para>
 
    <para>
index f070290f4835acb5443a46ad1d39acb1af560def..d8d8c4deb140aebe001e3e853be454489a2802de 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.153 2007/11/05 17:35:38 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.154 2007/11/09 17:31:07 mha Exp $ -->
 
 <chapter Id="runtime-config">
   <title>Server Configuration</title>
@@ -601,6 +601,21 @@ SET ENABLE_SEQSCAN TO OFF;
       </listitem>
      </varlistentry>
 
+        <varlistentry id="guc-krb-realm" xreflabel="krb_realm">
+         <term><varname>krb_realm</varname> (<type>string</type>)</term>
+         <indexterm>
+          <primary><varname>krb_realm</> configuration parameter</primary>
+         </indexterm>
+         <listitem>
+          <para>
+           Sets the realm to match Kerberos, GSSAPI and SSPI usernames against.
+               See <xref linkend="kerberos-auth">, <xref linkend="gssapi-auth"> or
+               <xref linkend="sspi-auth"> for details. This parameter can only be
+               set at server start.
+       </para>
+         </listitem>
+        </varlistentry>
+
      <varlistentry id="guc-krb-server-keyfile" xreflabel="krb_server_keyfile">
       <term><varname>krb_server_keyfile</varname> (<type>string</type>)</term>
       <indexterm>
index 403a9664b46bc105370d6316ceeb4292bea499a7..bba3ebf5b451bf098a5fdc46ff4736fd380d14c5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.156 2007/09/14 15:58:02 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/libpq/auth.c,v 1.157 2007/11/09 17:31:07 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,6 +42,7 @@ 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 USE_PAM
 #ifdef HAVE_PAM_PAM_APPL_H
@@ -102,30 +103,6 @@ static int CheckLDAPAuth(Port *port);
 #include <com_err.h>
 #endif
 
-/*
- * pg_an_to_ln -- return the local name corresponding to an authentication
- *                               name
- *
- * XXX Assumes that the first aname component is the user name.  This is NOT
- *        necessarily so, since an aname can actually be something out of your
- *        worst X.400 nightmare, like
- *               ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
- *        Note that the MIT an_to_ln code does the same thing if you don't
- *        provide an aname mapping database...it may be a better idea to use
- *        krb5_an_to_ln, except that it punts if multiple components are found,
- *        and we can't afford to punt.
- */
-static char *
-pg_an_to_ln(char *aname)
-{
-       char       *p;
-
-       if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
-               *p = '\0';
-       return aname;
-}
-
-
 /*
  * Various krb5 state which is not connection specfic, and a flag to
  * indicate whether we have initialised it yet.
@@ -216,6 +193,7 @@ pg_krb5_recvauth(Port *port)
        krb5_auth_context auth_context = NULL;
        krb5_ticket *ticket;
        char       *kusername;
+       char       *cp;
 
        if (get_role_line(port->user_name) == NULL)
                return STATUS_ERROR;
@@ -240,8 +218,6 @@ pg_krb5_recvauth(Port *port)
         * The "client" structure comes out of the ticket and is therefore
         * authenticated.  Use it to check the username obtained from the
         * postmaster startup packet.
-        *
-        * I have no idea why this is considered necessary.
         */
 #if defined(HAVE_KRB5_TICKET_ENC_PART2)
        retval = krb5_unparse_name(pg_krb5_context,
@@ -263,7 +239,42 @@ pg_krb5_recvauth(Port *port)
                return STATUS_ERROR;
        }
 
-       kusername = pg_an_to_ln(kusername);
+       cp = strchr(kusername, '@');
+       if (cp)
+       {
+               *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);
+
+                       if (ret)
+                       {
+                               elog(DEBUG2,
+                                        "krb5 realm (%s) and configured realm (%s) don't match",
+                                        cp, pg_krb_realm);
+
+                               krb5_free_ticket(pg_krb5_context, ticket);
+                               krb5_auth_con_free(pg_krb5_context, auth_context);
+                               return STATUS_ERROR;
+                       }
+               }
+       }
+       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;
+       }
+
        if (pg_krb_caseins_users)
                ret = pg_strncasecmp(port->user_name, kusername, SM_DATABASE_USER);
        else
@@ -509,14 +520,42 @@ pg_GSS_recvauth(Port *port)
                                         maj_stat, min_stat);
 
        /*
-        * Compare the part of the username that comes before the @
-        * sign only (ignore realm). The GSSAPI libraries won't have 
-        * authenticated the user if he's from an invalid realm.
+        * 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)
@@ -792,6 +831,21 @@ pg_SSPI_recvauth(Port *port)
 
        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.
index 64eeaabef3d1d16bba5457e3b981b87d373b0e64..49a4cc722e02337109178d85331c4e23b3b624b9 100644 (file)
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.423 2007/09/26 22:36:30 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.424 2007/11/09 17:31:07 mha Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -2043,6 +2043,16 @@ static struct config_string ConfigureNamesString[] =
                "$libdir", NULL, NULL
        },
 
+       {
+               {"krb_realm", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+                       gettext_noop("Sets realm to match Kerberos and GSSAPI users against."),
+                       NULL,
+                       GUC_SUPERUSER_ONLY
+               },
+               &pg_krb_realm,
+               NULL, NULL, NULL
+       },
+
        {
                {"krb_server_keyfile", PGC_POSTMASTER, CONN_AUTH_SECURITY,
                        gettext_noop("Sets the location of the Kerberos server key file."),
index 48023bc4ddf8bda474748b4e17db6a1744b90555..3a94829ced151ddb4dad1d97c03fc696eb4c2908 100644 (file)
@@ -85,6 +85,7 @@
 #krb_server_hostname = ''              # empty string matches any keytab entry
                                        # (change requires restart, kerberos only)
 #krb_caseins_users = off               # (change requires restart)
+#krb_realm = ''                        # (change requires restart)
 
 # - TCP Keepalives -
 # see 'man 7 tcp' for details
index 65c9d512d8494b42818a0d6ffebdd8059e9a4617..da0871d9ffee896bb0c050dacfcb24d7793175da 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/libpq/auth.h,v 1.33 2007/01/05 22:19:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/libpq/auth.h,v 1.34 2007/11/09 17:31:07 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,7 @@ extern char *pg_krb_server_keyfile;
 extern char *pg_krb_srvnam;
 extern bool pg_krb_caseins_users;
 extern char *pg_krb_server_hostname;
+extern char *pg_krb_realm;
 
 extern void ClientAuthentication(Port *port);