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