Go to main content

Developer's Guide to Oracle® Solaris 11.3 Security

Exit Print View

Updated: April 2020
 
 

SASL for Service Providers

This section describes how to create plugins for providing mechanisms and other services to SASL applications.


Note - Due to export regulations, the Oracle Solaris SASL SPI does not support a security layer for non- Oracle Solaris client/server mechanism plugins. As a result, non- Oracle Solaris client/server mechanism plugins cannot offer integrity or privacy services. Oracle Solaris client/server mechanism plugins do not have this restriction.

SASL Plugin Overview

The SASL service provider interface (SPI) enables communication between plugins and the libsasl library. SASL plugins are typically implemented as shared libraries. A single shared library can one or more SASL plugins of different types. Plugins that are in shared libraries are opened dynamically by libsasl through the dlopen(3C) function.

Plugins can also be statically bound to an application that calls libsasl. These kinds of plugins are loaded through either the sasl_client_add_plugin() function or the sasl_server_add_plugin() function, depending on whether the application is a client or server.

    A SASL plugin in the Oracle Solaris OS has the following requirements:

  • A plugin in a shared library must be in a valid executable object file, preferably with the .so file extension.

  • The plugin must be in a location that can be verified. The SASL_CB_VERIFYFILE callback is used to verify plugins.

  • The plugin must contain the proper entry points.

  • The version of the plugin for the SASL client must match the version of the corresponding plugin for the SASL server.

  • The plugin needs to be able to be initialized successfully.

  • The binary type of the plugin must match the binary type for libsasl.

    SASL plugins fall into four categories:

  • Client mechanism plugin

  • Server mechanism plugin

  • Canonicalization plugin

  • Auxprop plugin

The sasl_client_init() function causes SASL clients to load any available client plugins. The sasl_server_init() function causes SASL servers to load the server, canonicalization, and auxprop plugins. All plugins are unloaded when sasl_done() is called.

To locate plugins, libsasl uses either the SASL_CB_GETPATH callback function or the default path. SASL_CB_GETPATH returns a colon-separated list of directories to be searched for plugins. If the SASL consumer specifies a SASL_CB_GETPATH callback, then libsasl uses the returned path for searching. Otherwise, the SASL consumer can use the default path that corresponds to the binary type.

    The following list shows the default path and binary type correspondence:

  • 64-bit SPARC architecture: /usr/lib/sasl/sparcv9

  • x64 architecture: /usr/lib/sasl/amd64

  • 32-bit SPARC architecture: /usr/lib/sasl

  • 32-bit x86 architecture: /usr/lib/sasl

As part of the loading process, libsasl calls the latest, supported version of the plugin. The plugin returns the version and a structure that describes the plugin. If the version checks out, libsasl loads the plugin. The current version number is SASL_UTILS_VERSION.

After a plugin has been initialized, subsequent communication between the plugin and libsasl takes place through structures that have to be established. Plugins use the sasl_utils_t structure to call libsasl.

    The libsasl library uses entry points in the following structures to communicate with plugins:

  • sasl_out_params_t

  • sasl_client_params_t

  • sasl_server_params_t

  • sasl_client_plug_t

  • sasl_server_plug_t

  • sasl_canonuser_plug_t

  • sasl_auxprop_plug_t

The source code for these structures can be found in the SASL header files. The structures are described in the following section.

Important Structures for SASL Plugins

    Communication between libsasl and plugins is accomplished through the following structures:

  • sasl_utils_t – The sasl_utils_t structure contains a number of utility functions, along with the three contexts.

    This structure contains a number of utility functions that serve as a convenience for plugin writers. Many of the functions are pointers to public interfaces in libsasl. Plug–ins do not need to call libsasl directly, unless for some reason the plugin needs to be a SASL consumer.

      libsasl creates three contexts for sasl_utils_t:

    • sasl_conn_t *conn

    • sasl_rand_t *rpool

    • void *getopt_context

    In some cases, such as loading plugins, the conn variable in sasl_utils_t is not actually associated with a connection. In other cases, conn is the SASL consumer's SASL connection context. The rpool variable is used for random number generation functions. getopt_context is the context that should be used with the getopt() function.

    See sasl_getopt_t(3SASL), sasl_log_t(3SASL), and sasl_getcallback_t(3SASL).

  • sasl_out_params_t – libsasl creates the sasl_out_params_t structure and passes the structure to mech_step() in the client or server. This structure communicates the following information to libsasl: authentication status, the authid, the authzid, maxbuf, the negotiated ssf, and information for encoding and decoding data.

  • sasl_client_params_t – The sasl_client_params_t structure is used by libsasl to pass the client state to a SASL client mechanism. The client mechanism's mech_new(), mech_step(), and mech_idle() entry points are used to send this state data. The canon_user_client() entry point also requires client state to be passed along.

  • sasl_server_params_t – The sasl_server_params_t structure performs a similar function to sasl_client_params_t on the server side.

Client Plugins

Client plugins are used to manage the client-side of a SASL negotiation. Client plugins are usually packaged with the corresponding server plugins. A client plugin contains one or more client-side SASL mechanisms. Each SASL client mechanism supports authentication, and optionally integrity and confidentiality.

    Each SASL client mechanism provides information about that mechanism's capabilities:

  • Maximum SSF

  • Maximum security flags

  • Plugin features

  • Callbacks and prompt IDs for using the plugin

Client plugins must export sasl_client_plug_init(). libsasl calls sasl_client_plug_init() to initialize the plugin for the client. The plugin returns a sasl_client_plug_t structure.

    The sasl_client_plug_t provides the following entry points for libsasl to call the mechanism:

  • mech_new() – The client starts a connection by calling sasl_client_start(), which uses mech_new(). mech_new() performs initialization that is specific to the mechanism. If necessary, a connection context is allocated.

  • mech_step()mech_step() can be called by sasl_client_start() and sasl_client_step(). mech_step() performs authentication on the client side after mech_new() has been called. mech_step() returns SASL_OK if authentication is successful. SASL_CONTINUE is returned if more data is required. A SASL error code is returned if authentication fails. If an error occurs, then seterror() is called. If the authentication is successful, mech_step() must return the sasl_out_params_t structure with the relevant security layer information and callbacks. The canon_user() function is part of this structure. canon_user() must be called when the client receives the authentication and authorization IDs.

  • mech_dispose()mech_dispose() is called when the context can be safely closed. mech_dispose() is called by sasl_dispose().

  • mech_free()mech_free() is called when libsasl shuts down. Any remaining global state for the plugin is freed by mech_free().

Server Plugins

Server plugins are used to manage the server-side of a SASL negotiation. Server plugins are usually packaged with the corresponding client plugins. A server plugin contains one or more server-side SASL mechanisms. Each SASL server mechanism supports authentication, and optionally integrity and confidentiality.

    Each SASL server mechanism provides information about that mechanism's capabilities:

  • Maximum SSF

  • Maximum security flags

  • Plugin features

  • Callbacks and prompt IDs for using the plugin

Server plugins must export sasl_server_plug_init(). libsasl calls sasl_server_plug_init() to initialize the plugin for the server. The plugin returns a sasl_server_plug_t structure.

    The sasl_server_plug_t structure provides the following entry points for libsasl to call the mechanism:

  • mech_new() – The server starts a connection by calling sasl_server_start(), which uses mech_new(). mech_new() performs initialization that is specific to the mechanism. If necessary, mech_new() allocates a connection context.

  • mech_step()mech_step() can be called by sasl_server_start() and sasl_server_step(). mech_step() performs authentication on the server-side after mech_new() has been called. mech_step() returns SASL_OK if authentication is successful. SASL_CONTINUE is returned if more data is required. A SASL error code is returned if authentication fails. If an error occurs, then seterror() is called. If the authentication is successful, mech_step() must return the sasl_out_params_t structure with the relevant security layer information and callbacks. The canon_user() function is part of this structure. canon_user() must be called when the server receives the authentication and authorization IDs. Calling the canon_user() function causes propctx to be filled in. Any required auxiliary property requests should be performed before the authentication is canonicalized. Authorization ID lookups are performed after the authentication is canonicalized.

      The mech_step() function must fill any related sasl_out_params_t fields before SASL_OK is returned. These fields perform the following functions:

    • doneflag – Indicates a complete exchange

    • maxoutbuf – Indicates maximum output size for a security layer

    • mech_ssf – Supplied SSF for the security layer

    • encode() – Called by sasl_encode(), sasl_encodev(), and sasl_decode()

    • decode() – Called by sasl_encode(), sasl_encodev(), and sasl_decode()

    • encode_context() – Called by sasl_encode(), sasl_encodev(), and sasl_decode()

    • decode_context() – Called by sasl_encode(), sasl_encodev(), and sasl_decode()

  • mech_dispose()mech_dispose() is called when the context can be safely closed. mech_dispose() is called by sasl_dispose().

  • mech_free()mech_free() is called when libsasl shuts down. Any remaining global state for the plugin is freed by mech_free().

  • setpass() sets a user's password. setpass() enables a mechanism to have an internal password.

  • mech_avail() is called by sasl_listmech() to check if a mechanism is available for a given user. mech_avail() can create a new context and thus avoid a call to mech_new(). Use this method to create a context as long as performance is not affected.

User Canonicalization Plugins

A canonicalization plugin provides support for alternate canonicalization of authentication and authorization names for both the client and server-side. The sasl_canonuser_plug_init() is used to load canonicalization plugins.

    A canonicalization plugin has the following requirements:

  • The canonicalized name must be copied to the output buffers.

  • The same input buffer can be used as an output buffer.

  • A canonicalization plugin must function in cases where only authentication IDs or authorization IDs exist.

User canonicalization plugins must export a sasl_canonuser_init() function. The sasl_canonuser_init() function must return sasl_canonuser_plug_t to establish the necessary entry points. User canonicalization plugins must implement at least one of the canon_user_client() or canon_user_server() members of the sasl_canonuser_plug_t structure.

Auxiliary Property (auxprop) Plugins

Auxprop plugins provide support for the lookup of auxiliary properties for both authid and authzid for a SASL server. For example, an application might want to look up the user password for an internal authentication. The sasl_auxprop_plug_init() function is used to initialize auxprop plugins and returns the sasl_auxpropr_plug_t structure.

To implement an auxprop plugin successfully, the auxprop_lookup member of the sasl_auxprop_plug_t structure must be implemented. The auxprop_lookup() function is called after canonicalization of the user name, with the canonicalized user name. The plugin can then do any lookups that are needed for the requested auxiliary properties.


Note - Oracle Corporation does not currently provide auxprop plugins.

SASL Plugin Development Guidelines

This section provides some additional pointers for developing SASL plugins.

Error Reporting in SASL Plugins

Good error reporting can help in tracking down authentication problems and in other debugging. Developers of plugins are encouraged to use the sasl_seterror() callback in the sasl_utils_t structure to supply detailed error information for a given connection.

Memory Allocation in SASL Plugins

The general rule for allocating memory in SASL is to free any memory that you have allocated when that memory is no longer needed. Following this rule improves performance and portability, and prevents memory leaks.

Setting the SASL Negotiation Sequence

    A plugin mechanism can set the order in which a client and server conduct a SASL conversation through the following flags:

  • SASL_FEAT_WANT_CLIENT_FIRST – The client side begins the interchange.

  • SASL_FEAT_WANT_SERVER_LAST – The server sends the final data to the client.

If neither flag is set, the mechanism plugin sets the order internally. In this case, the mechanism must check both the client and server for data that needs to be sent. Note that the situation where the client sends first is only possible when the protocol permits an initial response.

The case in which the server sends last requires that the plugin set *serverout when the step function returns SASL_OK. Those mechanisms that never have the server send last must set *serverout to NULL. Those mechanisms that always have the server send last need to point *serverout to the success data.