diff options
Diffstat (limited to 'auth-client.c')
-rw-r--r-- | auth-client.c | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/auth-client.c b/auth-client.c new file mode 100644 index 0000000..c56ffee --- /dev/null +++ b/auth-client.c @@ -0,0 +1,471 @@ +/* + * This file is part of libESMTP, a library for submission of RFC 2822 + * formatted electronic mail messages using the SMTP protocol described + * in RFC 2821. + * + * Copyright (C) 2001 Brian Stafford <brian@stafford.uklinux.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> + +#ifdef USE_PTHREADS +#include <pthread.h> +#endif + +#if HAVE_DLSYM +# include <dlfcn.h> +# ifndef DLEXT +# define DLEXT ".so" +# endif +# ifndef RTLD_LAZY +# define RTLD_LAZY RTLD_NOW +# endif +typedef void *dlhandle_t; +#else +# include <ltdl.h> +# ifndef DLEXT +# define DLEXT "" +# endif +typedef lt_dlhandle dlhandle_t; +# define dlopen(n,f) lt_dlopenext((n)) +# define dlsym(h,s) lt_dlsym((h),(s)) +# define dlclose(h) lt_dlclose((h)) +#endif + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include <missing.h> + +#include "auth-client.h" +#include "auth-plugin.h" +#include "api.h" + +#ifdef USE_PTHREADS +static pthread_mutex_t plugin_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +struct auth_plugin + { + struct auth_plugin *next; + dlhandle_t module; + const struct auth_client_plugin *info; + }; +static struct auth_plugin *client_plugins, *end_client_plugins; + +struct auth_context + { + int min_ssf; + unsigned flags; + const struct auth_client_plugin *client; + void *plugin_ctx; + auth_interact_t interact; + void *interact_arg; + char *external_id; + }; + +#define mechanism_disabled(p,a,f) \ + (((p)->flags & AUTH_PLUGIN_##f) && !((a)->flags & AUTH_PLUGIN_##f)) + +#if HAVE_DLSYM && defined AUTHPLUGINDIR +# define PLUGIN_DIR AUTHPLUGINDIR "/" +#else +# define PLUGIN_DIR +#endif + +static char * +plugin_name (const char *str) +{ + char *buf, *p; + static const char prefix[] = PLUGIN_DIR "sasl-"; + + assert (str != NULL); + + buf = malloc (sizeof prefix + strlen (str) + sizeof DLEXT); + if (buf == NULL) + return NULL; + strcpy (buf, prefix); + p = buf + sizeof prefix - 1; + while (*str != '\0') + *p++ = tolower (*str++); + strcpy (p, DLEXT); + return buf; +} + +static int +append_plugin (dlhandle_t module, const struct auth_client_plugin *info) +{ + struct auth_plugin *auth_plugin; + + assert (info != NULL); + + auth_plugin = malloc (sizeof (struct auth_plugin)); + if (auth_plugin == NULL) + { + /* FIXME: propagate an ENOMEM back to the app. */ + return 0; + } + auth_plugin->info = info; + auth_plugin->module = module; + auth_plugin->next = NULL; + if (client_plugins == NULL) + client_plugins = auth_plugin; + else + end_client_plugins->next = auth_plugin; + end_client_plugins = auth_plugin; + return 1; +} + +static const struct auth_client_plugin * +load_client_plugin (const char *name) +{ + dlhandle_t module; + char *plugin; + const struct auth_client_plugin *info; + + assert (name != NULL); + + /* Try searching for a plugin. */ + plugin = plugin_name (name); + if (plugin == NULL) + return NULL; + module = dlopen (plugin, RTLD_LAZY); + free (plugin); + if (module == NULL) + return NULL; + + info = dlsym (module, "sasl_client"); + if (info == NULL || info->response == NULL) + { + dlclose (module); + return NULL; + } + + /* This plugin module is OK. Add it to the list of loaded modules. + */ + if (!append_plugin (module, info)) + { + dlclose (module); + return NULL; + } + + return info; +} + +void +auth_client_init (void) +{ +#if !HAVE_DLSYM +# ifdef USE_PTHREADS + pthread_mutex_lock (&plugin_mutex); +# endif + lt_dlinit (); +# ifdef AUTHPLUGINDIR + lt_dladdsearchdir (AUTHPLUGINDIR); +# endif + /* Add builtin mechanisms to the plugin list */ +# ifdef USE_PTHREADS + pthread_mutex_unlock (&plugin_mutex); +# endif +#endif +} + +void +auth_client_exit (void) +{ + struct auth_plugin *plugin, *next; + + /* Scan the auth_plugin array and dlclose() the modules */ +#ifdef USE_PTHREADS + pthread_mutex_lock (&plugin_mutex); +#endif + for (plugin = client_plugins; plugin != NULL; plugin = next) + { + next = plugin->next; + if (plugin->module != NULL) + dlclose (plugin->module); + free (plugin); + } + client_plugins = end_client_plugins = NULL; +#if !HAVE_DLSYM + lt_dlexit (); +#endif +#ifdef USE_PTHREADS + pthread_mutex_unlock (&plugin_mutex); +#endif +} + +auth_context_t +auth_create_context (void) +{ + auth_context_t context; + + context = malloc (sizeof (struct auth_context)); + if (context == NULL) + return NULL; + + memset (context, 0, sizeof (struct auth_context)); + return context; +} + +int +auth_destroy_context (auth_context_t context) +{ + API_CHECK_ARGS (context != NULL, 0); + + if (context->plugin_ctx != NULL) + { + if (context->client != NULL && context->client->destroy != NULL) + (*context->client->destroy) (context->plugin_ctx); + } + free (context); + return 1; +} + +int +auth_set_mechanism_flags (auth_context_t context, unsigned set, unsigned clear) +{ + API_CHECK_ARGS (context != NULL, 0); + + context->flags &= AUTH_PLUGIN_EXTERNAL | ~clear; + context->flags |= ~AUTH_PLUGIN_EXTERNAL & set; + return 1; +} + +int +auth_set_mechanism_ssf (auth_context_t context, int min_ssf) +{ + API_CHECK_ARGS (context != NULL, 0); + + context->min_ssf = min_ssf; + return 1; +} + +int +auth_set_external_id (auth_context_t context, const char *identity) +{ + static const struct auth_client_plugin external_client = + { + /* Plugin information */ + "EXTERNAL", + "SASL EXTERNAL mechanism (RFC 2222)", + /* Plugin instance */ + NULL, + NULL, + /* Authentication */ + (auth_response_t) 1, + AUTH_PLUGIN_EXTERNAL, + /* Security Layer */ + 0, + NULL, + NULL, + }; + struct auth_plugin *plugin; + + API_CHECK_ARGS (context != NULL, 0); + + if (context->external_id != NULL) + free (context->external_id); + if (identity != NULL) + { + /* Install external module if required */ + for (plugin = client_plugins; plugin != NULL; plugin = plugin->next) + if (plugin->info->flags & AUTH_PLUGIN_EXTERNAL) + break; + if (plugin == NULL) + { +#ifdef USE_PTHREADS + pthread_mutex_lock (&plugin_mutex); +#endif + append_plugin (NULL, &external_client); +#ifdef USE_PTHREADS + pthread_mutex_unlock (&plugin_mutex); +#endif + } + context->flags |= AUTH_PLUGIN_EXTERNAL; + context->external_id = strdup (identity); + } + else + { + context->flags &= ~AUTH_PLUGIN_EXTERNAL; + context->external_id = NULL; + } + return 1; +} + +int +auth_set_interact_cb (auth_context_t context, + auth_interact_t interact, void *arg) +{ + API_CHECK_ARGS (context != NULL, 0); + + context->interact = interact; + context->interact_arg = arg; + return 1; +} + +/* Perform various checks to see if SASL is usable. Do not check + for loaded plugins though. This is checked when trying to + select one for use. */ +int +auth_client_enabled (auth_context_t context) +{ + if (context == NULL) + return 0; + if (context->interact == NULL) + return 0; + return 1; +} + +int +auth_set_mechanism (auth_context_t context, const char *name) +{ + struct auth_plugin *plugin; + const struct auth_client_plugin *info; + + API_CHECK_ARGS (context != NULL && name != NULL, 0); + +#ifdef USE_PTHREADS + pthread_mutex_lock (&plugin_mutex); +#endif + + /* Get rid of old context */ + if (context->plugin_ctx != NULL) + { + if (context->client != NULL && context->client->destroy != NULL) + (*context->client->destroy) (context->plugin_ctx); + context->plugin_ctx = NULL; + } + + /* Check the list of already loaded modules. */ + info = NULL; + for (plugin = client_plugins; plugin != NULL; plugin = plugin->next) + if (strcasecmp (name, plugin->info->keyw) == 0) + { + info = plugin->info; + break; + } + + /* Load the module if not found above. */ + if (info == NULL && (info = load_client_plugin (name)) == NULL) + { +#ifdef USE_PTHREADS + pthread_mutex_unlock (&plugin_mutex); +#endif + return 0; + } + + /* Check the application's requirements regarding minimum SSF and + whether anonymous or plain text mechanisms are explicitly allowed. + This has to be checked here since the list of loaded plugins is + global and the plugin may have been acceptable in another context + but not in this one. */ + if (info->ssf < context->min_ssf + || mechanism_disabled (info, context, EXTERNAL) + || mechanism_disabled (info, context, ANONYMOUS) + || mechanism_disabled (info, context, PLAIN)) + { +#ifdef USE_PTHREADS + pthread_mutex_unlock (&plugin_mutex); +#endif + return 0; + } + + context->client = info; +#ifdef USE_PTHREADS + pthread_mutex_unlock (&plugin_mutex); +#endif + return 1; +} + +const char * +auth_mechanism_name (auth_context_t context) +{ + API_CHECK_ARGS (context != NULL && context->client != NULL, NULL); + + return context->client->keyw; +} + +const char * +auth_response (auth_context_t context, const char *challenge, int *len) +{ + API_CHECK_ARGS (context != NULL + && context->client != NULL + && len != NULL + && ((context->client->flags & AUTH_PLUGIN_EXTERNAL) + || context->interact != NULL), + NULL); + + if (challenge == NULL) + { + if (context->plugin_ctx != NULL && context->client->destroy != NULL) + (*context->client->destroy) (context->plugin_ctx); + if (context->client->init == NULL) + context->plugin_ctx = NULL; + else if (!(*context->client->init) (&context->plugin_ctx)) + return NULL; + } + if (context->client->flags & AUTH_PLUGIN_EXTERNAL) + { + *len = strlen (context->external_id); + return context->external_id; + } + assert (context->client->response != NULL); + return (*context->client->response) (context->plugin_ctx, + challenge, len, + context->interact, + context->interact_arg); +} + +int +auth_get_ssf (auth_context_t context) +{ + API_CHECK_ARGS (context != NULL && context->client != NULL, -1); + + return context->client->ssf; +} + +void +auth_encode (char **dstbuf, int *dstlen, + const char *srcbuf, int srclen, void *arg) +{ + auth_context_t context = arg; + + if (context != NULL + && context->client != NULL + && context->client->encode != NULL) + (*context->client->encode) (context->plugin_ctx, + dstbuf, dstlen, srcbuf, srclen); +} + +void +auth_decode (char **dstbuf, int *dstlen, + const char *srcbuf, int srclen, void *arg) +{ + auth_context_t context = arg; + + if (context != NULL + && context->client != NULL + && context->client->encode != NULL) + (*context->client->decode) (context->plugin_ctx, + dstbuf, dstlen, srcbuf, srclen); +} |