-<!-- $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>
<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>
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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
#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.
krb5_auth_context auth_context = NULL;
krb5_ticket *ticket;
char *kusername;
+ char *cp;
if (get_role_line(port->user_name) == NULL)
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.
- *
- * I have no idea why this is considered necessary.
*/
#if defined(HAVE_KRB5_TICKET_ENC_PART2)
retval = krb5_unparse_name(pg_krb5_context,
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
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)
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.