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