1 | /* SASL server API implementation |
---|
2 | * Rob Siemborski |
---|
3 | * Tim Martin |
---|
4 | * $Id: client.c,v 1.2 2003-07-07 20:26:35 rbasch Exp $ |
---|
5 | */ |
---|
6 | /* |
---|
7 | * Copyright (c) 2001 Carnegie Mellon University. All rights reserved. |
---|
8 | * |
---|
9 | * Redistribution and use in source and binary forms, with or without |
---|
10 | * modification, are permitted provided that the following conditions |
---|
11 | * are met: |
---|
12 | * |
---|
13 | * 1. Redistributions of source code must retain the above copyright |
---|
14 | * notice, this list of conditions and the following disclaimer. |
---|
15 | * |
---|
16 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
17 | * notice, this list of conditions and the following disclaimer in |
---|
18 | * the documentation and/or other materials provided with the |
---|
19 | * distribution. |
---|
20 | * |
---|
21 | * 3. The name "Carnegie Mellon University" must not be used to |
---|
22 | * endorse or promote products derived from this software without |
---|
23 | * prior written permission. For permission or any other legal |
---|
24 | * details, please contact |
---|
25 | * Office of Technology Transfer |
---|
26 | * Carnegie Mellon University |
---|
27 | * 5000 Forbes Avenue |
---|
28 | * Pittsburgh, PA 15213-3890 |
---|
29 | * (412) 268-4387, fax: (412) 268-7395 |
---|
30 | * tech-transfer@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 | |
---|
64 | static sasl_global_callbacks_t global_callbacks; |
---|
65 | |
---|
66 | static int _sasl_client_active = 0; |
---|
67 | |
---|
68 | static int init_mechlist() |
---|
69 | { |
---|
70 | cmechlist->mutex = sasl_MUTEX_ALLOC(); |
---|
71 | if(!cmechlist->mutex) return SASL_FAIL; |
---|
72 | |
---|
73 | cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks); |
---|
74 | if (cmechlist->utils==NULL) |
---|
75 | return SASL_NOMEM; |
---|
76 | |
---|
77 | cmechlist->mech_list=NULL; |
---|
78 | cmechlist->mech_length=0; |
---|
79 | |
---|
80 | return SASL_OK; |
---|
81 | } |
---|
82 | |
---|
83 | static void client_done(void) { |
---|
84 | cmechanism_t *cm; |
---|
85 | cmechanism_t *cprevm; |
---|
86 | |
---|
87 | cm=cmechlist->mech_list; /* m point to begging of the list */ |
---|
88 | while (cm!=NULL) |
---|
89 | { |
---|
90 | cprevm=cm; |
---|
91 | cm=cm->next; |
---|
92 | |
---|
93 | if (cprevm->plug->mech_free) { |
---|
94 | cprevm->plug->mech_free(cprevm->plug->glob_context, |
---|
95 | cmechlist->utils); |
---|
96 | } |
---|
97 | |
---|
98 | sasl_FREE(cprevm->plugname); |
---|
99 | sasl_FREE(cprevm); |
---|
100 | } |
---|
101 | sasl_MUTEX_FREE(cmechlist->mutex); |
---|
102 | _sasl_free_utils(&cmechlist->utils); |
---|
103 | sasl_FREE(cmechlist); |
---|
104 | |
---|
105 | cmechlist = NULL; |
---|
106 | |
---|
107 | _sasl_client_active = 0; |
---|
108 | } |
---|
109 | |
---|
110 | int sasl_client_add_plugin(const char *plugname, |
---|
111 | sasl_client_plug_init_t *entry_point) |
---|
112 | { |
---|
113 | int plugcount; |
---|
114 | sasl_client_plug_t *pluglist; |
---|
115 | cmechanism_t *mech; |
---|
116 | int result; |
---|
117 | int version; |
---|
118 | int lupe; |
---|
119 | |
---|
120 | if(!plugname || !entry_point) return SASL_BADPARAM; |
---|
121 | |
---|
122 | result = entry_point(cmechlist->utils, SASL_CLIENT_PLUG_VERSION, &version, |
---|
123 | &pluglist, &plugcount); |
---|
124 | |
---|
125 | if (result != SASL_OK) |
---|
126 | { |
---|
127 | _sasl_log(NULL, SASL_LOG_WARN, |
---|
128 | "entry_point failed in sasl_client_add_plugin for %s", |
---|
129 | plugname); |
---|
130 | return result; |
---|
131 | } |
---|
132 | |
---|
133 | if (version != SASL_CLIENT_PLUG_VERSION) |
---|
134 | { |
---|
135 | _sasl_log(NULL, SASL_LOG_WARN, |
---|
136 | "version conflict in sasl_client_add_plugin for %s", plugname); |
---|
137 | return SASL_BADVERS; |
---|
138 | } |
---|
139 | |
---|
140 | for (lupe=0;lupe< plugcount ;lupe++) |
---|
141 | { |
---|
142 | mech = sasl_ALLOC(sizeof(cmechanism_t)); |
---|
143 | if (! mech) return SASL_NOMEM; |
---|
144 | |
---|
145 | mech->plug=pluglist++; |
---|
146 | if(_sasl_strdup(plugname, &mech->plugname, NULL) != SASL_OK) { |
---|
147 | sasl_FREE(mech); |
---|
148 | return SASL_NOMEM; |
---|
149 | } |
---|
150 | mech->version = version; |
---|
151 | mech->next = cmechlist->mech_list; |
---|
152 | cmechlist->mech_list = mech; |
---|
153 | cmechlist->mech_length++; |
---|
154 | } |
---|
155 | |
---|
156 | return SASL_OK; |
---|
157 | } |
---|
158 | |
---|
159 | static int |
---|
160 | client_idle(sasl_conn_t *conn) |
---|
161 | { |
---|
162 | cmechanism_t *m; |
---|
163 | if (! cmechlist) |
---|
164 | return 0; |
---|
165 | |
---|
166 | for (m = cmechlist->mech_list; |
---|
167 | m; |
---|
168 | m = m->next) |
---|
169 | if (m->plug->idle |
---|
170 | && m->plug->idle(m->plug->glob_context, |
---|
171 | conn, |
---|
172 | conn ? ((sasl_client_conn_t *)conn)->cparams : NULL)) |
---|
173 | return 1; |
---|
174 | return 0; |
---|
175 | } |
---|
176 | |
---|
177 | /* initialize the SASL client drivers |
---|
178 | * callbacks -- base callbacks for all client connections |
---|
179 | * returns: |
---|
180 | * SASL_OK -- Success |
---|
181 | * SASL_NOMEM -- Not enough memory |
---|
182 | * SASL_BADVERS -- Mechanism version mismatch |
---|
183 | * SASL_BADPARAM -- error in config file |
---|
184 | * SASL_NOMECH -- No mechanisms available |
---|
185 | * ... |
---|
186 | */ |
---|
187 | |
---|
188 | int sasl_client_init(const sasl_callback_t *callbacks) |
---|
189 | { |
---|
190 | int ret; |
---|
191 | const add_plugin_list_t ep_list[] = { |
---|
192 | { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin }, |
---|
193 | { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin }, |
---|
194 | { NULL, NULL } |
---|
195 | }; |
---|
196 | |
---|
197 | _sasl_client_cleanup_hook = &client_done; |
---|
198 | _sasl_client_idle_hook = &client_idle; |
---|
199 | |
---|
200 | global_callbacks.callbacks = callbacks; |
---|
201 | global_callbacks.appname = NULL; |
---|
202 | |
---|
203 | cmechlist=sasl_ALLOC(sizeof(cmech_list_t)); |
---|
204 | if (cmechlist==NULL) return SASL_NOMEM; |
---|
205 | |
---|
206 | /* load plugins */ |
---|
207 | ret=init_mechlist(); |
---|
208 | if (ret!=SASL_OK) |
---|
209 | return ret; |
---|
210 | |
---|
211 | sasl_client_add_plugin("EXTERNAL", &external_client_plug_init); |
---|
212 | |
---|
213 | ret = _sasl_common_init(); |
---|
214 | |
---|
215 | if (ret == SASL_OK) |
---|
216 | ret = _sasl_load_plugins(ep_list, |
---|
217 | _sasl_find_getpath_callback(callbacks), |
---|
218 | _sasl_find_verifyfile_callback(callbacks)); |
---|
219 | |
---|
220 | if (ret == SASL_OK) { |
---|
221 | _sasl_client_active = 1; |
---|
222 | ret = _sasl_build_mechlist(); |
---|
223 | } |
---|
224 | |
---|
225 | return ret; |
---|
226 | } |
---|
227 | |
---|
228 | static void client_dispose(sasl_conn_t *pconn) |
---|
229 | { |
---|
230 | sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn; |
---|
231 | |
---|
232 | if (c_conn->mech && c_conn->mech->plug->mech_dispose) { |
---|
233 | c_conn->mech->plug->mech_dispose(pconn->context, |
---|
234 | c_conn->cparams->utils); |
---|
235 | } |
---|
236 | |
---|
237 | pconn->context = NULL; |
---|
238 | |
---|
239 | if (c_conn->clientFQDN) |
---|
240 | sasl_FREE(c_conn->clientFQDN); |
---|
241 | |
---|
242 | if (c_conn->cparams) { |
---|
243 | _sasl_free_utils(&(c_conn->cparams->utils)); |
---|
244 | sasl_FREE(c_conn->cparams); |
---|
245 | } |
---|
246 | |
---|
247 | _sasl_conn_dispose(pconn); |
---|
248 | } |
---|
249 | |
---|
250 | /* initialize a client exchange based on the specified mechanism |
---|
251 | * service -- registered name of the service using SASL (e.g. "imap") |
---|
252 | * serverFQDN -- the fully qualified domain name of the server |
---|
253 | * iplocalport -- client IPv4/IPv6 domain literal string with port |
---|
254 | * (if NULL, then mechanisms requiring IPaddr are disabled) |
---|
255 | * ipremoteport -- server IPv4/IPv6 domain literal string with port |
---|
256 | * (if NULL, then mechanisms requiring IPaddr are disabled) |
---|
257 | * prompt_supp -- list of client interactions supported |
---|
258 | * may also include sasl_getopt_t context & call |
---|
259 | * NULL prompt_supp = user/pass via SASL_INTERACT only |
---|
260 | * NULL proc = interaction supported via SASL_INTERACT |
---|
261 | * secflags -- security flags (see above) |
---|
262 | * in/out: |
---|
263 | * pconn -- connection negotiation structure |
---|
264 | * pointer to NULL => allocate new |
---|
265 | * non-NULL => recycle storage and go for next available mech |
---|
266 | * |
---|
267 | * Returns: |
---|
268 | * SASL_OK -- success |
---|
269 | * SASL_NOMECH -- no mechanism meets requested properties |
---|
270 | * SASL_NOMEM -- not enough memory |
---|
271 | */ |
---|
272 | int sasl_client_new(const char *service, |
---|
273 | const char *serverFQDN, |
---|
274 | const char *iplocalport, |
---|
275 | const char *ipremoteport, |
---|
276 | const sasl_callback_t *prompt_supp, |
---|
277 | unsigned flags, |
---|
278 | sasl_conn_t **pconn) |
---|
279 | { |
---|
280 | int result; |
---|
281 | char name[MAXHOSTNAMELEN]; |
---|
282 | sasl_client_conn_t *conn; |
---|
283 | sasl_utils_t *utils; |
---|
284 | |
---|
285 | if(_sasl_client_active==0) return SASL_NOTINIT; |
---|
286 | |
---|
287 | /* Remember, iplocalport and ipremoteport can be NULL and be valid! */ |
---|
288 | if (!pconn || !service || !serverFQDN) |
---|
289 | return SASL_BADPARAM; |
---|
290 | |
---|
291 | *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t)); |
---|
292 | if (*pconn==NULL) { |
---|
293 | _sasl_log(NULL, SASL_LOG_ERR, |
---|
294 | "Out of memory allocating connection context"); |
---|
295 | return SASL_NOMEM; |
---|
296 | } |
---|
297 | memset(*pconn, 0, sizeof(sasl_client_conn_t)); |
---|
298 | |
---|
299 | (*pconn)->destroy_conn = &client_dispose; |
---|
300 | |
---|
301 | conn = (sasl_client_conn_t *)*pconn; |
---|
302 | |
---|
303 | conn->mech = NULL; |
---|
304 | |
---|
305 | conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t)); |
---|
306 | if (conn->cparams==NULL) |
---|
307 | MEMERROR(*pconn); |
---|
308 | memset(conn->cparams,0,sizeof(sasl_client_params_t)); |
---|
309 | |
---|
310 | result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT, |
---|
311 | &client_idle, serverFQDN, |
---|
312 | iplocalport, ipremoteport, |
---|
313 | prompt_supp, &global_callbacks); |
---|
314 | if (result != SASL_OK) RETURN(*pconn, result); |
---|
315 | |
---|
316 | utils=_sasl_alloc_utils(*pconn, &global_callbacks); |
---|
317 | if (utils==NULL) |
---|
318 | MEMERROR(*pconn); |
---|
319 | |
---|
320 | utils->conn= *pconn; |
---|
321 | |
---|
322 | /* Setup the non-lazy parts of cparams, the rest is done in |
---|
323 | * sasl_client_start */ |
---|
324 | conn->cparams->utils = utils; |
---|
325 | conn->cparams->canon_user = &_sasl_canon_user; |
---|
326 | conn->cparams->flags = flags; |
---|
327 | |
---|
328 | /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */ |
---|
329 | memset(name, 0, sizeof(name)); |
---|
330 | gethostname(name, MAXHOSTNAMELEN); |
---|
331 | |
---|
332 | result = _sasl_strdup(name, &conn->clientFQDN, NULL); |
---|
333 | |
---|
334 | if(result == SASL_OK) return SASL_OK; |
---|
335 | |
---|
336 | /* result isn't SASL_OK */ |
---|
337 | _sasl_conn_dispose(*pconn); |
---|
338 | sasl_FREE(*pconn); |
---|
339 | *pconn = NULL; |
---|
340 | _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new"); |
---|
341 | return result; |
---|
342 | } |
---|
343 | |
---|
344 | static int have_prompts(sasl_conn_t *conn, |
---|
345 | const sasl_client_plug_t *mech) |
---|
346 | { |
---|
347 | static const unsigned long default_prompts[] = { |
---|
348 | SASL_CB_AUTHNAME, |
---|
349 | SASL_CB_PASS, |
---|
350 | SASL_CB_LIST_END |
---|
351 | }; |
---|
352 | |
---|
353 | const unsigned long *prompt; |
---|
354 | int (*pproc)(); |
---|
355 | void *pcontext; |
---|
356 | int result; |
---|
357 | |
---|
358 | for (prompt = (mech->required_prompts |
---|
359 | ? mech->required_prompts : |
---|
360 | default_prompts); |
---|
361 | *prompt != SASL_CB_LIST_END; |
---|
362 | prompt++) { |
---|
363 | result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext); |
---|
364 | if (result != SASL_OK && result != SASL_INTERACT) |
---|
365 | return 0; /* we don't have this required prompt */ |
---|
366 | } |
---|
367 | |
---|
368 | return 1; /* we have all the prompts */ |
---|
369 | } |
---|
370 | |
---|
371 | /* select a mechanism for a connection |
---|
372 | * mechlist -- mechanisms server has available (punctuation ignored) |
---|
373 | * secret -- optional secret from previous session |
---|
374 | * output: |
---|
375 | * prompt_need -- on SASL_INTERACT, list of prompts needed to continue |
---|
376 | * clientout -- the initial client response to send to the server |
---|
377 | * mech -- set to mechanism name |
---|
378 | * |
---|
379 | * Returns: |
---|
380 | * SASL_OK -- success |
---|
381 | * SASL_NOMEM -- not enough memory |
---|
382 | * SASL_NOMECH -- no mechanism meets requested properties |
---|
383 | * SASL_INTERACT -- user interaction needed to fill in prompt_need list |
---|
384 | */ |
---|
385 | |
---|
386 | /* xxx confirm this with rfc 2222 |
---|
387 | * SASL mechanism allowable characters are "AZaz-_" |
---|
388 | * seperators can be any other characters and of any length |
---|
389 | * even variable lengths between |
---|
390 | * |
---|
391 | * Apps should be encouraged to simply use space or comma space |
---|
392 | * though |
---|
393 | */ |
---|
394 | int sasl_client_start(sasl_conn_t *conn, |
---|
395 | const char *mechlist, |
---|
396 | sasl_interact_t **prompt_need, |
---|
397 | const char **clientout, |
---|
398 | unsigned *clientoutlen, |
---|
399 | const char **mech) |
---|
400 | { |
---|
401 | sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn; |
---|
402 | char name[SASL_MECHNAMEMAX + 1]; |
---|
403 | cmechanism_t *m=NULL,*bestm=NULL; |
---|
404 | size_t pos=0,place; |
---|
405 | size_t list_len; |
---|
406 | sasl_ssf_t bestssf = 0, minssf = 0; |
---|
407 | int result; |
---|
408 | |
---|
409 | if(_sasl_client_active==0) return SASL_NOTINIT; |
---|
410 | |
---|
411 | if (!conn) return SASL_BADPARAM; |
---|
412 | |
---|
413 | /* verify parameters */ |
---|
414 | if (mechlist == NULL) |
---|
415 | PARAMERROR(conn); |
---|
416 | |
---|
417 | /* if prompt_need != NULL we've already been here |
---|
418 | and just need to do the continue step again */ |
---|
419 | |
---|
420 | /* do a step */ |
---|
421 | /* FIXME: Hopefully they only give us our own prompt_need back */ |
---|
422 | if (prompt_need && *prompt_need != NULL) { |
---|
423 | goto dostep; |
---|
424 | } |
---|
425 | |
---|
426 | if(conn->props.min_ssf < conn->external.ssf) { |
---|
427 | minssf = 0; |
---|
428 | } else { |
---|
429 | minssf = conn->props.min_ssf - conn->external.ssf; |
---|
430 | } |
---|
431 | |
---|
432 | /* parse mechlist */ |
---|
433 | list_len = strlen(mechlist); |
---|
434 | |
---|
435 | while (pos<list_len) |
---|
436 | { |
---|
437 | place=0; |
---|
438 | while ((pos<list_len) && (isalnum((unsigned char)mechlist[pos]) |
---|
439 | || mechlist[pos] == '_' |
---|
440 | || mechlist[pos] == '-')) { |
---|
441 | name[place]=mechlist[pos]; |
---|
442 | pos++; |
---|
443 | place++; |
---|
444 | if (SASL_MECHNAMEMAX < place) { |
---|
445 | place--; |
---|
446 | while(pos<list_len && (isalnum((unsigned char)mechlist[pos]) |
---|
447 | || mechlist[pos] == '_' |
---|
448 | || mechlist[pos] == '-')) |
---|
449 | pos++; |
---|
450 | } |
---|
451 | } |
---|
452 | pos++; |
---|
453 | name[place]=0; |
---|
454 | |
---|
455 | if (! place) continue; |
---|
456 | |
---|
457 | /* foreach in server list */ |
---|
458 | for (m = cmechlist->mech_list; m != NULL; m = m->next) { |
---|
459 | int myflags; |
---|
460 | |
---|
461 | /* Is this the mechanism the server is suggesting? */ |
---|
462 | if (strcasecmp(m->plug->mech_name, name)) |
---|
463 | continue; /* no */ |
---|
464 | |
---|
465 | /* Do we have the prompts for it? */ |
---|
466 | if (!have_prompts(conn, m->plug)) |
---|
467 | break; |
---|
468 | |
---|
469 | /* Is it strong enough? */ |
---|
470 | if (minssf > m->plug->max_ssf) |
---|
471 | break; |
---|
472 | |
---|
473 | /* Does it meet our security properties? */ |
---|
474 | myflags = conn->props.security_flags; |
---|
475 | |
---|
476 | /* if there's an external layer this is no longer plaintext */ |
---|
477 | if ((conn->props.min_ssf <= conn->external.ssf) && |
---|
478 | (conn->external.ssf > 1)) { |
---|
479 | myflags &= ~SASL_SEC_NOPLAINTEXT; |
---|
480 | } |
---|
481 | |
---|
482 | if (((myflags ^ m->plug->security_flags) & myflags) != 0) { |
---|
483 | break; |
---|
484 | } |
---|
485 | |
---|
486 | /* Can we meet it's features? */ |
---|
487 | if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN) |
---|
488 | && !conn->serverFQDN) { |
---|
489 | break; |
---|
490 | } |
---|
491 | |
---|
492 | /* Can it meet our features? */ |
---|
493 | if ((conn->flags & SASL_NEED_PROXY) && |
---|
494 | !(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) { |
---|
495 | break; |
---|
496 | } |
---|
497 | |
---|
498 | #ifdef PREFER_MECH |
---|
499 | if (strcasecmp(m->plug->mech_name, PREFER_MECH) && |
---|
500 | bestm && m->plug->max_ssf <= bestssf) { |
---|
501 | /* this mechanism isn't our favorite, and it's no better |
---|
502 | than what we already have! */ |
---|
503 | break; |
---|
504 | } |
---|
505 | #else |
---|
506 | if (bestm && m->plug->max_ssf <= bestssf) { |
---|
507 | /* this mechanism is no better than what we already have! */ |
---|
508 | break; |
---|
509 | } |
---|
510 | #endif |
---|
511 | |
---|
512 | /* compare security flags, only take new mechanism if it has |
---|
513 | * all the security flags of the previous one. |
---|
514 | * |
---|
515 | * From the mechanisms we ship with, this yields the order: |
---|
516 | * |
---|
517 | * SRP |
---|
518 | * GSSAPI + KERBEROS_V4 |
---|
519 | * DIGEST + OTP |
---|
520 | * CRAM + EXTERNAL |
---|
521 | * PLAIN + LOGIN + ANONYMOUS |
---|
522 | * |
---|
523 | * This might be improved on by comparing the numeric value of |
---|
524 | * the bitwise-or'd security flags, which splits DIGEST/OTP, |
---|
525 | * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we |
---|
526 | * are depending on the numeric values of the flags (which may |
---|
527 | * change, and their ordering could be considered dumb luck. |
---|
528 | */ |
---|
529 | |
---|
530 | if (bestm && |
---|
531 | ((m->plug->security_flags ^ bestm->plug->security_flags) & |
---|
532 | bestm->plug->security_flags)) { |
---|
533 | break; |
---|
534 | } |
---|
535 | |
---|
536 | if (mech) { |
---|
537 | *mech = m->plug->mech_name; |
---|
538 | } |
---|
539 | bestssf = m->plug->max_ssf; |
---|
540 | bestm = m; |
---|
541 | break; |
---|
542 | } |
---|
543 | } |
---|
544 | |
---|
545 | if (bestm == NULL) { |
---|
546 | sasl_seterror(conn, SASL_NOLOG, "No worthy mechs found"); |
---|
547 | result = SASL_NOMECH; |
---|
548 | goto done; |
---|
549 | } |
---|
550 | |
---|
551 | /* make (the rest of) cparams */ |
---|
552 | c_conn->cparams->service = conn->service; |
---|
553 | c_conn->cparams->servicelen = strlen(conn->service); |
---|
554 | |
---|
555 | c_conn->cparams->serverFQDN = conn->serverFQDN; |
---|
556 | c_conn->cparams->slen = strlen(conn->serverFQDN); |
---|
557 | |
---|
558 | c_conn->cparams->clientFQDN = c_conn->clientFQDN; |
---|
559 | c_conn->cparams->clen = strlen(c_conn->clientFQDN); |
---|
560 | |
---|
561 | c_conn->cparams->external_ssf = conn->external.ssf; |
---|
562 | c_conn->cparams->props = conn->props; |
---|
563 | c_conn->mech = bestm; |
---|
564 | |
---|
565 | /* init that plugin */ |
---|
566 | result = c_conn->mech->plug->mech_new(c_conn->mech->plug->glob_context, |
---|
567 | c_conn->cparams, |
---|
568 | &(conn->context)); |
---|
569 | if(result != SASL_OK) goto done; |
---|
570 | |
---|
571 | /* do a step -- but only if we can do a client-send-first */ |
---|
572 | dostep: |
---|
573 | if(clientout) { |
---|
574 | if(c_conn->mech->plug->features & SASL_FEAT_SERVER_FIRST) { |
---|
575 | *clientout = NULL; |
---|
576 | *clientoutlen = 0; |
---|
577 | result = SASL_CONTINUE; |
---|
578 | } else { |
---|
579 | result = sasl_client_step(conn, NULL, 0, prompt_need, |
---|
580 | clientout, clientoutlen); |
---|
581 | } |
---|
582 | } |
---|
583 | else |
---|
584 | result = SASL_CONTINUE; |
---|
585 | |
---|
586 | done: |
---|
587 | RETURN(conn, result); |
---|
588 | } |
---|
589 | |
---|
590 | /* do a single authentication step. |
---|
591 | * serverin -- the server message received by the client, MUST have a NUL |
---|
592 | * sentinel, not counted by serverinlen |
---|
593 | * output: |
---|
594 | * prompt_need -- on SASL_INTERACT, list of prompts needed to continue |
---|
595 | * clientout -- the client response to send to the server |
---|
596 | * |
---|
597 | * returns: |
---|
598 | * SASL_OK -- success |
---|
599 | * SASL_INTERACT -- user interaction needed to fill in prompt_need list |
---|
600 | * SASL_BADPROT -- server protocol incorrect/cancelled |
---|
601 | * SASL_BADSERV -- server failed mutual auth |
---|
602 | */ |
---|
603 | |
---|
604 | int sasl_client_step(sasl_conn_t *conn, |
---|
605 | const char *serverin, |
---|
606 | unsigned serverinlen, |
---|
607 | sasl_interact_t **prompt_need, |
---|
608 | const char **clientout, |
---|
609 | unsigned *clientoutlen) |
---|
610 | { |
---|
611 | sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn; |
---|
612 | int result; |
---|
613 | |
---|
614 | if(_sasl_client_active==0) return SASL_NOTINIT; |
---|
615 | if(!conn) return SASL_BADPARAM; |
---|
616 | |
---|
617 | /* check parameters */ |
---|
618 | if ((serverin==NULL) && (serverinlen>0)) |
---|
619 | PARAMERROR(conn); |
---|
620 | |
---|
621 | /* Don't do another step if the plugin told us that we're done */ |
---|
622 | if (conn->oparams.doneflag) { |
---|
623 | _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag"); |
---|
624 | return SASL_FAIL; |
---|
625 | } |
---|
626 | |
---|
627 | if(clientout) *clientout = NULL; |
---|
628 | if(clientoutlen) *clientoutlen = 0; |
---|
629 | |
---|
630 | /* do a step */ |
---|
631 | result = c_conn->mech->plug->mech_step(conn->context, |
---|
632 | c_conn->cparams, |
---|
633 | serverin, |
---|
634 | serverinlen, |
---|
635 | prompt_need, |
---|
636 | clientout, (int *)clientoutlen, |
---|
637 | &conn->oparams); |
---|
638 | |
---|
639 | if (result == SASL_OK) { |
---|
640 | /* So we're done on this end, but if both |
---|
641 | * 1. the mech does server-send-last |
---|
642 | * 2. the protocol does not |
---|
643 | * we need to return no data */ |
---|
644 | if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) { |
---|
645 | *clientout = ""; |
---|
646 | *clientoutlen = 0; |
---|
647 | } |
---|
648 | |
---|
649 | if(!conn->oparams.maxoutbuf) { |
---|
650 | conn->oparams.maxoutbuf = conn->props.maxbufsize; |
---|
651 | } |
---|
652 | |
---|
653 | if(conn->oparams.user == NULL || conn->oparams.authid == NULL) { |
---|
654 | sasl_seterror(conn, 0, |
---|
655 | "mech did not call canon_user for both authzid and authid"); |
---|
656 | result = SASL_BADPROT; |
---|
657 | } |
---|
658 | } |
---|
659 | |
---|
660 | RETURN(conn,result); |
---|
661 | } |
---|
662 | |
---|
663 | /* returns the length of all the mechanisms |
---|
664 | * added up |
---|
665 | */ |
---|
666 | |
---|
667 | static unsigned mech_names_len() |
---|
668 | { |
---|
669 | cmechanism_t *listptr; |
---|
670 | unsigned result = 0; |
---|
671 | |
---|
672 | for (listptr = cmechlist->mech_list; |
---|
673 | listptr; |
---|
674 | listptr = listptr->next) |
---|
675 | result += strlen(listptr->plug->mech_name); |
---|
676 | |
---|
677 | return result; |
---|
678 | } |
---|
679 | |
---|
680 | |
---|
681 | int _sasl_client_listmech(sasl_conn_t *conn, |
---|
682 | const char *prefix, |
---|
683 | const char *sep, |
---|
684 | const char *suffix, |
---|
685 | const char **result, |
---|
686 | unsigned *plen, |
---|
687 | int *pcount) |
---|
688 | { |
---|
689 | cmechanism_t *m=NULL; |
---|
690 | sasl_ssf_t minssf = 0; |
---|
691 | int ret; |
---|
692 | unsigned int resultlen; |
---|
693 | int flag; |
---|
694 | const char *mysep; |
---|
695 | |
---|
696 | if(_sasl_client_active == 0) return SASL_NOTINIT; |
---|
697 | if (!conn) return SASL_BADPARAM; |
---|
698 | if(conn->type != SASL_CONN_CLIENT) PARAMERROR(conn); |
---|
699 | |
---|
700 | if (! result) |
---|
701 | PARAMERROR(conn); |
---|
702 | |
---|
703 | if (plen != NULL) |
---|
704 | *plen = 0; |
---|
705 | if (pcount != NULL) |
---|
706 | *pcount = 0; |
---|
707 | |
---|
708 | if (sep) { |
---|
709 | mysep = sep; |
---|
710 | } else { |
---|
711 | mysep = " "; |
---|
712 | } |
---|
713 | |
---|
714 | if(conn->props.min_ssf < conn->external.ssf) { |
---|
715 | minssf = 0; |
---|
716 | } else { |
---|
717 | minssf = conn->props.min_ssf - conn->external.ssf; |
---|
718 | } |
---|
719 | |
---|
720 | if (! cmechlist || cmechlist->mech_length <= 0) |
---|
721 | INTERROR(conn, SASL_NOMECH); |
---|
722 | |
---|
723 | resultlen = (prefix ? strlen(prefix) : 0) |
---|
724 | + (strlen(mysep) * (cmechlist->mech_length - 1)) |
---|
725 | + mech_names_len() |
---|
726 | + (suffix ? strlen(suffix) : 0) |
---|
727 | + 1; |
---|
728 | ret = _buf_alloc(&conn->mechlist_buf, |
---|
729 | &conn->mechlist_buf_len, resultlen); |
---|
730 | if(ret != SASL_OK) MEMERROR(conn); |
---|
731 | |
---|
732 | if (prefix) |
---|
733 | strcpy (conn->mechlist_buf,prefix); |
---|
734 | else |
---|
735 | *(conn->mechlist_buf) = '\0'; |
---|
736 | |
---|
737 | flag = 0; |
---|
738 | for (m = cmechlist->mech_list; m != NULL; m = m->next) { |
---|
739 | /* do we have the prompts for it? */ |
---|
740 | if (!have_prompts(conn, m->plug)) |
---|
741 | continue; |
---|
742 | |
---|
743 | /* is it strong enough? */ |
---|
744 | if (minssf > m->plug->max_ssf) |
---|
745 | continue; |
---|
746 | |
---|
747 | /* does it meet our security properties? */ |
---|
748 | if (((conn->props.security_flags ^ m->plug->security_flags) |
---|
749 | & conn->props.security_flags) != 0) { |
---|
750 | continue; |
---|
751 | } |
---|
752 | |
---|
753 | /* Can we meet it's features? */ |
---|
754 | if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN) |
---|
755 | && !conn->serverFQDN) { |
---|
756 | continue; |
---|
757 | } |
---|
758 | |
---|
759 | /* Can it meet our features? */ |
---|
760 | if ((conn->flags & SASL_NEED_PROXY) && |
---|
761 | !(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) { |
---|
762 | break; |
---|
763 | } |
---|
764 | |
---|
765 | /* Okay, we like it, add it to the list! */ |
---|
766 | |
---|
767 | if (pcount != NULL) |
---|
768 | (*pcount)++; |
---|
769 | |
---|
770 | /* print seperator */ |
---|
771 | if (flag) { |
---|
772 | strcat(conn->mechlist_buf, mysep); |
---|
773 | } else { |
---|
774 | flag = 1; |
---|
775 | } |
---|
776 | |
---|
777 | /* now print the mechanism name */ |
---|
778 | strcat(conn->mechlist_buf, m->plug->mech_name); |
---|
779 | } |
---|
780 | |
---|
781 | if (suffix) |
---|
782 | strcat(conn->mechlist_buf,suffix); |
---|
783 | |
---|
784 | if (plen!=NULL) |
---|
785 | *plen=strlen(conn->mechlist_buf); |
---|
786 | |
---|
787 | *result = conn->mechlist_buf; |
---|
788 | |
---|
789 | return SASL_OK; |
---|
790 | } |
---|
791 | |
---|
792 | sasl_string_list_t *_sasl_client_mechs(void) |
---|
793 | { |
---|
794 | cmechanism_t *listptr; |
---|
795 | sasl_string_list_t *retval = NULL, *next=NULL; |
---|
796 | |
---|
797 | if(!_sasl_client_active) return NULL; |
---|
798 | |
---|
799 | /* make list */ |
---|
800 | for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) { |
---|
801 | next = sasl_ALLOC(sizeof(sasl_string_list_t)); |
---|
802 | |
---|
803 | if(!next && !retval) return NULL; |
---|
804 | else if(!next) { |
---|
805 | next = retval->next; |
---|
806 | do { |
---|
807 | sasl_FREE(retval); |
---|
808 | retval = next; |
---|
809 | next = retval->next; |
---|
810 | } while(next); |
---|
811 | return NULL; |
---|
812 | } |
---|
813 | |
---|
814 | next->d = listptr->plug->mech_name; |
---|
815 | |
---|
816 | if(!retval) { |
---|
817 | next->next = NULL; |
---|
818 | retval = next; |
---|
819 | } else { |
---|
820 | next->next = retval; |
---|
821 | retval = next; |
---|
822 | } |
---|
823 | } |
---|
824 | |
---|
825 | return retval; |
---|
826 | } |
---|