Quick-SASL  0.11.3
Data Structures | Typedefs | Functions | Variables
Quick and Easy SASL backends over Diameter.

Data Structures

struct  diasasl_session
 
struct  diasasl_node_t
 TCP connection to a DiaSASL serving backend node. More...
 
struct  diasasl_session._opaque
 
struct  diasasl_node_t._opaque
 

Typedefs

typedef void(* diasasl_callback_t) (struct diasasl_session *session, const int32_t *com_errno, char *opt_mechanism_list, uint8_t *s2c_token, uint32_t s2c_token_len)
 Callback prototype for event-driven notifications. More...
 

Functions

void diasasl_node_open (struct diasasl_node *node)
 Initialise a diasasl_node_t. More...
 
void diasasl_node_close (struct diasasl_node *node)
 Finalise a diasasl_node_t. More...
 
void diasasl_open (struct diasasl_node *node, struct diasasl_session *new_session, char *server_domain, char *opt_svcproto)
 Open a SASL authentication session on a diasasl_node_t. More...
 
void diasasl_close (struct diasasl_node *node, struct diasasl_session *session)
 Close a SASL authentication session. More...
 
static const char * diasasl_sessionid (struct diasasl_session *session)
 Retrieve the Session-Id string used by DiaSASL. More...
 
void diasasl_break (struct diasasl_node *node, int32_t com_errno)
 Disrupt each diasasl_sesssion for a diasasl_node. More...
 
int32_t diasasl_process (struct diasasl_node *node, bool just_one)
 Process any data returned from the local/trusted Diameter node by invoking callbacks. More...
 
void diasasl_send (struct diasasl_node *node, struct diasasl_session *session, char *opt_mechanism_choice, uint8_t *c2s_token, uint32_t c2s_token_len)
 Send a step in a SASL authentication session. More...
 
int32_t diasasl_export (struct diasasl_node *node, struct diasasl_session *session, uint8_t **export_s2s_token, uint32_t *export_s2s_token_len)
 Export a SASL authentication session's state. More...
 
int32_t diasasl_import (struct diasasl_node *node, struct diasasl_session *new_session, uint8_t *import_s2s_token, uint32_t import_s2s_token_len)
 Import previously exported SASL authentication session state back into the same API. More...
 
static void diasasl_mechfilter (char *mechlist_filtered, const char *mechlist_filter)
 
void diasasl_mechinfo (const char *mech_name, const uint8_t *c2s0, uint32_t c2s0len, char **realm, uint32_t *realmlen, char **cbtyp, uint32_t *cbtyplen, bool *fit4plain)
 

Variables

struct diasasl_session diasasl_session_t
 

Detailed Description

This API is intended for pass-through SASL authentication, which is very much simpler than actually interfacing with a SASL library as a server endpoint.

These calls hand off SASL to a local/trusted Diameter node, through a simple local protocol. This node may be central to a site, serving multiple of these services with the same authentication logic. The node may employ Realm Crossover to welcome visitors from other domains that bring their own identity as established by their own domain. This idea of Bring Your Own IDentity (BYOID) serves their control over their online identity, and saves the server from managing accounts (and credentials...) and leaves the choice of a security level to the client.

Aside from Realm Crossover, support is also available for locally hosted domains, where the local/trusted Diameter node would serve as a "virtual server" for one or more of such domains. Combinations may also be used. In general, the choice of mechanisms is setup in the local/trusted Diameter node for a server domain; the interaction starts by indicating that server domain to extract SASL mechanisms that the client may use. Realm Crossover redirects to a client domain that may or may not match the server domain.

Authentications with Realm Crossover always yield identities of the form user@.nosp@m.doma.nosp@m.in.na.nosp@m.me. The user part is established by a remote server which our local/trusted node validates as being authoritative for the domain.name. Local accounts may also be supported through SASL mechanisms that do not add Realm Crossover. Be careful when permitting the same syntax user@.nosp@m.doma.nosp@m.in.na.nosp@m.me for local validations.

SPDX-License-Identifier: BSD-2-Clause Author: Rick van Rein rick@.nosp@m.open.nosp@m.fortr.nosp@m.ess..nosp@m.nl Author: Henri Manson info@.nosp@m.mans.nosp@m.oft.n.nosp@m.l


Data Structure Documentation

◆ diasasl_session

struct diasasl_session

DiaSASL authentication session structure.

This structure is to be partially filled by the program and partially to be treated as though it were opaque.

Clients of this API allocate memory and initialise it to zero. This is done with mem_alloc(). The opaque parts are filled during diasasl_open() and zeroed by a matching call to diasasl_close(), which must precede mem_free() or mem_recycle() calls.

It is customary for clients to wrap the diasasl_session into a larger whole that includes private data for their own use.

The diasasl_session travels to/over a diasasl_node.

Collaboration diagram for diasasl_session:
Data Fields
struct diasasl_session _opaque
diasasl_callback_t callback
uint8_t * chanbind
uint32_t chanbind_len
char * client_domain
char * client_userid

◆ diasasl_node_t

struct diasasl_node_t

TCP connection to a DiaSASL serving backend node.

This structure is to be partially setup by the program, and partially to be considered as though it were opaque.

Applications usually have one for their local/trusted Diameter node. Setup the socket with the TCP link to that node and let head be empty.

One diasasl_node may carry the traffic for many diasasl_session.

Data Fields
struct diasasl_node_t _opaque
int socket
uint32_t trunk
bool trunk_set

◆ diasasl_session._opaque

struct diasasl_session._opaque
Data Fields
ripple_ref next
char * sessionid
uint8_t * state
uint32_t state_len

◆ diasasl_node_t._opaque

struct diasasl_node_t._opaque
Data Fields
ripple_ref head

Typedef Documentation

◆ diasasl_callback_t

typedef void(* diasasl_callback_t) (struct diasasl_session *session, const int32_t *com_errno, char *opt_mechanism_list, uint8_t *s2c_token, uint32_t s2c_token_len)

Callback prototype for event-driven notifications.

Whenever a message arrives, it is brought to the client through a callback. This is may be done immediately when an attempt to send a request fails, or otherwise while the client calls the diasasl_process() function.

The callback function receives the following bits of data, all of which is gone after the callback returns.

First, whether an error occurred. The com_errno value is 0 for correct progression, or another value for failure. The value NULL indicates that there was no final com_err value and processing should continue.

Second, the response to diasasl_open() mentions a list of mechanisms. This does not happen for callbacks in response to diasasl_send().

The SASL token may carry data, be empty or absent. The absent case is recognised through a NULL pointer, unlike empty or data-carrying situations. Empty simply has a lenth 0. Note that tokens are binary and may contain byte values like 0x00 or 0xff.

What the final identity is, and whether SASL is done or fails, is derived by the client-side SASL library.

The callback should unlink the diasasl_session if it is done, which includes the error condition. This is done with diasasl_close() or diasasl_export() functions.

Note that callbacks are not provided with a pointer to private data; instead, their allocation of diasasl_session allows them to wrap it into a larger structure that adds any desired private data fields.

Function Documentation

◆ diasasl_break()

void diasasl_break ( struct diasasl_node *  node,
int32_t  com_errno 
)

Disrupt each diasasl_sesssion for a diasasl_node.

This is done by sending an error message to each. This triggers the same reactions in callbacks as a failed Diameter response would.

◆ diasasl_close()

void diasasl_close ( struct diasasl_node *  node,
struct diasasl_session session 
)

Close a SASL authentication session.

This removes it from the diasasl_node. When the session has already presented a final-comerr, it no longer exists in the identity node; but when this call is made before final-comerr, a signal is sent to the identity node to terminate this session.

Upon return, the _opaque parts of diasasl_session are reset to a state suited for another diasasl_open() call. Other parts are not modified to be supportive of mem_recycle() for reuse of the structure in another authentication round.

◆ diasasl_export()

int32_t diasasl_export ( struct diasasl_node *  node,
struct diasasl_session session,
uint8_t **  export_s2s_token,
uint32_t *  export_s2s_token_len 
)

Export a SASL authentication session's state.

This can be used for stateless protocols such as HTTP, to avoid storing state in the protocol. To facilitate such protocols securely, this information can be passed in plain text.

After this call, the SASL session has been closed as though diasasl_close() had been called; this may be used with mem_recycle() to secure reuse the memory.

The export token is made with mem_alloc() so it is cleared when the diasasl_session context is. Note that the export token is binary, so it may contain special byte values like 0x00 or 0xff, and it may be empty. It will not be absent.

The return value is a com_errno like they are also sent to callback functions, so 0 indicates success for the export. When not 0, nothing has effectively happened.

Use of this function may involve linking an extra library and its dependencies.

◆ diasasl_import()

int32_t diasasl_import ( struct diasasl_node *  node,
struct diasasl_session new_session,
uint8_t *  import_s2s_token,
uint32_t  import_s2s_token_len 
)

Import previously exported SASL authentication session state back into the same API.

This can be used for stateless protocols such as HTTP, to recover state that was dropped for reasons of statelessness.

Before this call, the SASL session must be closed and this call will cause the effect of diasasl_open() and any exported state will also be recovered.

The API client may discard the export token as soon as this call returns. Note that tokens are imported from a binary format, including special byte values like 0x00 or 0xff, and that it may be empty but not absent.

The return value is a com_errno like they are also sent to callback functions, so 0 indicates success for the import. When not 0, nothing has effectively happened.

Use of this function may involve linking an extra library and its dependencies.

◆ diasasl_mechfilter()

static void diasasl_mechfilter ( char *  mechlist_filtered,
const char *  mechlist_filter 
)
inlinestatic

Compose two mechanism lists, such that the output mentions only those mechanisms that occur on both.

The lists are space-separated and NUL-terminated.

The filtered mechanism list will change in-place by removing mechanisms, but otherwise kept in order. The filter mechanism list works to constrain the mechanisms kept in the filtered list.

This function does not fail, but it might return an empty list as a sign that no overlap existed.

This is currently an inline function because it is usually only needed in one place. This saves a separate object to link against in the interest of minimal code size.

◆ diasasl_mechinfo()

void diasasl_mechinfo ( const char *  mech_name,
const uint8_t *  c2s0,
uint32_t  c2s0len,
char **  realm,
uint32_t *  realmlen,
char **  cbtyp,
uint32_t *  cbtyplen,
bool *  fit4plain 
)

Inform a mechanism-unaware program about pass-through aspects of a SASL mechanism and first token before deciding whether or how to pass it on.

Given a SASL mechanism name and a first C2S token, determine (1) what client realm it is addressing (NULL if not applicable) (2) what channel binding type it uses (3) whether it can be revealed in plaintext to intermediates

Return a finding in all cases, being cautious with unknown SASL mechanisms. When the c2s0 token is not provided, this function will be able to extract fit4plain, but not strings.

This function is not part of the normal XoverSASL library, and it does not target SASL end points. Instead, it is meant to help intermediate elements in the Realm Crossover chain. This is why it may link separately.

◆ diasasl_node_close()

void diasasl_node_close ( struct diasasl_node *  node)

Finalise a diasasl_node_t.

Call this for cleanup.

◆ diasasl_node_open()

void diasasl_node_open ( struct diasasl_node *  node)

Initialise a diasasl_node_t.

Call this before any diasasl_open() invocation.

◆ diasasl_open()

void diasasl_open ( struct diasasl_node *  node,
struct diasasl_session new_session,
char *  server_domain,
char *  opt_svcproto 
)

Open a SASL authentication session on a diasasl_node_t.

The structures are managed by the client. The diasasl_session is a context returned from mem_alloc() so its opaque part is zeroed. It is supported to wrap the diasasl_session into a larger structure. The mem() facilities will be used for other allocation of memory under this SASL session, and that can also be done by the API client.

The opt_svcproto can be a "service name" as it appears before a slash in the Kerberos PrincipalName; this is often a protocol name, like "imap", in lowercase. One exception is "HTTP", in uppercase, which has become mainstream without adherence to this naming practice.

If the trunk_set flag in the node is true, then its trunk value will be sent along too. This value designates a long-term relationship between a client and server, and may be manually assigned. If the node structure is initialised with zero bytes then this behaviour will be disabled.

The callback is called with either an error or a list of mechanisms. The mechanism list will be specific to the server_domain as provided by the local/trusted Diameter node. Note that the callback function must be set before calling.

◆ diasasl_process()

int32_t diasasl_process ( struct diasasl_node *  node,
bool  just_one 
)

Process any data returned from the local/trusted Diameter node by invoking callbacks.

This function is used to control when the callback function may be called; callbacks do not arrive asynchronously. When no data is available from the node, nothing will be done. When multiple answers are available, they will all be handled, except when the just_one flag is set, in which case at most one is handled.

The return value is a com_errno and is 0 for success. If an attempted read returns EAGAIN or EWOULDBLOCK, which is regularly the case on a non-blocking socket, it returns 0 for success, so this temporary setback is not fatal. All remaining non-0 return values are basically fatal and may lead to a reconnection attempt or may be handled by calling diasasl_break() to stop outstanding requests.

◆ diasasl_send()

void diasasl_send ( struct diasasl_node *  node,
struct diasasl_session session,
char *  opt_mechanism_choice,
uint8_t *  c2s_token,
uint32_t  c2s_token_len 
)

Send a step in a SASL authentication session.

This implies a request for the callback function to be invoked for the answer. All data may be removed when this call returns.

The first step must supply the mechanism choice, no later steps may supply it. This step will send channel binding information when it has been setup.

The SASL token may be non-NULL and then have any length including 0, or it may be set to NULL with any length to indicate absense of a SASL token. Note that SASL requires distinct handling of empty tokens from absent tokens, which is hereby addressed. Als note that a SASL token is a binary value, which include bytes with values like 0x00 or 0xff. Your application may need to encode this into a textual form before it can further process a SASL token.

◆ diasasl_sessionid()

static const char* diasasl_sessionid ( struct diasasl_session session)
inlinestatic

Retrieve the Session-Id string used by DiaSASL.

The return value is NULL when the session is not currently open. Note that diasasl_close() is called implicitly after the return from a callback that reports com_errno != NULL, so the last callback is the last chance of copying this string.

Also note that Quick-SASL is built on top of Quick-MEM, so the string is not actually cleared until the containing memory pool for the diasasl_sesssion is. This means that it may be safe in some programs to replicate just the pointer and not the string.