I've been doing some analysis of the Cyrus SASL code.
When you say "we're using the parameters as documented by Cyrus", I presume the documentation you refer to is the comments within the source, in which case this all hinges on one line in include/sasl.h:
* user_realm -- the user realm (may be NULL in case of client)
(in the parameter list for the SASL_CB_CANON_USER callback). So what's meant by "the user realm"?
When the callback is actually made (in lib/canonusr.c), the value passed is sconn->user_realm. This appears to be the value you get from sasl_getprop() with SASL_DEFUSERREALM.
The user_realm value can be set in sasl_server_new (lib/server.c) and sasl_setprop (lib/common.c).
There is a further description of realms in sasl/include.h:
* IMPORTANT NOTE: server realms / username syntax * ... A single server may support multiple realms. If the * server knows the realm at connection creation time (e.g., a server * with multiple IP addresses tightly binds one address to a specific * realm) then that realm must be passed in the user_realm field of * the sasl_server_new call. 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.
That is: it seems to me fairly clear that the purpose of user_realm is to provide a *default* realm which can be appended to the username when canonicalizing it.
As a specific example, the code in _canonuser_internal() appends @<user_realm> if the username doesn't contain '@'.
Furthermore, it's also clear from this that the username (SASL_USERNAME) is intended to include '@' and a realm; Cyrus is not designed to split it into a username part and a realm part. This matches what I see when using Cyrus SASL's standalone "sample-server" and "sample-client" programs, with GSSAPI and a client with a kerberos ticket from a different realm to the server:
... Negotiation complete Username: student@REALM3.WS.NSRC.ORG << sasl_getprop SASL_USERNAME Realm: (NULL) << sasl_getprop SASL_DEFUSERREALM SSF: 56 ...
There's a full transcript of this example at http://lists.andrew.cmu.edu/pipermail/cyrus-sasl/2010-December/002177.html
Next, looking at plugins/gssapi.c: AFAICS it doesn't make use of user_realm, but does ask GSSAPI if the name without realm is the same as the name with realm, and if so returns the name with the realm stripped off.
/* If the id contains a realm get the identifier for the user without the realm and see if it's the same id (i.e. tmartin == tmartin@ANDREW.CMU.EDU. If this is the case we just want to return the id (i.e. just "tmartin" */
So, there is code in the GSSAPI plugin to *strip* the realm if it's the same as the default, but I don't think there's code to *separate* the username from the realm if the realm is foreign.
Putting all this together: it seems to me that the "user_realm" you see is really just the configured default realm for the session; and that when you query SASL_USERNAME for a Kerberos client, you will see "username" if it's in the default realm, and "username@foreign.realm" for other realms. And this matches what I observe when testing this in OpenLDAP.
As you suggested, I did bring this up on the cyrus-sasl mailing list (archive URL above), but no replies have been forthcoming so far.
Now, if my understanding of the user_realm parameter is correct, I think there are two ways to make OpenLDAP's behaviour consistent with its documentation.
(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.
I think it would make more sense, if olcSaslRealm is present, to use it to *qualify* usernames which don't have a realm.
uid=kurt,cn=gssapi,cn=auth ---> uid=kurt@<olcsaslrealm>,cn=gssapi,cn=auth
i.e. change the canonicalize function to append @user_realm if the username doesn't contain '@'.
This would be useful if you want to undo the Cyrus SASL GSSAPI behaviour of stripping off the default realm.
(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
Regards, Brian.