Subject: Global state and libsasl

From: Jeff Moore <[email-masked]>
Date: Tue, 15 Jul 2003 08:25:12 -0700

As has been previously discussed, libsasl maintains global state
which can cause problems when both an application and one of its
libraries use libsasl.

The most recent suggestions on how to deal with this issue in a
compatible way do not seem to address all the issues with global
state in the Cyrus libsasl implementation.

The Cyrus 2.x libsasl implementation uses global state for the
following:

1. Loading plugins
        If an application decides which plugins to load and how to
        configure them, any other consumers of libsasl may not not
        have their needs met. Additionally, some plugins may be
        specific to a given application or library. They may not
        want the plugin to be used by other components.

        There are a number of ways there could be problems:

        a. Location of plugins (SASL_CB_GETPATH)
                Through the SASL_CB_GETPATH callback. If this callback
                is specified, libsasl will use the specified path to
                locate plugins. The specified path may not include the
                standard plugins - or it may include "private" plugins.
        b. Verification of plugins/configuration (SASL_CB_VERIFYFILE)
                Through the SASL_CB_VERIFYFILE callback. What one
                component may accept another might reject.
        c. logging of non-connection oriented problems (SASL_CB_LOG)
                 libraries will not be able to report these sorts of
                 problems themselves.
        d. Setting any global plugin configuration data:
                e.g. DIGEST-MD5 - reauth_timeout,
                     GSSAPI - keytab
                     KERBEROS_4 - srvtab
                     SRP - srp_mda
                The way to set these today is via the getopt function
                registered in the global callback. Also note that for
                things like reauth_timeout, you may not want to share
                it between different components.
        e. Loading application/library specific plugins
                Any plugin that is loaded via sasl_xxx_add_plugin will be
                available to application or libraries.
        f. Loading application/library private plugins

2. memory allocation
        sasl_set_alloc() setting memory allocation functions may have
        unintended interactions between application and libraries. If
        the library has already used libsasl to perform some memory
        allocations before the application has set sasl_set_alloc(),
        bad things may happen.
        
3. mutex functions
        sasl_set_mutex() setting mutex allocation functions may have
        unintended interactions between applications and libraries. If
        this function is a MUST, what should a library do? 

4. sasl_xxx_init() and sasl_done()
        Count of usage may deal with this - although no separation
        between client and server and potential for misbehaving
        applications/libraries to break others.

5. Global callbacks
        If specified, they will apply to both application and libraries.
        The application's global callbacks may end up handling callbacks
        for the library. If, as has been proposed, that libraries should
        not use global callbacks, the application will not see this
        problem.

6. Server sasl option configuration file
        Just as with global callbacks, options may be set for library that
        are not expected or wanted.

7. Requirement of non-thread-safe initialization.

Ideally, we would all like to see the global state problem be solved
with a minimum of changes to the existing semantics. One way to address
much of the above is to introduce a sasl global context that would be
inferred by the current sasl API, but which would allow libraries (or
applications) to use libsasl independently of each other. (There are
additional issues for sasl_set_alloc() and sasl_set_mutex().)

This approach would introduce a  number of new functions:

void *sasl_create_context(void)

Create a sasl global context - the current global state would be now
associated with the created context.

void sasl_free_context(void *ctx)

Release all resources associated with the sasl global context. This would
be semantically the same as the current sasl_done().

int _sasl_client_init(void *ctx, const sasl_callback_t *callbacks);

Initialize the sasl global context for sasl client access.

int _sasl_client_new(void *ctx, const char *service, ...)

Create a sasl client connection using the sasl global context.

int _sasl_server_init(void *ctx, const sasl_callback_t *callbacks, ...);

Initialize the sasl global context for sasl server access.

int _sasl_server_new(void *ctx, const char *service, ...)

Create a sasl server connection using the sasl global context.

int _sasl_client_add_plugin(void *ctx, const char *plugname, ...);
int _sasl_server_add_plugin(void *ctx, const char *plugname, ...);
int _sasl_canonuser_add_plugin(void *ctx, const char *plugname, ...);
int _sasl_auxprop_add_plugin(void *ctx, const char *plugname, ...);

Add plugins to the sasl global context

The functions sasl_set_mutex() and sasl_global_listmech() would be
deprecated.

If sasl_set_alloc() is not deprecated, then _sasl_set_alloc(void *ctx, ...)
could be defined.

This approach would preserve the existing semantic and avoid: global
state issues when a library initializes the libsasl before the
application, the potential problem of applications getting callbacks for
libraries and plugins installed by one component being available for use
by other components.

Cheers,

Jeff