[Date Prev][Date Next] [Chronological] [Thread] [Top]

Re: (ITS#6757) SASL canonicalize doesn't work as documented

On Sun, Jan 02, 2011 at 07:40:25PM -0800, Howard Chu wrote:
> >(1) Stick more or less with the current behaviour, and change the
> >documentation to say that you'll get
> >     uid=ursula/admin@foreign.realm,cn=gssapi,cn=auth
> >for foreign realms.
> >
> >However, the odd thing about the current behaviour is that setting
> >olcSaslRealm always sticks a static value (...,cn=<olcSaslRealm>,...) into
> >the auth DN, regardless of whether it's local or foreign.  That's not very
> >useful.
> Note that it's not the OpenLDAP code that's sticking this in there;
> it's the Cyrus code that's returning this realm in the callback. IMO
> our job is to faithfully return what the Cyrus library gave us.

The Cyrus library is faithfully passing on the *default* user_realm that
you originally passed to sasl_server_new() in servers/slapd/sasl.c

(which is global_realm, a.k.a. olcSaslRealm)

Then OpenLDAP is sticking it into the authDN, in slap_sasl_getdn()

> >I think it would make more sense, if olcSaslRealm is present, to use it to
> >*qualify* usernames which don't have a realm.
> That decision is made by the Cyrus library, not us.

It does not mandate what you do with the user_realm. You are free to ignore
it, use it to qualify usernames which have no realm, or whatever.

The documentation for canon_user (in include/sasl.h) says:

 * If user_realm is non-empty and an
 * unqualified user name is supplied, then the canon_user facility is
 * expected to append "@" and user_realm to the user name.  The canon_user
 * facility may treat other characters such as "%" as equivalent to "@".

Here is _canonuser_internal from lib/canonusr.c in Cyrus SASL:

static int _canonuser_internal(const sasl_utils_t *utils,
                               const char *user, unsigned ulen,
                               unsigned flags __attribute__((unused)),
                               char *out_user,
                               unsigned out_umax, unsigned *out_ulen) 
    /* Need to append realm if necessary (see sasl.h) */
    if(sconn && sconn->user_realm && !strchr(user, '@')) {
        u_apprealm = (unsigned) strlen(sconn->user_realm) + 1;
    /* Now Copy */
    memcpy(out_user, begin_u, MIN(ulen, out_umax));
    if(sconn && u_apprealm) {
        if(ulen >= out_umax) return SASL_BUFOVER;
        out_user[ulen] = '@';
        memcpy(&(out_user[ulen+1]), sconn->user_realm,
               MIN(u_apprealm-1, out_umax-ulen-1));
    out_user[MIN(ulen + u_apprealm,out_umax)] = '\0';

> >(2) Change the OpenLDAP behaviour so that it matches the documentation at
> >http://www.openldap.org/doc/admin24/sasl.html#GSSAPI
> >
> >To do this, the canonicalize function would have to parse the username,
> >splitting it on '@' to separate username from realm, so that you would get
> >
> >     uid=ursula/admin,cn=foreign.realm,cn=gssapi,cn=auth
> >
> >If the username doesn't contain '@', but olcSaslRealm is set, then I suggest
> >you insert that instead:
> >
> >     uid=kurt,cn=<olcsaslrealm>,cn=gssapi,cn=auth
> >
> >And if there's no '@' and no olcSaslRealm, then just leave it alone:
> >
> >     uid=kurt,cn=gssapi,cn=auth
> This has been discussed at great length, read the -devel archives
> from 9 or 10 years ago. The fact is that the SASL specification
> doesn't reserve '@' as a special character and there is no guarantee
> that this is actually a realm separator. There are plenty of
> authentication mechanisms where '@' is an integral part of the
> username. This suggestion simply won't fly.

I suspect that's why canon_user was made pluggable in the first place; if
you have your own idea of what constitutes a username with or without a
realm then you code for it there.

In Cyrus-SASL, plugins/gssapi.c has it hard-coded:

        if (strchr((char *) name_token.value, (int) '@') != NULL) {
            /* cut off string at '@' */
            (strchr(name_without_realm.value,'@'))[0] = '\0';

I agree that non-Kerberos SASL mechanisms might have different username
formats; and hence, without additional configuration, OpenLDAP cannot split
arbitrary SASL usernames into uid=user,cn=realm.

However the current behaviour of olcSaslRealm isn't useful either, since it
just gives you uid=user[@realm],cn=olcSaslRealm.

I think the most appropriate behaviour for olcSaslRealm would be for it to
qualify usernames that don't contain '@' - and if your app doesn't use
usernames with '@', then don't set olcSaslRealm.

> I don't believe we have any freedom to make any code changes here;
> feel free to suggest verbiage changes for the documentation.

No problem. I propose the following to bring the docs in line with

--- sasl.sdf.orig	2011-01-03 09:45:55.754879001 +0000
+++ sasl.sdf	2011-01-03 10:07:34.808208000 +0000
@@ -135,25 +135,35 @@
 For the purposes of authentication and authorization, {{slapd}}(8)
 associates an authentication request DN of the form:
->	uid=<primary[/instance]>,cn=<realm>,cn=gssapi,cn=auth
+>	uid=<primary[/instance][@realm]>,cn=gssapi,cn=auth
+The realm is omitted by Cyrus SASL if it's equal to the default realm of the
+server in {{FILE:/etc/krb5.conf}}.
 Continuing our example, a user with the Kerberos principal
 {{EX:kurt@EXAMPLE.COM}} would have the associated DN:
->	uid=kurt,cn=example.com,cn=gssapi,cn=auth
+>	uid=kurt,cn=gssapi,cn=auth
 and the principal {{EX:ursula/admin@FOREIGN.REALM}} would have the
 associated DN:
->	uid=ursula/admin,cn=foreign.realm,cn=gssapi,cn=auth
+>	uid=ursula/admin@foreign.realm,cn=gssapi,cn=auth
-The authentication request DN can be used directly ACLs and
+The authentication request DN can be used directly in ACLs and
 {{EX:groupOfNames}} "member" attributes, since it is of legitimate
 LDAP DN format.  Or alternatively, the authentication DN could be
 mapped before use.  See the section {{SECT:Mapping Authentication
 Identities}} for details.
+If you configure olcSaslRealm then it is always inserted as an extra
+component in the authorization DN, regardless of the realm of the client.
+For example, if you set olcSaslRealm to {{EX:example.com}} then you will
+>	uid=kurt,cn=example.com,cn=gssapi,cn=auth
+>	uid=ursula/admin@foreign.realm,cn=example.com,cn=gssapi,cn=auth