1 /*
2 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /* SASL server API implementation
7 * Rob Siemborski
8 * Tim Martin
9 * $Id: client.c,v 1.61 2003/04/16 19:36:00 rjs3 Exp $
10 */
11 /*
12 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 *
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 *
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in
23 * the documentation and/or other materials provided with the
24 * distribution.
25 *
26 * 3. The name "Carnegie Mellon University" must not be used to
27 * endorse or promote products derived from this software without
28 * prior written permission. For permission or any other legal
29 * details, please contact
30 * Office of Technology Transfer
31 * Carnegie Mellon University
32 * 5000 Forbes Avenue
33 * Pittsburgh, PA 15213-3890
34 * (412) 268-4387, fax: (412) 268-7395
35 * tech-transfer@andrew.cmu.edu
36 *
37 * 4. Redistributions of any form whatsoever must retain the following
38 * acknowledgment:
39 * "This product includes software developed by Computing Services
40 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
41 *
42 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
43 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
44 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
45 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
46 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
47 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
48 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
49 */
50
51 #include <config.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <limits.h>
55 #include <ctype.h>
56 #include <string.h>
57 #ifdef HAVE_UNISTD_H
58 #include <unistd.h>
59 #endif
60
61 /* SASL Headers */
62 #include "sasl.h"
63 #include "saslplug.h"
64 #include "saslutil.h"
65 #include "saslint.h"
66
67 #ifdef _SUN_SDK_
68 DEFINE_STATIC_MUTEX(init_client_mutex);
69 DEFINE_STATIC_MUTEX(client_active_mutex);
70 /*
71 * client_plug_mutex ensures only one client plugin is init'ed at a time
72 * If a plugin is loaded more than once, the glob_context may be overwritten
73 * which may lead to a memory leak. We keep glob_context with each mech
74 * to avoid this problem.
75 */
76 DEFINE_STATIC_MUTEX(client_plug_mutex);
77 #else
78 static cmech_list_t *cmechlist; /* global var which holds the list */
79
80 static sasl_global_callbacks_t global_callbacks;
81
82 static int _sasl_client_active = 0;
83 #endif /* _SUN_SDK_ */
84
85 #ifdef _SUN_SDK_
init_mechlist(_sasl_global_context_t * gctx)86 static int init_mechlist(_sasl_global_context_t *gctx)
87 {
88 cmech_list_t *cmechlist = gctx->cmechlist;
89 #else
90 static int init_mechlist()
91 {
92 #endif /* _SUN_SDK_ */
93
94 cmechlist->mutex = sasl_MUTEX_ALLOC();
95 if(!cmechlist->mutex) return SASL_FAIL;
96
97 #ifdef _SUN_SDK_
98 cmechlist->utils=
99 _sasl_alloc_utils(gctx, NULL, &gctx->client_global_callbacks);
100 #else
101 cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks);
102 #endif /* _SUN_SDK_ */
103 if (cmechlist->utils==NULL)
104 return SASL_NOMEM;
105
106 cmechlist->mech_list=NULL;
107 cmechlist->mech_length=0;
108
109 return SASL_OK;
110 }
111
112 #ifdef _SUN_SDK_
113 static int client_done(_sasl_global_context_t *gctx) {
114 cmech_list_t *cmechlist = gctx->cmechlist;
115 _sasl_path_info_t *path_info, *p;
116 #else
117 static int client_done(void) {
118 #endif /* _SUN_SDK_ */
119 cmechanism_t *cm;
120 cmechanism_t *cprevm;
121
122 #ifdef _SUN_SDK_
123 if(!gctx->sasl_client_active)
124 return SASL_NOTINIT;
125 if (LOCK_MUTEX(&client_active_mutex) < 0) {
126 return (SASL_FAIL);
127 }
128 gctx->sasl_client_active--;
129
130 if(gctx->sasl_client_active) {
131 /* Don't de-init yet! Our refcount is nonzero. */
132 UNLOCK_MUTEX(&client_active_mutex);
133 return SASL_CONTINUE;
134 }
135 #else
136 if(!_sasl_client_active)
137 return SASL_NOTINIT;
138 else
139 _sasl_client_active--;
140
141 if(_sasl_client_active) {
142 /* Don't de-init yet! Our refcount is nonzero. */
143 return SASL_CONTINUE;
144 }
145 #endif /* _SUN_SDK_ */
146
147 cm=cmechlist->mech_list; /* m point to begging of the list */
148 while (cm!=NULL)
149 {
150 cprevm=cm;
151 cm=cm->next;
152
153 if (cprevm->plug->mech_free) {
154 #ifdef _SUN_SDK_
155 cprevm->plug->mech_free(cprevm->glob_context, cmechlist->utils);
156 #else
157 cprevm->plug->mech_free(cprevm->plug->glob_context,
158 cmechlist->utils);
159 #endif /* _SUN_SDK_ */
160 }
161
162 sasl_FREE(cprevm->plugname);
163 sasl_FREE(cprevm);
164 }
165 sasl_MUTEX_FREE(cmechlist->mutex);
166 _sasl_free_utils(&cmechlist->utils);
167 sasl_FREE(cmechlist);
168
169 #ifdef _SUN_SDK_
170 gctx->cmechlist = NULL;
171 p = gctx->cplug_path_info;
172 while((path_info = p) != NULL) {
173 sasl_FREE(path_info->path);
174 p = path_info->next;
175 sasl_FREE(path_info);
176 }
177 gctx->cplug_path_info = NULL;
178 UNLOCK_MUTEX(&client_active_mutex);
179 #else
180 cmechlist = NULL;
181 #endif /* _SUN_SDK_ */
182
183 return SASL_OK;
184 }
185
186 int sasl_client_add_plugin(const char *plugname,
187 sasl_client_plug_init_t *entry_point)
188 {
189 #ifdef _SUN_SDK_
190 return (_sasl_client_add_plugin(_sasl_gbl_ctx(), plugname, entry_point));
191 }
192
193 int _sasl_client_add_plugin(void *ctx,
194 const char *plugname,
195 sasl_client_plug_init_t *entry_point)
196 {
197 cmech_list_t *cmechlist;
198 #ifdef _INTEGRATED_SOLARIS_
199 _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
200 int sun_reg;
201 #endif /* _INTEGRATED_SOLARIS_ */
202 int i;
203 cmechanism_t *m;
204 #endif /* _SUN_SDK_ */
205 int plugcount;
206 sasl_client_plug_t *pluglist;
207 cmechanism_t *mech;
208 int result;
209 int version;
210 int lupe;
211
212 if(!plugname || !entry_point) return SASL_BADPARAM;
213
214 #ifdef _SUN_SDK_
215 cmechlist = gctx->cmechlist;
216
217 if (cmechlist == NULL) return SASL_BADPARAM;
218
219 /* Check to see if this plugin has already been registered */
220 m = cmechlist->mech_list;
221 for (i = 0; i < cmechlist->mech_length; i++) {
222 if (strcmp(plugname, m->plugname) == 0) {
223 return SASL_OK;
224 }
225 m = m->next;
226 }
227
228 result = LOCK_MUTEX(&client_plug_mutex);
229 if (result != SASL_OK)
230 return result;
231
232 #endif /* _SUN_SDK_ */
233
234 result = entry_point(cmechlist->utils, SASL_CLIENT_PLUG_VERSION, &version,
235 &pluglist, &plugcount);
236
237 #ifdef _INTEGRATED_SOLARIS_
238 sun_reg = _is_sun_reg(pluglist);
239 #endif /* _INTEGRATED_SOLARIS_ */
240 if (result != SASL_OK)
241 {
242 #ifdef _SUN_SDK_
243 UNLOCK_MUTEX(&client_plug_mutex);
244 __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_WARN,
245 "entry_point failed in sasl_client_add_plugin for %s",
246 plugname);
247 #else
248 _sasl_log(NULL, SASL_LOG_WARN,
249 "entry_point failed in sasl_client_add_plugin for %s",
250 plugname);
251 #endif /* _SUN_SDK_ */
252 return result;
253 }
254
255 if (version != SASL_CLIENT_PLUG_VERSION)
256 {
257 #ifdef _SUN_SDK_
258 UNLOCK_MUTEX(&client_plug_mutex);
259 __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_WARN,
260 "version conflict in sasl_client_add_plugin for %s", plugname);
261 #else
262 _sasl_log(NULL, SASL_LOG_WARN,
263 "version conflict in sasl_client_add_plugin for %s", plugname);
264 #endif /* _SUN_SDK_ */
265 return SASL_BADVERS;
266 }
267
268 #ifdef _SUN_SDK_
269 /* Check plugins to make sure mech_name is non-NULL */
270 for (lupe=0;lupe < plugcount ;lupe++) {
271 if (pluglist[lupe].mech_name == NULL)
272 break;
273 }
274 if (lupe < plugcount) {
275 UNLOCK_MUTEX(&client_plug_mutex);
276 __sasl_log(gctx, gctx->client_global_callbacks.callbacks,
277 SASL_LOG_ERR, "invalid client plugin %s", plugname);
278 return SASL_BADPROT;
279 }
280 #endif /* _SUN_SDK_ */
281
282 for (lupe=0;lupe< plugcount ;lupe++)
283 {
284 mech = sasl_ALLOC(sizeof(cmechanism_t));
285 #ifdef _SUN_SDK_
286 if (! mech) {
287 UNLOCK_MUTEX(&client_plug_mutex);
288 return SASL_NOMEM;
289 }
290 mech->glob_context = pluglist->glob_context;
291 #else
292 if (! mech) return SASL_NOMEM;
293 #endif /* _SUN_SDK_ */
294
295 mech->plug=pluglist++;
296 if(_sasl_strdup(plugname, &mech->plugname, NULL) != SASL_OK) {
297 #ifdef _SUN_SDK_
298 UNLOCK_MUTEX(&client_plug_mutex);
299 #endif /* _SUN_SDK_ */
300 sasl_FREE(mech);
301 return SASL_NOMEM;
302 }
303 #ifdef _INTEGRATED_SOLARIS_
304 mech->sun_reg = sun_reg;
305 #endif /* _INTEGRATED_SOLARIS_ */
306 mech->version = version;
307 mech->next = cmechlist->mech_list;
308 cmechlist->mech_list = mech;
309 cmechlist->mech_length++;
310 }
311 #ifdef _SUN_SDK_
312 UNLOCK_MUTEX(&client_plug_mutex);
313 #endif /* _SUN_SDK_ */
314
315 return SASL_OK;
316 }
317
318 static int
319 client_idle(sasl_conn_t *conn)
320 {
321 cmechanism_t *m;
322 #ifdef _SUN_SDK_
323 _sasl_global_context_t *gctx = conn == NULL ? _sasl_gbl_ctx() : conn->gctx;
324 cmech_list_t *cmechlist = gctx->cmechlist;
325 #endif /* _SUN_SDK_ */
326
327 if (! cmechlist)
328 return 0;
329
330 for (m = cmechlist->mech_list;
331 m;
332 m = m->next)
333 if (m->plug->idle
334 #ifdef _SUN_SDK_
335 && m->plug->idle(m->glob_context,
336 #else
337 && m->plug->idle(m->plug->glob_context,
338 #endif /* _SUN_SDK_ */
339 conn,
340 conn ? ((sasl_client_conn_t *)conn)->cparams : NULL))
341 return 1;
342 return 0;
343 }
344
345 #ifdef _SUN_SDK_
346 static int _load_client_plugins(_sasl_global_context_t *gctx)
347 {
348 int ret;
349 const add_plugin_list_t _ep_list[] = {
350 { "sasl_client_plug_init", (add_plugin_t *)_sasl_client_add_plugin },
351 { "sasl_canonuser_init", (add_plugin_t *)_sasl_canonuser_add_plugin },
352 { NULL, NULL }
353 };
354 const sasl_callback_t *callbacks = gctx->client_global_callbacks.callbacks;
355
356 ret = _sasl_load_plugins(gctx, 0, _ep_list,
357 _sasl_find_getpath_callback(callbacks),
358 _sasl_find_verifyfile_callback(callbacks));
359 return (ret);
360 }
361 #endif /* _SUN_SDK_ */
362
363 /* initialize the SASL client drivers
364 * callbacks -- base callbacks for all client connections
365 * returns:
366 * SASL_OK -- Success
367 * SASL_NOMEM -- Not enough memory
368 * SASL_BADVERS -- Mechanism version mismatch
369 * SASL_BADPARAM -- error in config file
370 * SASL_NOMECH -- No mechanisms available
371 * ...
372 */
373
374 int sasl_client_init(const sasl_callback_t *callbacks)
375 {
376 #ifdef _SUN_SDK_
377 return _sasl_client_init(NULL, callbacks);
378 }
379
380 int _sasl_client_init(void *ctx,
381 const sasl_callback_t *callbacks)
382 {
383 int ret;
384 _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
385
386 if (gctx == NULL)
387 gctx = _sasl_gbl_ctx();
388
389 ret = LOCK_MUTEX(&init_client_mutex);
390 if (ret < 0) {
391 return (SASL_FAIL);
392 }
393 ret = LOCK_MUTEX(&client_active_mutex);
394 if (ret < 0) {
395 UNLOCK_MUTEX(&init_client_mutex);
396 return (SASL_FAIL);
397 }
398 if(gctx->sasl_client_active) {
399 /* We're already active, just increase our refcount */
400 /* xxx do something with the callback structure? */
401 gctx->sasl_client_active++;
402 UNLOCK_MUTEX(&client_active_mutex);
403 UNLOCK_MUTEX(&init_client_mutex);
404 return SASL_OK;
405 }
406
407 gctx->client_global_callbacks.callbacks = callbacks;
408 gctx->client_global_callbacks.appname = NULL;
409
410 gctx->cmechlist=sasl_ALLOC(sizeof(cmech_list_t));
411 if (gctx->cmechlist==NULL) {
412 UNLOCK_MUTEX(&init_client_mutex);
413 UNLOCK_MUTEX(&client_active_mutex);
414 return SASL_NOMEM;
415 }
416
417 gctx->sasl_client_active = 1;
418 UNLOCK_MUTEX(&client_active_mutex);
419
420 /* load plugins */
421 ret=init_mechlist(gctx);
422
423 if (ret!=SASL_OK) {
424 client_done(gctx);
425 UNLOCK_MUTEX(&init_client_mutex);
426 return ret;
427 }
428 _sasl_client_add_plugin(gctx, "EXTERNAL", &external_client_plug_init);
429
430 ret = _sasl_common_init(gctx, &gctx->client_global_callbacks, 0);
431 #else
432 int sasl_client_init(const sasl_callback_t *callbacks)
433 {
434 int ret;
435 const add_plugin_list_t ep_list[] = {
436 { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin },
437 { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin },
438 { NULL, NULL }
439 };
440
441 if(_sasl_client_active) {
442 /* We're already active, just increase our refcount */
443 /* xxx do something with the callback structure? */
444 _sasl_client_active++;
445 return SASL_OK;
446 }
447
448 global_callbacks.callbacks = callbacks;
449 global_callbacks.appname = NULL;
450
451 cmechlist=sasl_ALLOC(sizeof(cmech_list_t));
452 if (cmechlist==NULL) return SASL_NOMEM;
453
454 /* We need to call client_done if we fail now */
455 _sasl_client_active = 1;
456
457 /* load plugins */
458 ret=init_mechlist();
459 if (ret!=SASL_OK) {
460 client_done();
461 return ret;
462 }
463
464 sasl_client_add_plugin("EXTERNAL", &external_client_plug_init);
465
466 ret = _sasl_common_init(&global_callbacks);
467 #endif /* _SUN_SDK_ */
468
469 if (ret == SASL_OK)
470 #ifdef _SUN_SDK_
471 ret = _load_client_plugins(gctx);
472 #else
473 ret = _sasl_load_plugins(ep_list,
474 _sasl_find_getpath_callback(callbacks),
475 _sasl_find_verifyfile_callback(callbacks));
476 #endif /* _SUN_SDK_ */
477
478 #ifdef _SUN_SDK_
479 if (ret == SASL_OK)
480 /* If sasl_client_init returns error, sasl_done() need not be called */
481 ret = _sasl_build_mechlist(gctx);
482 if (ret == SASL_OK) {
483 gctx->sasl_client_cleanup_hook = &client_done;
484 gctx->sasl_client_idle_hook = &client_idle;
485 } else {
486 client_done(gctx);
487 }
488 UNLOCK_MUTEX(&init_client_mutex);
489 #else
490 if (ret == SASL_OK) {
491 _sasl_client_cleanup_hook = &client_done;
492 _sasl_client_idle_hook = &client_idle;
493
494 ret = _sasl_build_mechlist();
495 } else {
496 client_done();
497 }
498 #endif /* _SUN_SDK_ */
499
500 return ret;
501 }
502
503 static void client_dispose(sasl_conn_t *pconn)
504 {
505 sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn;
506 #ifdef _SUN_SDK_
507 sasl_free_t *free_func = c_conn->cparams->utils->free;
508 #endif /* _SUN_SDK_ */
509
510 if (c_conn->mech && c_conn->mech->plug->mech_dispose) {
511 c_conn->mech->plug->mech_dispose(pconn->context,
512 c_conn->cparams->utils);
513 }
514
515 pconn->context = NULL;
516
517 if (c_conn->clientFQDN)
518 #ifdef _SUN_SDK_
519 free_func(c_conn->clientFQDN);
520 #else
521 sasl_FREE(c_conn->clientFQDN);
522 #endif /* _SUN_SDK_ */
523
524 if (c_conn->cparams) {
525 _sasl_free_utils(&(c_conn->cparams->utils));
526 #ifdef _SUN_SDK_
527 free_func(c_conn->cparams);
528 #else
529 sasl_FREE(c_conn->cparams);
530 #endif /* _SUN_SDK_ */
531 }
532
533 _sasl_conn_dispose(pconn);
534 }
535
536 /* initialize a client exchange based on the specified mechanism
537 * service -- registered name of the service using SASL (e.g. "imap")
538 * serverFQDN -- the fully qualified domain name of the server
539 * iplocalport -- client IPv4/IPv6 domain literal string with port
540 * (if NULL, then mechanisms requiring IPaddr are disabled)
541 * ipremoteport -- server IPv4/IPv6 domain literal string with port
542 * (if NULL, then mechanisms requiring IPaddr are disabled)
543 * prompt_supp -- list of client interactions supported
544 * may also include sasl_getopt_t context & call
545 * NULL prompt_supp = user/pass via SASL_INTERACT only
546 * NULL proc = interaction supported via SASL_INTERACT
547 * secflags -- security flags (see above)
548 * in/out:
549 * pconn -- connection negotiation structure
550 * pointer to NULL => allocate new
551 * non-NULL => recycle storage and go for next available mech
552 *
553 * Returns:
554 * SASL_OK -- success
555 * SASL_NOMECH -- no mechanism meets requested properties
556 * SASL_NOMEM -- not enough memory
557 */
558 int sasl_client_new(const char *service,
559 const char *serverFQDN,
560 const char *iplocalport,
561 const char *ipremoteport,
562 const sasl_callback_t *prompt_supp,
563 unsigned flags,
564 sasl_conn_t **pconn)
565 {
566 #ifdef _SUN_SDK_
567 return _sasl_client_new(NULL, service, serverFQDN, iplocalport,
568 ipremoteport, prompt_supp, flags, pconn);
569 }
570 int _sasl_client_new(void *ctx,
571 const char *service,
572 const char *serverFQDN,
573 const char *iplocalport,
574 const char *ipremoteport,
575 const sasl_callback_t *prompt_supp,
576 unsigned flags,
577 sasl_conn_t **pconn)
578 {
579 _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
580 #endif /* _SUN_SDK_ */
581 int result;
582 char name[MAXHOSTNAMELEN];
583 sasl_client_conn_t *conn;
584 sasl_utils_t *utils;
585
586 #ifdef _SUN_SDK_
587 if (gctx == NULL)
588 gctx = _sasl_gbl_ctx();
589
590 if(gctx->sasl_client_active==0) return SASL_NOTINIT;
591 #else
592 if(_sasl_client_active==0) return SASL_NOTINIT;
593 #endif /* _SUN_SDK_ */
594
595 /* Remember, iplocalport and ipremoteport can be NULL and be valid! */
596 if (!pconn || !service || !serverFQDN)
597 return SASL_BADPARAM;
598
599 *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t));
600 if (*pconn==NULL) {
601 #ifdef _SUN_SDK_
602 __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR,
603 "Out of memory allocating connection context");
604 #else
605 _sasl_log(NULL, SASL_LOG_ERR,
606 "Out of memory allocating connection context");
607 #endif /* _SUN_SDK_ */
608 return SASL_NOMEM;
609 }
610 memset(*pconn, 0, sizeof(sasl_client_conn_t));
611
612 #ifdef _SUN_SDK_
613 (*pconn)->gctx = gctx;
614 #endif /* _SUN_SDK_ */
615
616 (*pconn)->destroy_conn = &client_dispose;
617
618 conn = (sasl_client_conn_t *)*pconn;
619
620 conn->mech = NULL;
621
622 conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t));
623 if (conn->cparams==NULL)
624 MEMERROR(*pconn);
625 memset(conn->cparams,0,sizeof(sasl_client_params_t));
626
627 result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT,
628 &client_idle, serverFQDN,
629 iplocalport, ipremoteport,
630 #ifdef _SUN_SDK_
631 prompt_supp, &gctx->client_global_callbacks);
632 #else
633 prompt_supp, &global_callbacks);
634 #endif /* _SUN_SDK_ */
635
636 if (result != SASL_OK) RETURN(*pconn, result);
637
638 #ifdef _SUN_SDK_
639 utils=_sasl_alloc_utils(gctx, *pconn, &gctx->client_global_callbacks);
640 #else
641 utils=_sasl_alloc_utils(*pconn, &global_callbacks);
642 #endif /* _SUN_SDK_ */
643 if (utils==NULL)
644 MEMERROR(*pconn);
645
646 utils->conn= *pconn;
647
648 /* Setup the non-lazy parts of cparams, the rest is done in
649 * sasl_client_start */
650 conn->cparams->utils = utils;
651 conn->cparams->canon_user = &_sasl_canon_user;
652 conn->cparams->flags = flags;
653 conn->cparams->prompt_supp = (*pconn)->callbacks;
654
655 /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */
656 memset(name, 0, sizeof(name));
657 gethostname(name, MAXHOSTNAMELEN);
658
659 result = _sasl_strdup(name, &conn->clientFQDN, NULL);
660
661 if(result == SASL_OK) return SASL_OK;
662
663 #ifdef _SUN_SDK_
664 conn->cparams->iplocalport = (*pconn)->iplocalport;
665 conn->cparams->iploclen = strlen((*pconn)->iplocalport);
666 conn->cparams->ipremoteport = (*pconn)->ipremoteport;
667 conn->cparams->ipremlen = strlen((*pconn)->ipremoteport);
668 #endif /* _SUN_SDK_ */
669
670 /* result isn't SASL_OK */
671 _sasl_conn_dispose(*pconn);
672 sasl_FREE(*pconn);
673 *pconn = NULL;
674 #ifdef _SUN_SDK_
675 __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR,
676 "Out of memory in sasl_client_new");
677 #else
678 _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new");
679 #endif /* _SUN_SDK_ */
680 return result;
681 }
682
683 static int have_prompts(sasl_conn_t *conn,
684 const sasl_client_plug_t *mech)
685 {
686 static const unsigned long default_prompts[] = {
687 SASL_CB_AUTHNAME,
688 SASL_CB_PASS,
689 SASL_CB_LIST_END
690 };
691
692 const unsigned long *prompt;
693 int (*pproc)();
694 void *pcontext;
695 int result;
696
697 for (prompt = (mech->required_prompts
698 ? mech->required_prompts :
699 default_prompts);
700 *prompt != SASL_CB_LIST_END;
701 prompt++) {
702 result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext);
703 if (result != SASL_OK && result != SASL_INTERACT)
704 return 0; /* we don't have this required prompt */
705 }
706
707 return 1; /* we have all the prompts */
708 }
709
710 /* select a mechanism for a connection
711 * mechlist -- mechanisms server has available (punctuation ignored)
712 * secret -- optional secret from previous session
713 * output:
714 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
715 * clientout -- the initial client response to send to the server
716 * mech -- set to mechanism name
717 *
718 * Returns:
719 * SASL_OK -- success
720 * SASL_NOMEM -- not enough memory
721 * SASL_NOMECH -- no mechanism meets requested properties
722 * SASL_INTERACT -- user interaction needed to fill in prompt_need list
723 */
724
725 /* xxx confirm this with rfc 2222
726 * SASL mechanism allowable characters are "AZaz-_"
727 * seperators can be any other characters and of any length
728 * even variable lengths between
729 *
730 * Apps should be encouraged to simply use space or comma space
731 * though
732 */
733 int sasl_client_start(sasl_conn_t *conn,
734 const char *mechlist,
735 sasl_interact_t **prompt_need,
736 const char **clientout,
737 unsigned *clientoutlen,
738 const char **mech)
739 {
740 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
741 char name[SASL_MECHNAMEMAX + 1];
742 cmechanism_t *m=NULL,*bestm=NULL;
743 size_t pos=0,place;
744 size_t list_len;
745 sasl_ssf_t bestssf = 0, minssf = 0;
746 int result;
747 #ifdef _SUN_SDK_
748 _sasl_global_context_t *gctx = (conn == NULL) ?
749 _sasl_gbl_ctx() : conn->gctx;
750 cmech_list_t *cmechlist;
751
752 if(gctx->sasl_client_active==0) return SASL_NOTINIT;
753 cmechlist = gctx->cmechlist;
754 #else
755 if(_sasl_client_active==0) return SASL_NOTINIT;
756 #endif /* _SUN_SDK_ */
757
758 if (!conn) return SASL_BADPARAM;
759
760 /* verify parameters */
761 if (mechlist == NULL)
762 PARAMERROR(conn);
763
764 /* if prompt_need != NULL we've already been here
765 and just need to do the continue step again */
766
767 /* do a step */
768 /* FIXME: Hopefully they only give us our own prompt_need back */
769 if (prompt_need && *prompt_need != NULL) {
770 goto dostep;
771 }
772
773 #ifdef _SUN_SDK_
774 if (c_conn->mech != NULL) {
775 if (c_conn->mech->plug->mech_dispose != NULL) {
776 c_conn->mech->plug->mech_dispose(conn->context,
777 c_conn->cparams->utils);
778 c_conn->mech = NULL;
779 }
780 }
781 memset(&conn->oparams, 0, sizeof(sasl_out_params_t));
782
783 (void) _load_client_plugins(gctx);
784 #endif /* _SUN_SDK_ */
785
786 if(conn->props.min_ssf < conn->external.ssf) {
787 minssf = 0;
788 } else {
789 minssf = conn->props.min_ssf - conn->external.ssf;
790 }
791
792 /* parse mechlist */
793 list_len = strlen(mechlist);
794
795 while (pos<list_len)
796 {
797 place=0;
798 while ((pos<list_len) && (isalnum((unsigned char)mechlist[pos])
799 || mechlist[pos] == '_'
800 || mechlist[pos] == '-')) {
801 name[place]=mechlist[pos];
802 pos++;
803 place++;
804 if (SASL_MECHNAMEMAX < place) {
805 place--;
806 while(pos<list_len && (isalnum((unsigned char)mechlist[pos])
807 || mechlist[pos] == '_'
808 || mechlist[pos] == '-'))
809 pos++;
810 }
811 }
812 pos++;
813 name[place]=0;
814
815 if (! place) continue;
816
817 /* foreach in server list */
818 for (m = cmechlist->mech_list; m != NULL; m = m->next) {
819 int myflags;
820
821 /* Is this the mechanism the server is suggesting? */
822 if (strcasecmp(m->plug->mech_name, name))
823 continue; /* no */
824
825 /* Do we have the prompts for it? */
826 if (!have_prompts(conn, m->plug))
827 break;
828
829 /* Is it strong enough? */
830 if (minssf > m->plug->max_ssf)
831 break;
832
833 #ifdef _INTEGRATED_SOLARIS_
834 /* If not SUN supplied mech, it has no strength */
835 if (minssf > 0 && !m->sun_reg)
836 break;
837 #endif /* _INTEGRATED_SOLARIS_ */
838
839 /* Does it meet our security properties? */
840 myflags = conn->props.security_flags;
841
842 /* if there's an external layer this is no longer plaintext */
843 if ((conn->props.min_ssf <= conn->external.ssf) &&
844 (conn->external.ssf > 1)) {
845 myflags &= ~SASL_SEC_NOPLAINTEXT;
846 }
847
848 if (((myflags ^ m->plug->security_flags) & myflags) != 0) {
849 break;
850 }
851
852 /* Can we meet it's features? */
853 if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN)
854 && !conn->serverFQDN) {
855 break;
856 }
857
858 /* Can it meet our features? */
859 if ((conn->flags & SASL_NEED_PROXY) &&
860 !(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) {
861 break;
862 }
863
864 #ifdef PREFER_MECH
865 #ifdef _INTEGRATED_SOLARIS_
866 if (strcasecmp(m->plug->mech_name, PREFER_MECH) &&
867 bestm && (m->sun_reg && m->plug->max_ssf <= bestssf) ||
868 (m->plug->max_ssf == 0)) {
869 #else
870 if (strcasecmp(m->plug->mech_name, PREFER_MECH) &&
871 bestm && m->plug->max_ssf <= bestssf) {
872 #endif /* _INTEGRATED_SOLARIS_ */
873
874 /* this mechanism isn't our favorite, and it's no better
875 than what we already have! */
876 break;
877 }
878 #else
879 #ifdef _INTEGRATED_SOLARIS_
880 if (bestm && m->sun_reg && m->plug->max_ssf <= bestssf) {
881 #else
882
883 if (bestm && m->plug->max_ssf <= bestssf) {
884 #endif /* _INTEGRATED_SOLARIS_ */
885
886 /* this mechanism is no better than what we already have! */
887 break;
888 }
889 #endif
890
891 /* compare security flags, only take new mechanism if it has
892 * all the security flags of the previous one.
893 *
894 * From the mechanisms we ship with, this yields the order:
895 *
896 * SRP
897 * GSSAPI + KERBEROS_V4
898 * DIGEST + OTP
899 * CRAM + EXTERNAL
900 * PLAIN + LOGIN + ANONYMOUS
901 *
902 * This might be improved on by comparing the numeric value of
903 * the bitwise-or'd security flags, which splits DIGEST/OTP,
904 * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we
905 * are depending on the numeric values of the flags (which may
906 * change, and their ordering could be considered dumb luck.
907 */
908
909 if (bestm &&
910 ((m->plug->security_flags ^ bestm->plug->security_flags) &
911 bestm->plug->security_flags)) {
912 break;
913 }
914
915 if (mech) {
916 *mech = m->plug->mech_name;
917 }
918 #ifdef _INTEGRATED_SOLARIS_
919 bestssf = m->sun_reg ? m->plug->max_ssf : 0;
920 #else
921 bestssf = m->plug->max_ssf;
922 #endif /* _INTEGRATED_SOLARIS_ */
923 bestm = m;
924 break;
925 }
926 }
927
928 if (bestm == NULL) {
929 #ifdef _INTEGRATED_SOLARIS_
930 sasl_seterror(conn, 0, gettext("No worthy mechs found"));
931 #else
932 sasl_seterror(conn, 0, "No worthy mechs found");
933 #endif /* _INTEGRATED_SOLARIS_ */
934 result = SASL_NOMECH;
935 goto done;
936 }
937
938 /* make (the rest of) cparams */
939 c_conn->cparams->service = conn->service;
940 c_conn->cparams->servicelen = strlen(conn->service);
941
942 c_conn->cparams->serverFQDN = conn->serverFQDN;
943 c_conn->cparams->slen = strlen(conn->serverFQDN);
944
945 c_conn->cparams->clientFQDN = c_conn->clientFQDN;
946 c_conn->cparams->clen = strlen(c_conn->clientFQDN);
947
948 c_conn->cparams->external_ssf = conn->external.ssf;
949 c_conn->cparams->props = conn->props;
950 #ifdef _INTEGRATED_SOLARIS_
951 if (!bestm->sun_reg) {
952 c_conn->cparams->props.min_ssf = 0;
953 c_conn->cparams->props.max_ssf = 0;
954 }
955 c_conn->base.sun_reg = bestm->sun_reg;
956 #endif /* _INTEGRATED_SOLARIS_ */
957 c_conn->mech = bestm;
958
959 /* init that plugin */
960 #ifdef _SUN_SDK_
961 result = c_conn->mech->plug->mech_new(c_conn->mech->glob_context,
962 #else
963 result = c_conn->mech->plug->mech_new(c_conn->mech->plug->glob_context,
964 #endif /* _SUN_SDK_ */
965 c_conn->cparams,
966 &(conn->context));
967 if(result != SASL_OK) goto done;
968
969 /* do a step -- but only if we can do a client-send-first */
970 dostep:
971 if(clientout) {
972 if(c_conn->mech->plug->features & SASL_FEAT_SERVER_FIRST) {
973 *clientout = NULL;
974 *clientoutlen = 0;
975 result = SASL_CONTINUE;
976 } else {
977 result = sasl_client_step(conn, NULL, 0, prompt_need,
978 clientout, clientoutlen);
979 }
980 }
981 else
982 result = SASL_CONTINUE;
983
984 done:
985 RETURN(conn, result);
986 }
987
988 /* do a single authentication step.
989 * serverin -- the server message received by the client, MUST have a NUL
990 * sentinel, not counted by serverinlen
991 * output:
992 * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
993 * clientout -- the client response to send to the server
994 *
995 * returns:
996 * SASL_OK -- success
997 * SASL_INTERACT -- user interaction needed to fill in prompt_need list
998 * SASL_BADPROT -- server protocol incorrect/cancelled
999 * SASL_BADSERV -- server failed mutual auth
1000 */
1001
1002 int sasl_client_step(sasl_conn_t *conn,
1003 const char *serverin,
1004 unsigned serverinlen,
1005 sasl_interact_t **prompt_need,
1006 const char **clientout,
1007 unsigned *clientoutlen)
1008 {
1009 sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
1010 int result;
1011
1012 #ifdef _SUN_SDK_
1013 _sasl_global_context_t *gctx = (conn == NULL) ?
1014 _sasl_gbl_ctx() : conn->gctx;
1015
1016 if(gctx->sasl_client_active==0) return SASL_NOTINIT;
1017 #else
1018 if(_sasl_client_active==0) return SASL_NOTINIT;
1019 #endif /* _SUN_SDK_ */
1020 if(!conn) return SASL_BADPARAM;
1021
1022 /* check parameters */
1023 if ((serverin==NULL) && (serverinlen>0))
1024 PARAMERROR(conn);
1025
1026 /* Don't do another step if the plugin told us that we're done */
1027 if (conn->oparams.doneflag) {
1028 _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag");
1029 return SASL_FAIL;
1030 }
1031
1032 if(clientout) *clientout = NULL;
1033 if(clientoutlen) *clientoutlen = 0;
1034
1035 /* do a step */
1036 result = c_conn->mech->plug->mech_step(conn->context,
1037 c_conn->cparams,
1038 serverin,
1039 serverinlen,
1040 prompt_need,
1041 clientout, clientoutlen,
1042 &conn->oparams);
1043
1044 if (result == SASL_OK) {
1045 /* So we're done on this end, but if both
1046 * 1. the mech does server-send-last
1047 * 2. the protocol does not
1048 * we need to return no data */
1049 if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) {
1050 *clientout = "";
1051 *clientoutlen = 0;
1052 }
1053
1054 if(!conn->oparams.maxoutbuf) {
1055 conn->oparams.maxoutbuf = conn->props.maxbufsize;
1056 }
1057
1058 if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
1059 #ifdef _SUN_SDK_
1060 _sasl_log(conn, SASL_LOG_ERR,
1061 "mech did not call canon_user for both authzid and authid");
1062 #else
1063 sasl_seterror(conn, 0,
1064 "mech did not call canon_user for both authzid and authid");
1065 #endif /* _SUN_SDK_ */
1066 result = SASL_BADPROT;
1067 }
1068 }
1069
1070 RETURN(conn,result);
1071 }
1072
1073 /* returns the length of all the mechanisms
1074 * added up
1075 */
1076
1077 #ifdef _SUN_SDK_
1078 static unsigned mech_names_len(_sasl_global_context_t *gctx)
1079 {
1080 cmech_list_t *cmechlist = gctx->cmechlist;
1081 #else
1082 static unsigned mech_names_len()
1083 {
1084 #endif /* _SUN_SDK_ */
1085 cmechanism_t *listptr;
1086 unsigned result = 0;
1087
1088 for (listptr = cmechlist->mech_list;
1089 listptr;
1090 listptr = listptr->next)
1091 result += strlen(listptr->plug->mech_name);
1092
1093 return result;
1094 }
1095
1096
1097 int _sasl_client_listmech(sasl_conn_t *conn,
1098 const char *prefix,
1099 const char *sep,
1100 const char *suffix,
1101 const char **result,
1102 unsigned *plen,
1103 int *pcount)
1104 {
1105 cmechanism_t *m=NULL;
1106 sasl_ssf_t minssf = 0;
1107 int ret;
1108 unsigned int resultlen;
1109 int flag;
1110 const char *mysep;
1111 #ifdef _SUN_SDK_
1112 _sasl_global_context_t *gctx = conn == NULL ? _sasl_gbl_ctx() : conn->gctx;
1113 cmech_list_t *cmechlist;
1114
1115 if(gctx->sasl_client_active==0) return SASL_NOTINIT;
1116 cmechlist = gctx->cmechlist;
1117 #else
1118 if(_sasl_client_active == 0) return SASL_NOTINIT;
1119 #endif /* _SUN_SDK_ */
1120 if (!conn) return SASL_BADPARAM;
1121 if(conn->type != SASL_CONN_CLIENT) PARAMERROR(conn);
1122
1123 if (! result)
1124 PARAMERROR(conn);
1125
1126 #ifdef _SUN_SDK_
1127 (void) _load_client_plugins(gctx);
1128 #endif /* _SUN_SDK_ */
1129
1130 if (plen != NULL)
1131 *plen = 0;
1132 if (pcount != NULL)
1133 *pcount = 0;
1134
1135 if (sep) {
1136 mysep = sep;
1137 } else {
1138 mysep = " ";
1139 }
1140
1141 if(conn->props.min_ssf < conn->external.ssf) {
1142 minssf = 0;
1143 } else {
1144 minssf = conn->props.min_ssf - conn->external.ssf;
1145 }
1146
1147 if (! cmechlist || cmechlist->mech_length <= 0)
1148 INTERROR(conn, SASL_NOMECH);
1149
1150 resultlen = (prefix ? strlen(prefix) : 0)
1151 + (strlen(mysep) * (cmechlist->mech_length - 1))
1152 #ifdef _SUN_SDK_
1153 + mech_names_len(gctx)
1154 #else
1155 + mech_names_len()
1156 #endif /* _SUN_SDK_ */
1157 + (suffix ? strlen(suffix) : 0)
1158 + 1;
1159 ret = _buf_alloc(&conn->mechlist_buf,
1160 &conn->mechlist_buf_len, resultlen);
1161 if(ret != SASL_OK) MEMERROR(conn);
1162
1163 if (prefix)
1164 strcpy (conn->mechlist_buf,prefix);
1165 else
1166 *(conn->mechlist_buf) = '\0';
1167
1168 flag = 0;
1169 for (m = cmechlist->mech_list; m != NULL; m = m->next) {
1170 /* do we have the prompts for it? */
1171 if (!have_prompts(conn, m->plug))
1172 continue;
1173
1174 /* is it strong enough? */
1175 if (minssf > m->plug->max_ssf)
1176 continue;
1177
1178 #ifdef _INTEGRATED_SOLARIS_
1179 /* If not SUN supplied mech, it has no strength */
1180 if (minssf > 0 && !m->sun_reg)
1181 continue;
1182 #endif /* _INTEGRATED_SOLARIS_ */
1183
1184 /* does it meet our security properties? */
1185 if (((conn->props.security_flags ^ m->plug->security_flags)
1186 & conn->props.security_flags) != 0) {
1187 continue;
1188 }
1189
1190 /* Can we meet it's features? */
1191 if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN)
1192 && !conn->serverFQDN) {
1193 continue;
1194 }
1195
1196 /* Can it meet our features? */
1197 if ((conn->flags & SASL_NEED_PROXY) &&
1198 !(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) {
1199 break;
1200 }
1201
1202 /* Okay, we like it, add it to the list! */
1203
1204 if (pcount != NULL)
1205 (*pcount)++;
1206
1207 /* print seperator */
1208 if (flag) {
1209 strcat(conn->mechlist_buf, mysep);
1210 } else {
1211 flag = 1;
1212 }
1213
1214 /* now print the mechanism name */
1215 strcat(conn->mechlist_buf, m->plug->mech_name);
1216 }
1217
1218 if (suffix)
1219 strcat(conn->mechlist_buf,suffix);
1220
1221 if (plen!=NULL)
1222 *plen=strlen(conn->mechlist_buf);
1223
1224 *result = conn->mechlist_buf;
1225
1226 return SASL_OK;
1227 }
1228
1229 #ifdef _SUN_SDK_
1230 sasl_string_list_t *_sasl_client_mechs(_sasl_global_context_t *gctx)
1231 {
1232 cmech_list_t *cmechlist = gctx->cmechlist;
1233 #else
1234 sasl_string_list_t *_sasl_client_mechs(void)
1235 {
1236 #endif /* _SUN_SDK_ */
1237 cmechanism_t *listptr;
1238 sasl_string_list_t *retval = NULL, *next=NULL;
1239
1240 #ifdef _SUN_SDK_
1241 if(!gctx->sasl_client_active) return NULL;
1242 #else
1243 if(!_sasl_client_active) return NULL;
1244 #endif /* _SUN_SDK_ */
1245
1246 /* make list */
1247 for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) {
1248 next = sasl_ALLOC(sizeof(sasl_string_list_t));
1249
1250 if(!next && !retval) return NULL;
1251 else if(!next) {
1252 next = retval->next;
1253 do {
1254 sasl_FREE(retval);
1255 retval = next;
1256 next = retval->next;
1257 } while(next);
1258 return NULL;
1259 }
1260
1261 next->d = listptr->plug->mech_name;
1262
1263 if(!retval) {
1264 next->next = NULL;
1265 retval = next;
1266 } else {
1267 next->next = retval;
1268 retval = next;
1269 }
1270 }
1271
1272 return retval;
1273 }
1274