From 4b606ee444a1663a5ae668f5cd0560f165b04c43 Mon Sep 17 00:00:00 2001
From: Magnus Hagander <magnus@hagander.net>
Date: Fri, 9 Nov 2007 17:31:07 +0000
Subject: [PATCH] Add parameter krb_realm used by GSSAPI, SSPI and Kerberos 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                 |  10 +-
 doc/src/sgml/config.sgml                      |  17 ++-
 src/backend/libpq/auth.c                      | 116 +++++++++++++-----
 src/backend/utils/misc/guc.c                  |  12 +-
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/libpq/auth.h                      |   3 +-
 6 files changed, 120 insertions(+), 39 deletions(-)

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index b4a851588ea..9e3ab2440d9 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -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>
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f070290f483..d8d8c4deb14 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -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>
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 403a9664b46..bba3ebf5b45 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -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.
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 64eeaabef3d..49a4cc722e0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -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."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 48023bc4ddf..3a94829ced1 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -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
diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h
index 65c9d512d84..da0871d9ffe 100644
--- a/src/include/libpq/auth.h
+++ b/src/include/libpq/auth.h
@@ -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);
 
-- 
GitLab