Subject: Request change to section 4 of sasl plain

From: Sam Hartman <[email-masked]>
Date: Wed, 06 Apr 2005 07:42:31 -0400

I'm still not really happy with section 4 for the plain draft.  My
concerns still have to do with the framework implementation case.  One
way of addressing my concerns is to say that the code in section 4
does not apply to that case; the first paragraph makes it clear that
section 4 is not normative.

However I think there's a simple fix that would make me happy:
explicitly separate out the step of preparing the authorization ID.

1) Rename DeriveAuthzid to DerivePreparedAuthzid

2)   AD an else branch to the authzid == null case that calls a new function PrepareAuthzid

3) Pass a prepared authzid into authorize

Naturally PrepareAuthzid will be application profile or application
implementation specific.

My concern is that in the framework case I do not believe it is
generally true that the type of the output of DeriveAuthzid can be the
same domain as the type of an authzid received from the client.  For
example on Windows I'd expect DerivePreparedAuthzid to give you back a
list of sids where as the authzid coming from the client will be
application protocol specific.  

--Sam

---

From: "Kurt D. Zeilenga" <[email-masked]>
Date: Wed, 06 Apr 2005 20:30:44 -0700

>concerns still have to do with the framework implementation case.

As the framework is just an abstraction layer, and most every
implementation has some sort of abstraction layer between
mechanism(s) and the application protocol(s), the term
"the framework implementation" doesn't lend itself to
anything particular case.

My guess is that you are trying to pseudo-code reflects that
it possible to implement the Authorize function without
application protocol specific knowledge.  If so, this will
be quite problematic.

Before I comment on your suggestion, let me present pseudo-code
which, I think, is consistent with your suggestion.

boolean Verify(string authzid, string authcid, string passwd) {
  string pAuthcid = SASLprep(authcid, true); # prepare authcid
  string pPasswd = SASLprep(passwd, true);   # prepare passwd
  string pAuthzId;

  if (pAuthcid == NULL || pPasswd == NULL) {
    return false;     # preparation failed
  }
  if (pAuthcid == "" || pPasswd == "") {
    return false;     # empty prepared string
  }

  storedHash = FetchPasswordHash(pAuthcid);
  if (storedHash == NULL || storedHash == "") {
    return false;     # error or unknown authcid
  }

  if (!Compare(storedHash, Hash(pPasswd))) {
    return false;     # incorrect password
  }

  if (authzid == NULL ) {
    pAuthzid = DerivePreparedAuthzidFromPreparedAuthcid(pAuthcid);
    if (pauthzid == NULL) {
        return false; # could not derive/prepare                  
    }
  } else {
    pAuthzid = PrepareAuthzId(authzid);
    if (pauthzid == NULL) {
        return false; # could not prepare                   
    }
  }

  if (!Authorize(pAuthcid, pauthzid)) {
    return false;     # not authorized
  }

  return true;
}

I have a number of problems with this.

1) I think this text does not line up well with the prose of
Section 3, nor prose of RFC 2222 nor RFC 2222bis.

2) This assumes that there that there is a protocol defined
preparation function and that this function is applied on the server
side.  It might also be taken to imply that the output of the
preparation function is canonical.  None of which the base
specification requires.  What if the authzid is a
"dn:uid=Kurt,dc=example,dc=com" (e.g., an LDAP dnAuthzId)?
To assume use a server side string preparation matching approach
is, IMO, inappropriate as it will lead to implementations
which cannot support protocols which utilize other matching
approaches.

3) It seems what you are trying to make it so that Authorize()
function require no knowledge of the form of authzid
used by the application level protocol, but it still does.
Say protocol X says authzid is a UTF-8 string which is to
folded to lowercase for matching.  Say protocol Y says
the authzid is a UTF-8 string which is not to be case
folded.  So in X, "joe","JOE" -> "joe" but in Y "joe"->
"joe" and "JOE" -> "JOE".   So, even where the preparation
produces a canonical representation, the Authorize function
still needs to know whether additional transformation are
necessary.  And, to make matters worse, who's to say
identities in X and identities in Y are in the same name
space.  That is, say Z is like X excepting its identities
are in a different name space.

I'd like you to consider another variant:

boolean Verify(string authzid, string authcid, string passwd) {
  string pAuthcid = SASLprep(authcid, true); # prepare authcid
  string pPasswd = SASLprep(passwd, true);   # prepare passwd
  string pAuthzId;

  if (pAuthcid == NULL || pPasswd == NULL) {
    return false;     # preparation failed
  }
  if (pAuthcid == "" || pPasswd == "") {
    return false;     # empty prepared string
  }

  storedHash = FetchPasswordHash(pAuthcid);
  if (storedHash == NULL || storedHash == "") {
    return false;     # error or unknown authcid
  }

  if (!Compare(storedHash, Hash(pPasswd))) {
    return false;     # incorrect password
  }

  if (authzid == NULL ) {
    authzid = DeriveAuthzidFromPreparedAuthcid(pAuthcid);
    if (authzid == NULL || authzid == "") {
        return false; # could not derive authzid              
    }
  }

  pAuthzid = PrepareAuthzId(authzid);
  if (pauthzid == NULL) {
        return false; # could not prepare authzid
  }

  if (!Authorize(pAuthcid, pauthzid)) {
    return false;     # not authorized
  }

  return true;
}

Note there that PrepareAuthzId doesn't "genericize" the authzId,
it only prepares the string representation of the authzid
for matching (possibly a no-op).  That is, your suggestion doesn't
actually resolve the problem you are apparently trying to solve.
Authorize() still has to deal with application-specific,
implementation-specific, or local semantics.  There is no
        genericAuthzId = genericize( protocol, pAuthzid )
nor any:
        genericAuthcId = genericize( mechanism, pAuthcid );

>same domain as the type of an authzid received from the client.

I generally assume that for any authcid there is some application
protocol specific authzid that the client could have asserted
and gotten the same authorization identity as would have been
derived by the server in absence of an authzid.  Why do you think
this is not generally the case?

>For

Wouldn't the client, asserting an authzid, also get in the
end a list of sids for authorization in the server?  And if so,
cannot the server first map authcid to the authzid that produces
the right list of sids?  Why would the server do this mappping
in two places?  Wouldn't that lead to issues where the
authcid->authzid (note applications protocols can specify
relations between authcid and authzid, for instance, in detailing
how identity assumption policy is represented in the protocol) but
authcid->XlistOfSids and authzid->YlistOfSids?

Kurt

---

From: "Kurt D. Zeilenga" <[email-masked]>
Date: Wed, 06 Apr 2005 20:30:44 -0700

if (authzid == NULL ) {

  return true;
}

  if (authzid == NULL ) {

  return true;
}

Kurt