1 /*
2  *  This file is part of libESMTP, a library for submission of RFC 2822
3  *  formatted electronic mail messages using the SMTP protocol described
4  *  in RFC 2821.
5  *
6  *  Copyright (C) 2001  Brian Stafford  <brian@stafford.uklinux.net>
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <assert.h>
28 
29 #ifdef USE_PTHREADS
30 #include <pthread.h>
31 #endif
32 
33 #if HAVE_DLSYM
34 # include <dlfcn.h>
35 # ifndef DLEXT
36 #  define DLEXT ".so"
37 # endif
38 # ifndef RTLD_LAZY
39 #  define RTLD_LAZY RTLD_NOW
40 # endif
41 typedef void *dlhandle_t;
42 #else
43 # include <ltdl.h>
44 # ifndef DLEXT
45 #  define DLEXT ""
46 # endif
47 typedef lt_dlhandle dlhandle_t;
48 # define dlopen(n,f)	lt_dlopenext((n))
49 # define dlsym(h,s)	lt_dlsym((h),(s))
50 # define dlclose(h)	lt_dlclose((h))
51 #endif
52 
53 #include <stdlib.h>
54 #include <string.h>
55 #include <ctype.h>
56 
57 #include <missing.h>
58 
59 #include "auth-client.h"
60 #include "auth-plugin.h"
61 #include "api.h"
62 
63 #ifdef USE_PTHREADS
64 static pthread_mutex_t plugin_mutex = PTHREAD_MUTEX_INITIALIZER;
65 #endif
66 
67 struct auth_plugin
68   {
69     struct auth_plugin *next;
70     dlhandle_t module;
71     const struct auth_client_plugin *info;
72   };
73 static struct auth_plugin *client_plugins, *end_client_plugins;
74 
75 struct auth_context
76   {
77     int min_ssf;
78     unsigned flags;
79     const struct auth_client_plugin *client;
80     void *plugin_ctx;
81     auth_interact_t interact;
82     void *interact_arg;
83     char *external_id;
84   };
85 
86 #define mechanism_disabled(p,a,f)		\
87 	  (((p)->flags & AUTH_PLUGIN_##f) && !((a)->flags & AUTH_PLUGIN_##f))
88 
89 #if HAVE_DLSYM && defined AUTHPLUGINDIR
90 # define PLUGIN_DIR AUTHPLUGINDIR "/"
91 #else
92 # define PLUGIN_DIR
93 #endif
94 
95 static char *
plugin_name(const char * str)96 plugin_name (const char *str)
97 {
98   char *buf, *p;
99   static const char prefix[] = PLUGIN_DIR "sasl-";
100 
101   assert (str != NULL);
102 
103   buf = malloc (sizeof prefix + strlen (str) + sizeof DLEXT);
104   if (buf == NULL)
105     return NULL;
106   strcpy (buf, prefix);
107   p = buf + sizeof prefix - 1;
108   while (*str != '\0')
109     *p++ = tolower (*str++);
110   strcpy (p, DLEXT);
111   return buf;
112 }
113 
114 static int
append_plugin(dlhandle_t module,const struct auth_client_plugin * info)115 append_plugin (dlhandle_t module, const struct auth_client_plugin *info)
116 {
117   struct auth_plugin *auth_plugin;
118 
119   assert (info != NULL);
120 
121   auth_plugin = malloc (sizeof (struct auth_plugin));
122   if (auth_plugin == NULL)
123     {
124       /* FIXME: propagate an ENOMEM back to the app. */
125       return 0;
126     }
127   auth_plugin->info = info;
128   auth_plugin->module = module;
129   auth_plugin->next = NULL;
130   if (client_plugins == NULL)
131     client_plugins = auth_plugin;
132   else
133     end_client_plugins->next = auth_plugin;
134   end_client_plugins = auth_plugin;
135   return 1;
136 }
137 
138 static const struct auth_client_plugin *
load_client_plugin(const char * name)139 load_client_plugin (const char *name)
140 {
141   dlhandle_t module;
142   char *plugin;
143   const struct auth_client_plugin *info;
144 
145   assert (name != NULL);
146 
147   /* Try searching for a plugin. */
148   plugin = plugin_name (name);
149   if (plugin == NULL)
150     return NULL;
151   module = dlopen (plugin, RTLD_LAZY);
152   free (plugin);
153   if (module == NULL)
154     return NULL;
155 
156   info = dlsym (module, "sasl_client");
157   if (info == NULL || info->response == NULL)
158     {
159       dlclose (module);
160       return NULL;
161     }
162 
163   /* This plugin module is OK.  Add it to the list of loaded modules.
164    */
165   if (!append_plugin (module, info))
166     {
167       dlclose (module);
168       return NULL;
169     }
170 
171   return info;
172 }
173 
174 void
auth_client_init(void)175 auth_client_init (void)
176 {
177 #if !HAVE_DLSYM
178 # ifdef USE_PTHREADS
179   pthread_mutex_lock (&plugin_mutex);
180 # endif
181   lt_dlinit ();
182 # ifdef AUTHPLUGINDIR
183   lt_dladdsearchdir (AUTHPLUGINDIR);
184 # endif
185   /* Add builtin mechanisms to the plugin list */
186 # ifdef USE_PTHREADS
187   pthread_mutex_unlock (&plugin_mutex);
188 # endif
189 #endif
190 }
191 
192 void
auth_client_exit(void)193 auth_client_exit (void)
194 {
195   struct auth_plugin *plugin, *next;
196 
197   /* Scan the auth_plugin array and dlclose() the modules */
198 #ifdef USE_PTHREADS
199   pthread_mutex_lock (&plugin_mutex);
200 #endif
201   for (plugin = client_plugins; plugin != NULL; plugin = next)
202     {
203       next = plugin->next;
204       if (plugin->module != NULL)
205 	dlclose (plugin->module);
206       free (plugin);
207     }
208   client_plugins = end_client_plugins = NULL;
209 #if !HAVE_DLSYM
210   lt_dlexit ();
211 #endif
212 #ifdef USE_PTHREADS
213   pthread_mutex_unlock (&plugin_mutex);
214 #endif
215 }
216 
217 auth_context_t
auth_create_context(void)218 auth_create_context (void)
219 {
220   auth_context_t context;
221 
222   context = malloc (sizeof (struct auth_context));
223   if (context == NULL)
224     return NULL;
225 
226   memset (context, 0, sizeof (struct auth_context));
227   return context;
228 }
229 
230 int
auth_destroy_context(auth_context_t context)231 auth_destroy_context (auth_context_t context)
232 {
233   API_CHECK_ARGS (context != NULL, 0);
234 
235   if (context->plugin_ctx != NULL)
236     {
237       if (context->client != NULL && context->client->destroy != NULL)
238 	(*context->client->destroy) (context->plugin_ctx);
239     }
240   free (context);
241   return 1;
242 }
243 
244 int
auth_set_mechanism_flags(auth_context_t context,unsigned set,unsigned clear)245 auth_set_mechanism_flags (auth_context_t context, unsigned set, unsigned clear)
246 {
247   API_CHECK_ARGS (context != NULL, 0);
248 
249   context->flags &= AUTH_PLUGIN_EXTERNAL | ~clear;
250   context->flags |= ~AUTH_PLUGIN_EXTERNAL & set;
251   return 1;
252 }
253 
254 int
auth_set_mechanism_ssf(auth_context_t context,int min_ssf)255 auth_set_mechanism_ssf (auth_context_t context, int min_ssf)
256 {
257   API_CHECK_ARGS (context != NULL, 0);
258 
259   context->min_ssf = min_ssf;
260   return 1;
261 }
262 
263 int
auth_set_external_id(auth_context_t context,const char * identity)264 auth_set_external_id (auth_context_t context, const char *identity)
265 {
266   static const struct auth_client_plugin external_client =
267     {
268     /* Plugin information */
269       "EXTERNAL",
270       "SASL EXTERNAL mechanism (RFC 2222)",
271     /* Plugin instance */
272       NULL,
273       NULL,
274     /* Authentication */
275       (auth_response_t) 1,
276       AUTH_PLUGIN_EXTERNAL,
277     /* Security Layer */
278       0,
279       NULL,
280       NULL,
281     };
282   struct auth_plugin *plugin;
283 
284   API_CHECK_ARGS (context != NULL, 0);
285 
286   if (context->external_id != NULL)
287     free (context->external_id);
288   if (identity != NULL)
289     {
290       /* Install external module if required */
291       for (plugin = client_plugins; plugin != NULL; plugin = plugin->next)
292         if (plugin->info->flags & AUTH_PLUGIN_EXTERNAL)
293           break;
294       if (plugin == NULL)
295         {
296 #ifdef USE_PTHREADS
297 	  pthread_mutex_lock (&plugin_mutex);
298 #endif
299 	  append_plugin (NULL, &external_client);
300 #ifdef USE_PTHREADS
301 	  pthread_mutex_unlock (&plugin_mutex);
302 #endif
303 	}
304       context->flags |= AUTH_PLUGIN_EXTERNAL;
305       context->external_id = strdup (identity);
306     }
307   else
308     {
309       context->flags &= ~AUTH_PLUGIN_EXTERNAL;
310       context->external_id = NULL;
311     }
312   return 1;
313 }
314 
315 int
auth_set_interact_cb(auth_context_t context,auth_interact_t interact,void * arg)316 auth_set_interact_cb (auth_context_t context,
317 		      auth_interact_t interact, void *arg)
318 {
319   API_CHECK_ARGS (context != NULL, 0);
320 
321   context->interact = interact;
322   context->interact_arg = arg;
323   return 1;
324 }
325 
326 /* Perform various checks to see if SASL is usable.   Do not check
327    for loaded plugins though.  This is checked when trying to
328    select one for use. */
329 int
auth_client_enabled(auth_context_t context)330 auth_client_enabled (auth_context_t context)
331 {
332   if (context == NULL)
333     return 0;
334   if (context->interact == NULL)
335     return 0;
336   return 1;
337 }
338 
339 int
auth_set_mechanism(auth_context_t context,const char * name)340 auth_set_mechanism (auth_context_t context, const char *name)
341 {
342   struct auth_plugin *plugin;
343   const struct auth_client_plugin *info;
344 
345   API_CHECK_ARGS (context != NULL && name != NULL, 0);
346 
347 #ifdef USE_PTHREADS
348   pthread_mutex_lock (&plugin_mutex);
349 #endif
350 
351   /* Get rid of old context */
352   if (context->plugin_ctx != NULL)
353     {
354       if (context->client != NULL && context->client->destroy != NULL)
355 	(*context->client->destroy) (context->plugin_ctx);
356       context->plugin_ctx = NULL;
357     }
358 
359   /* Check the list of already loaded modules. */
360   info = NULL;
361   for (plugin = client_plugins; plugin != NULL; plugin = plugin->next)
362     if (strcasecmp (name, plugin->info->keyw) == 0)
363       {
364 	info = plugin->info;
365 	break;
366       }
367 
368   /* Load the module if not found above. */
369   if (info == NULL && (info = load_client_plugin (name)) == NULL)
370     {
371 #ifdef USE_PTHREADS
372       pthread_mutex_unlock (&plugin_mutex);
373 #endif
374       return 0;
375     }
376 
377   /* Check the application's requirements regarding minimum SSF and
378      whether anonymous or plain text mechanisms are explicitly allowed.
379      This has to be checked here since the list of loaded plugins is
380      global and the plugin may have been acceptable in another context
381      but not in this one.  */
382   if (info->ssf < context->min_ssf
383       || mechanism_disabled (info, context, EXTERNAL)
384       || mechanism_disabled (info, context, ANONYMOUS)
385       || mechanism_disabled (info, context, PLAIN))
386     {
387 #ifdef USE_PTHREADS
388       pthread_mutex_unlock (&plugin_mutex);
389 #endif
390       return 0;
391     }
392 
393   context->client = info;
394 #ifdef USE_PTHREADS
395   pthread_mutex_unlock (&plugin_mutex);
396 #endif
397   return 1;
398 }
399 
400 const char *
auth_mechanism_name(auth_context_t context)401 auth_mechanism_name (auth_context_t context)
402 {
403   API_CHECK_ARGS (context != NULL && context->client != NULL, NULL);
404 
405   return context->client->keyw;
406 }
407 
408 const char *
auth_response(auth_context_t context,const char * challenge,int * len)409 auth_response (auth_context_t context, const char *challenge, int *len)
410 {
411   API_CHECK_ARGS (context != NULL
412                   && context->client != NULL
413                   && len != NULL
414 		  && ((context->client->flags & AUTH_PLUGIN_EXTERNAL)
415 		       || context->interact != NULL),
416 		  NULL);
417 
418   if (challenge == NULL)
419     {
420       if (context->plugin_ctx != NULL && context->client->destroy != NULL)
421         (*context->client->destroy) (context->plugin_ctx);
422       if (context->client->init == NULL)
423 	context->plugin_ctx = NULL;
424       else if (!(*context->client->init) (&context->plugin_ctx))
425 	return NULL;
426     }
427   if (context->client->flags & AUTH_PLUGIN_EXTERNAL)
428     {
429       *len = strlen (context->external_id);
430       return context->external_id;
431     }
432   assert (context->client->response != NULL);
433   return (*context->client->response) (context->plugin_ctx,
434   				       challenge, len,
435 				       context->interact,
436 				       context->interact_arg);
437 }
438 
439 int
auth_get_ssf(auth_context_t context)440 auth_get_ssf (auth_context_t context)
441 {
442   API_CHECK_ARGS (context != NULL && context->client != NULL, -1);
443 
444   return context->client->ssf;
445 }
446 
447 void
auth_encode(char ** dstbuf,int * dstlen,const char * srcbuf,int srclen,void * arg)448 auth_encode (char **dstbuf, int *dstlen,
449              const char *srcbuf, int srclen, void *arg)
450 {
451   auth_context_t context = arg;
452 
453   if (context != NULL
454       && context->client != NULL
455       && context->client->encode != NULL)
456     (*context->client->encode) (context->plugin_ctx,
457 				dstbuf, dstlen, srcbuf, srclen);
458 }
459 
460 void
auth_decode(char ** dstbuf,int * dstlen,const char * srcbuf,int srclen,void * arg)461 auth_decode (char **dstbuf, int *dstlen,
462 	     const char *srcbuf, int srclen, void *arg)
463 {
464   auth_context_t context = arg;
465 
466   if (context != NULL
467       && context->client != NULL
468       && context->client->encode != NULL)
469     (*context->client->decode) (context->plugin_ctx,
470 				dstbuf, dstlen, srcbuf, srclen);
471 }
472