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