Authentication rewrite project 

Auth Scheme API

      Copyright (c)  2000 Robert BT Collins
      Permission is granted to copy, distribute and/or modify this document
      under the terms of the GNU Free Documentation License, Version 1.1
      or any later version published by the Free Software Foundation;
      with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts.
      A copy of the license is included in the section entitled "GNU Free Documentation License".

Authentication rewrite summary:
"The auth_rewrite project aims at providing an extensible authentication mechanism for Squid, and at generally enhancing the existing authentication support."

Table of Contents:

  1. Definition of an auth scheme.
  2. Data types
  3. How to add a new auth scheme
  4. How to 'hook in' new functions to the API.
  5. GNU Free Documentation License

Definition of an auth scheme 

An auth scheme in squid is the collection of functions required to manage the authentication process for a given HTTP authentication scheme. Existing auth schemes in squid are Basic and NTLM. Other HTTP schemes (see for example rfc 2617) have been published and could be implemented in squid. The term auth scheme and auth module are interchangable. An auth module is not to be confused with an authentication helper, which is a scheme specific external program used by a specific scheme to perform data manipulation external to squid. Typically this involves comparing the browser submitted credentials with those in the organisation's user directory.

Auth modules SHOULD NOT perform access control functions. Squid has advanced caching access control functionality already. Future work in squid will allow a auth scheme helper to return group information for a user, to allow Squid to more seamlessly implement access control.

Data types

The data types are presented in C for the simple reason that squid is currently written exclusively in C.

Function typedefs.

Each function related to the general case of http authentication has a matching typedef. There are some additional function types used to register/initialise, deregister/shutdown and provide stats on auth modules:

typedef int AUTHSACTIVE();

The Active function is used by squid to determine whether the auth module has successfully configured and initialised itself. If Active returns 0 no other module functions except Shutdown/Dump/Parse/FreeConfig will be called by Squid.

typedef void AUTHSSETUP(authscheme_entry_t *);

functions of type AUTHSSETUP are used to register an auth module with squid. The registration function MUST be named "authSchemeSetup_SCHEME" where SCHEME is the auth_scheme as defined by rfc 2617. Only one auth scheme registered in squid can provide functionality for a given auth_scheme. (I.e. only one auth module can handle Basic, only one can handle Digest and so forth). The Setup function is responsible for registering the functions in the auth module into the passed authscheme_entry_t. The authscheme_entry_t will never be NULL. If it is NULL the auth module should log an error and do nothing. The other functions can have any desired name that does not collide with any statically linked function name within Squid. It is recommended to use names of the form "authe_SCHEME_FUNCTIONNAME" (for example authenticate_NTLM_Active is the Active() function for the NTLM auth module. 

typedef void AUTHSSHUTDOWN(void);

Functions of type AUTHSSHUTDOWN are responsible for freeing any resources used by the auth modules. The shutdown function will be called before squid reconfigures, and before squid shutsdown.

typedef void AUTHSINIT(authScheme *);

Functions of type AUTHSINIT are responsible for allocating any needed resources for the authentication module. AUTHSINIT functions are called after each configuration takes place before any new requests are made.

typedef void AUTHSPARSE(authScheme *, int, char *);

Functions of type AUTHSPARSE are responsible for parsing authentication parameters. The function currently needs a scheme scope data structure to store the configuration in. The passed scheme's scheme_data pointer should point to the local data structure. Future development will allow all authentication schemes direct access to their configuration data without a locally scope structure. The parse function is called by squid's config file parser when a auth_param scheme_name entry is encountered.

typedef void AUTHSFREECONFIG(authScheme *);

Functions of type AUTHSFREECONFIG are called by squid when freeing configuration data. The auth scheme should free any memory allocated that is related to parse data structures. The scheme MAY take advantage of this call to remove scheme local configuration dependent data. (Ie cached user details that are only relevant to a config setting).

typedef void AUTHSDUMP(StoreEntry *, const char *, authScheme *);

Functions of type AUTHSDUMP are responsible for writing to the StoreEntry the configuration parameters that a user would put in a config file to recreate the running configuration. 

typedef void AUTHSSTATS(StoreEntry *);

Functions of type AUTHSSTATS are called by the cachemgr to provide statistics on the authmodule. Current modules simply provide the statistics from the back end helpers (number of requests, state of the helpers), but more detailed statistics are possible - for example unique users seen or failed authentication requests.

The next set of functions work on the data structures used by the authentication schemes.

typedef void AUTHSREQFREE(auth_user_request_t *);

The AUTHSREQFREE function is called when a auth_user_request is being freed by the authentication framework, and scheme specific data was present. The function should free any scheme related data and MUST set the scheme_data pointer to NULL. Failure to unlink the scheme data will result in squid dieing.

typedef char *AUTHSUSERNAME(auth_user_t *);

Squid does not make assumptions about where the username is stored. This function must return a pointer to a NULL terminated string to be used in logging the request. Return NULL if no username/usercode is known. The string should NOT be allocated each time this function is called. 

typedef int AUTHSAUTHED(auth_user_request_t *);

The AUTHED function is used by squid to determine whether the auth scheme has successfully authenticated the user request. If timeouts on cached credentials have occured or for any reason the credentials are not valid, return false.

The next set of functions perform the actual authentication. The functions are used by squid for both WWW- and Proxy- authentication. Therefore they MUST NOT assume the authentication will be based on the Proxy-* Headers.

typedef void AUTHSAUTHUSER(auth_user_request_t *, request_t *, ConnStateData *, http_hdr_type);

Functions of type AUTHSAUTHUSER are called when Squid has a request that needs authentication. If needed the auth scheme can alter the auth_user pointer (usually to point to a previous instance of the user whose name is discovered late in the auth process. For an example of this see the ntlm scheme). These functions are responsible for performing any in-squid routines for the authentication of the user. The auth_user_request struct that is passed around is only persistent for the current request. If the auth module requires access to the structure in the future it MUST lock it, and implement some method for identifying it in the future. For example the NTLM module implements a connection based authentication scheme, so the auth_user_request struct gets referenced from the ConnStateData.

typedef void AUTHSDECODE(auth_user_request_t *, const char *);

Functions of type AUTHSDECODE are responsible for decoding the passed authentication header, creating or linking to a auth_user struct and for storing any needed details to complete authentication in AUTHSAUTHUSER. 

typedef int AUTHSDIRECTION(auth_user_request_t *);

Functions of type AUTHSDIRECTION are used by squid to determine what the next step in performing authentication for a given scheme is. The following are the return codes: 

typedef void AUTHSFIXERR(auth_user_request_t *, HttpReply *, http_hdr_type, request_t *);

Functions of type AUTHSFIXERR are used by squid to add scheme specific challenges when returning a 401 or 407 error code. On requests where no authentication information was provided, all registered auth modules will have their AUTHSFIXERR function called. When the client makes a request with an authentication header, on subsequent calls only the matching AUTHSFIXERR function is called (and then only if the auth module indicated it had a new challenge to send the client). If no auth schemes match the request, the authentication credentials in the request are ignored - and all auth modules are called.

typedef void AUTHSFREE(auth_user_t *);

These functions are responsible for freeing scheme specific data from the passed auth_user_t structure. This should only be called by squid when there are no outstanding requests linked to the auth user. This includes removing the user from any scheme specific memory caches.

typedef void AUTHSADDHEADER(auth_user_request_t *, HttpReply *, int);
typedef void AUTHSADDTRAILER(auth_user_request_t *, HttpReply *, int);

These functions are responsible for adding any authentication specific header(s) or trailer(s) OTHER THAN the WWW-Authenticate & Proxy-Authenticate headers to the passed HttpReply. The int indicates whether the request was an accelerated request or a proxied request. For example operation see the digest auth scheme. (Digest uses a Authentication-Info header.) This function is called whenever a auth_user_request exists in a request when the reply is constructed & after the body is sent on chunked replies respectively.

typedef void AUTHSONCLOSEC(ConnStateData *);

This function type is called when a auth_user_request is linked into a ConnStateData struct, and the connection is closed. If any scheme specific activities related to the request or connection are in progress, this function MUST clear them.

typedef void AUTHSSTART(auth_user_request_t * , RH * , void *);

This function type is called when squid is ready to put the request on hold and wait for a callback from the auth module when the auth module has performed it's external activities.

Structures

This is used to link auth_users into the username cache. Because some schemes may link in aliases to a user, the link is not part of the auth_user structure itself.

struct _auth_user_hash_pointer {
/* first two items must be same as hash_link */
char *key;
auth_user_hash_pointer *next;
auth_user_t *auth_user;
dlink_node link; /* other hash entries that point to the same auth_user */
};

This is the main user related structure. It stores user-related data, and is persistent across requests. It can even persistent across multiple external authentications. One major benefit of preserving this structure is the cached acl match results. This structure, is private to the authentication framework.

struct _auth_user_t {
/* extra fields for proxy_auth */
/* this determines what scheme owns the user data. */
auth_type_t auth_type;
/* the index +1 in the authscheme_list to the authscheme entry */
int auth_module;
/* we only have one username associated with a given auth_user struct */
auth_user_hash_pointer *usernamehash;
/* we may have many proxy-authenticate strings that decode to the same user*/
dlink_list proxy_auth_list;
dlink_list proxy_match_cache;
struct {
unsigned int credentials_ok:2; /*0=unchecked,1=ok,2=failed*/
} flags;
long expiretime;
/* IP addr this user authenticated from */
struct in_addr ipaddr;
time_t ip_expiretime;
/* how many references are outstanding to this instance*/
size_t references;
/* the auth scheme has it's own private data area */
void *scheme_data;
/* the auth_user_request structures that link to this. Yes it could be a splaytree
* but how many requests will a single username have in parallel? */
dlink_list requests;
};

This is a short lived structure is the visible aspect of the authentication framework. 

struct _auth_user_request_t {
/* this is the object passed around by client_side and acl functions */
/* it has request specific data, and links to user specific data */
/* the user */
auth_user_t *auth_user;
/* return a message on the 407 error pages */
char *message;
/* any scheme specific request related data */
void *scheme_data;
/* how many 'processes' are working on this data */
size_t references;
};

The authscheme_entry struct is used to store the runtime registered functions that make up an auth scheme. An auth scheme module MUST implement ALL functions except the following functions: oncloseconnection, AddHeader, AddTrailer.. In the future more optional functions may be added to this data type. 


struct _authscheme_entry {
char *typestr;
AUTHSACTIVE *Active;
AUTHSADDHEADER *AddHeader;
AUTHSADDTRAILER *AddTrailer;
AUTHSAUTHED *authenticated;
AUTHSAUTHUSER *authAuthenticate;
AUTHSDUMP *dump;
AUTHSFIXERR *authFixHeader;
AUTHSFREE *FreeUser;
AUTHSFREECONFIG *freeconfig;
AUTHSUSERNAME *authUserUsername;
AUTHSONCLOSEC *oncloseconnection; /*optional*/
AUTHSDECODE *decodeauth;
AUTHSDIRECTION *getdirection;
AUTHSPARSE *parse;
AUTHSINIT *init;
AUTHSREQFREE *requestFree;
AUTHSSHUTDOWN *donefunc;
AUTHSSTART *authStart;
AUTHSSTATS *authStats;
};

For information on the requirements for each of the functions, see the details under the typedefs above. For reference implementations, see the squid source code, /src/auth/basic for a request based stateless auth module, and /src/auth/ntlm for a connection based stateful auth module.

How to add a new auth scheme

Copy the nearest existing auth scheme and modify to recieve the approprate scheme headers. Now step through the acl.c MatchAclProxyUser function's code path and see how the functions call down through authenticate.c to your scheme. Write a helper to provide you scheme with any backend existence it needs. Remember any blocking code must go in AUTHSSTART function(s) and _MUST_ use callbacks.

How to 'hook in' new functions to the API.

Start of by figuring the code path that will result in the function being called, and what data it will need. Then create a typedef for the function, add and entry to the authscheme_entry struct. Add a wrapper function to authenticate.c (or if approprate cf_cache.c) that called the scheme specific function if it exists. Test it. Test it again. Now port  to all the existing auth schemes, or at least add a setting of NULL for the function for each scheme. 

GNU Free Documentation License.

A copy of the GNU Free Documentation License is available here. You can also find a copy on the Free Software Foundation website http://www.fsf.org .


Squid Now! Cache Now! Valid HTML 4.0! SourceForge
$Id: authapi.html,v 1.5 2003/06/03 21:55:55 serassio Exp $