Subject: Comments on the SASL C API

From: Simon Josefsson <[email-masked]>
Date: Sun, 29 Jun 2003 19:15:31 +0200

This message contains a description of a problem in the proposed API,
and a proposed extension for the API.  The extension allows moving
session contexts between processes, or even machines in a clustered
environment.  The rationale for the extension is included in the
proposal below.  Both are taken from old personal communication, but
as I don't know if the problem has a solution, and to gage interest in
the extension, I'd like to mention them in public.

Problem:

When I considered supporting the API, I discovered the following
problem.  Recall from 4.1:

int sasl_decode(sasl_conn_t *conn,
            const char *input,
            unsigned int inputlen,
            const char **output,
            unsigned int *outputlen);

      This function decodes a block of data received using security
      layer (if any). The output and outputlen are filled in with the
      decoded data and its length respectively. If there is no security
      layer the input buffer is returned in the output. Otherwise, the
      output is only valid until a next call to sasl_decode or sasl_dis-
      pose.

Consider what happens if the input/inputlen fields of the
sasl_decode() contains two tokens from sasl_encode(), or one SASL
token and a few bytes of the next SASL token.  If I understand the
specification correctly, the application have no way of splitting up
incoming data as separate SASL tokens, so it must simply pass on
whatever is returned from read() to sasl_decode().  If the server
always sends entire SASL tokens using one write(), it will probably
work, but if the server concatenates two sasl_encode() output tokens
and sends them, or if it buffers them somehow, the client will be
confused.

If this isn't a problem in sasl-c-api (I haven't read it carefully),
please point me to how it is handled.

The solution I would suggest is to replace "int inputlen" with "int
*inputlen" and have it populate the output variable with how many
bytes it actually used from the input buffer, so the application can
use the remaining bytes (possibly concated with new incoming data) and
call the function again.  This require that mechanisms are able to
figure this information out, and looking at e.g. DIGEST-MD5 I'm not
sure this is the case.  How does DIGEST-MD5 decode() know that a SASL
encode token ends?  Some framing seem to be required.  I may be
missing something, even though I have implemented this, and can't
recall how this problem is solved in my implementation.

Extension:

Here is a writeup on an idea I have received.  It might cause some
complications and likely needs more pondering.  However, if the
rationale for adding this makes sense to you, I'd appreciate if you
spent the time incorporating the functions.  If you are familiar with
the GSS-API, compare this to the gss_export_sec_context and
gss_import_sec_context functions that were added to GSS-API version 2
(see RFC 2744).

 5.2.   Exporting and importing security layer state

      Some application environments wishes to separate the initial and
      short authentication step from the potentially long lived
      application step, where the application uses sasl_encode() and
      sasl_decode() to transfer application data.  This separation may
      be between processes (separation across hosts is more
      complicated, as the security layer state is often dependent on
      the host address, but still conceivable).  The reason for such
      separation may be to guarantee that long-term secrets (e.g.,
      passwords) cannot be recovered by compromising the application
      that handles existing session.

      The functions defined in this section allows the application to
      export the security layer state associated with a session as an
      opaque character string, and later import the opaque character
      string.  The opaque state typically contains an encryption key,
      initialization vectors, what encryption algorithm to use, packet
      number etc.

  5.2.1. sasl_export_security_layer_state function

Arguments:
            sasl_conn_t *conn,
            const char **state,
            unsigned int *statelen

Results:
            SASL_OK        -- Success
            SASL_NOTDONE   -- Security layer negotiation not finished

      This function exports the security layer state required to
      implement the sasl_encode() and sasl_decode() functions after
      the state has been imported.  The security layer state is an
      opaque data string.  The state and statelen are filled in with
      the opaque state data and its length respectively. If the
      security layer negotiation has not finished, SASL_NOTDONE is
      returned.  After calling this function, the application should
      treat the SASL session as concluded and call sasl_dispose().  In
      particular, the application MUST NOT call sasl_encode() or
      sasl_decode() [XXX sasl_dencode should simply return a error
      code when it happens].  The state data is only valid until a
      call to sasl_dispose(), and should be copied before calling
      sasl_dipose() if necessary.

  5.2.2. sasl_import_security_layer_state function

Arguments:
            const char *state,
            unsigned int statelen
            sasl_conn_t **conn,

Results:
            SASL_OK        -- Success
            SASL_BADPARAM  -- Invalid parameter supplied

      This function create a SASL session, for use with sasl_encode(),
      sasl_decode() and sasl_dispose() only, from the given security
      layer state.  The security layer state is an opaque data string,
      as returned from sasl_export_security_layer_state().

Thanks,
Simon

---

From: Greg Hudson <[email-masked]>
Date: Sun, 29 Jun 2003 15:36:22 -0400

> token and a few bytes of the next SASL token.

Presumably it is the responsibility of the sasl_decode()
implementation to remember (using state associated with conn) the
partial bytes.  If there are two or more tokens in input, the
resulting output tokens can presumably be concatenated together; I
don't think applications are supposed to rely on knowing anything
about the security later's framing.

> This require that mechanisms are able to figure this information
> out, and looking at e.g. DIGEST-MD5 I'm not sure this is the case.
> How does DIGEST-MD5 decode() know that a SASL encode token ends?
> Some framing seem to be required.  I may be missing something, even
> though I have implemented this, and can't recall how this problem is
> solved in my implementation.

RFC 2222 states:

   Once the security layer is in effect, the protocol stream is
   processed by the security layer into buffers of cipher-text.  Each
   buffer is transferred over the connection as a stream of octets
   prepended with a four octet field in network byte order that
   represents the length of the following buffer.