1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2019 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
9 /* Copyright (c) Twitter Inc 2012
10 Author: Phil Pennock <pdp@exim.org> */
11 /* Copyright (c) Phil Pennock 2012 */
13 /* Interface to GNU SASL library for generic authentication. */
17 GNU SASL does not provide authentication data itself, so we have to expose
18 that decision to configuration. For some mechanisms, we need to act much
19 like plaintext. For others, we only need to be able to provide some
20 evaluated data on demand. There's no abstracted way (ie, without hardcoding
21 knowledge of authenticators here) to know which need what properties; we
22 can't query a session or the library for "we will need these for mechanism X".
24 So: we always require server_condition, even if sometimes it will just be
25 set as "yes". We do provide a number of other hooks, which might not make
26 sense in all contexts. For some, we can do checks at init time.
30 #define CHANNELBIND_HACK
33 /* dummy function to satisfy compilers when we link in an "empty" file. */
34 static void dummy(int x);
35 static void dummy2(int x) { dummy(x-1); }
36 static void dummy(int x) { dummy2(x-1); }
40 #include "gsasl_exim.h"
43 # include <stringprep.h>
47 /* Authenticator-specific options. */
48 /* I did have server_*_condition options for various mechanisms, but since
49 we only ever handle one mechanism at a time, I didn't see the point in keeping
50 that. In case someone sees a point, I've left the condition_check() API
52 optionlist auth_gsasl_options[] = {
53 { "client_authz", opt_stringptr,
54 (void *)(offsetof(auth_gsasl_options_block, client_authz)) },
55 { "client_channelbinding", opt_bool,
56 (void *)(offsetof(auth_gsasl_options_block, client_channelbinding)) },
57 { "client_password", opt_stringptr,
58 (void *)(offsetof(auth_gsasl_options_block, client_password)) },
59 { "client_username", opt_stringptr,
60 (void *)(offsetof(auth_gsasl_options_block, client_username)) },
62 { "server_channelbinding", opt_bool,
63 (void *)(offsetof(auth_gsasl_options_block, server_channelbinding)) },
64 { "server_hostname", opt_stringptr,
65 (void *)(offsetof(auth_gsasl_options_block, server_hostname)) },
66 { "server_mech", opt_stringptr,
67 (void *)(offsetof(auth_gsasl_options_block, server_mech)) },
68 { "server_password", opt_stringptr,
69 (void *)(offsetof(auth_gsasl_options_block, server_password)) },
70 { "server_realm", opt_stringptr,
71 (void *)(offsetof(auth_gsasl_options_block, server_realm)) },
72 { "server_scram_iter", opt_stringptr,
73 (void *)(offsetof(auth_gsasl_options_block, server_scram_iter)) },
74 { "server_scram_salt", opt_stringptr,
75 (void *)(offsetof(auth_gsasl_options_block, server_scram_salt)) },
76 { "server_service", opt_stringptr,
77 (void *)(offsetof(auth_gsasl_options_block, server_service)) }
79 /* GSASL_SCRAM_SALTED_PASSWORD documented only for client, so not implementing
80 hooks to avoid cleartext passwords in the Exim server. */
82 int auth_gsasl_options_count =
83 sizeof(auth_gsasl_options)/sizeof(optionlist);
85 /* Defaults for the authenticator-specific options. */
86 auth_gsasl_options_block auth_gsasl_option_defaults = {
87 .server_service = US"smtp",
88 .server_hostname = US"$primary_hostname",
89 .server_scram_iter = US"4096",
90 /* all others zero/null */
97 void auth_gsasl_init(auth_instance *ablock) {}
98 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
99 int auth_gsasl_client(auth_instance *ablock, void * sx,
100 int timeout, uschar *buffer, int buffsize) {return 0;}
101 void auth_gsasl_version_report(FILE *f) {}
103 #else /*!MACRO_PREDEF*/
107 /* "Globals" for managing the gsasl interface. */
109 static Gsasl *gsasl_ctx = NULL;
111 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
113 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
115 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
117 static BOOL sasl_error_should_defer = FALSE;
118 static Gsasl_property callback_loop = 0;
119 static BOOL checked_server_condition = FALSE;
121 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
123 struct callback_exim_state {
124 auth_instance *ablock;
129 /*************************************************
130 * Initialization entry point *
131 *************************************************/
133 /* Called for each instance, after its options have been read, to
134 enable consistency checks to be done, or anything else that needs
138 auth_gsasl_init(auth_instance *ablock)
140 static char * once = NULL;
142 auth_gsasl_options_block *ob =
143 (auth_gsasl_options_block *)(ablock->options_block);
145 /* As per existing Cyrus glue, use the authenticator's public name as
146 the default for the mechanism name; we don't handle multiple mechanisms
147 in one authenticator, but the same driver can be used multiple times. */
149 if (!ob->server_mech)
150 ob->server_mech = string_copy(ablock->public_name);
152 /* Can get multiple session contexts from one library context, so just
153 initialise the once. */
157 if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
158 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
159 "couldn't initialise GNU SASL library: %s (%s)",
160 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
162 gsasl_callback_set(gsasl_ctx, main_callback);
165 /* We don't need this except to log it for debugging. */
167 HDEBUG(D_auth) if (!once)
169 if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
170 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
171 "failed to retrieve list of mechanisms: %s (%s)",
172 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
174 debug_printf("GNU SASL supports: %s\n", once);
177 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
178 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
179 "GNU SASL does not support mechanism \"%s\"",
180 ablock->name, ob->server_mech);
182 ablock->server = TRUE;
184 if ( !ablock->server_condition
185 && ( streqic(ob->server_mech, US"EXTERNAL")
186 || streqic(ob->server_mech, US"ANONYMOUS")
187 || streqic(ob->server_mech, US"PLAIN")
188 || streqic(ob->server_mech, US"LOGIN")
191 ablock->server = FALSE;
192 HDEBUG(D_auth) debug_printf("%s authenticator: "
193 "Need server_condition for %s mechanism\n",
194 ablock->name, ob->server_mech);
197 /* This does *not* scale to new SASL mechanisms. Need a better way to ask
198 which properties will be needed. */
200 if ( !ob->server_realm
201 && streqic(ob->server_mech, US"DIGEST-MD5"))
203 ablock->server = FALSE;
204 HDEBUG(D_auth) debug_printf("%s authenticator: "
205 "Need server_realm for %s mechanism\n",
206 ablock->name, ob->server_mech);
209 /* At present, for mechanisms we don't panic on absence of server_condition;
210 need to figure out the most generically correct approach to deciding when
211 it's critical and when it isn't. Eg, for simple validation (PLAIN mechanism,
212 etc) it clearly is critical.
215 ablock->client = ob->client_username && ob->client_password;
219 /* GNU SASL uses one top-level callback, registered at library level.
220 We dispatch to client and server functions instead. */
223 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
226 struct callback_exim_state *cb_state =
227 (struct callback_exim_state *)gsasl_session_hook_get(sctx);
231 HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
232 #ifdef CHANNELBIND_HACK
233 if (prop == GSASL_CB_TLS_UNIQUE)
236 if ((s = gsasl_callback_hook_get(ctx)))
238 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
239 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
243 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE! dummy for now\n");
244 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
249 return GSASL_NO_CALLBACK;
253 debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
254 prop, callback_loop);
256 if (callback_loop > 0)
258 /* Most likely is that we were asked for property FOO, and to
259 expand the string we asked for property BAR to put into an auth
260 variable, but property BAR is not supplied for this mechanism. */
262 debug_printf("Loop, asked for property %d while handling property %d\n",
263 prop, callback_loop);
264 return GSASL_NO_CALLBACK;
266 callback_loop = prop;
268 if (cb_state->currently == CURRENTLY_CLIENT)
269 rc = client_callback(ctx, sctx, prop, cb_state->ablock);
270 else if (cb_state->currently == CURRENTLY_SERVER)
271 rc = server_callback(ctx, sctx, prop, cb_state->ablock);
273 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
274 "unhandled callback state, bug in Exim", cb_state->ablock->name);
282 /*************************************************
283 * Server entry point *
284 *************************************************/
286 /* For interface, see auths/README */
289 auth_gsasl_server(auth_instance *ablock, uschar *initial_data)
292 char *to_send, *received;
293 Gsasl_session *sctx = NULL;
294 auth_gsasl_options_block *ob =
295 (auth_gsasl_options_block *)(ablock->options_block);
296 struct callback_exim_state cb_state;
297 int rc, auth_result, exim_error, exim_error_override;
300 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
301 ablock->name, ob->server_mech);
304 if (tls_in.channelbinding && ob->server_channelbinding)
306 # ifdef EXPERIMENTAL_TLS_RESUME
307 if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
308 { /* per RFC 7677 section 4 */
309 HDEBUG(D_auth) debug_printf(
310 "channel binding not usable on resumed TLS without extended-master-secret");
314 # ifdef CHANNELBIND_HACK
315 /* This is a gross hack to get around the library a) requiring that
316 c-b was already set, at the _start() call, and b) caching a b64'd
317 version of the binding then which it never updates. */
319 gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
324 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
326 auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
327 gsasl_strerror_name(rc), gsasl_strerror(rc));
328 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
331 /* Hereafter: gsasl_finish(sctx) please */
333 cb_state.ablock = ablock;
334 cb_state.currently = CURRENTLY_SERVER;
335 gsasl_session_hook_set(sctx, &cb_state);
337 tmps = CS expand_string(ob->server_service);
338 gsasl_property_set(sctx, GSASL_SERVICE, tmps);
339 tmps = CS expand_string(ob->server_hostname);
340 gsasl_property_set(sctx, GSASL_HOSTNAME, tmps);
341 if (ob->server_realm)
343 tmps = CS expand_string(ob->server_realm);
345 gsasl_property_set(sctx, GSASL_REALM, tmps);
347 /* We don't support protection layers. */
348 gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
351 if (tls_in.channelbinding)
353 /* Some auth mechanisms can ensure that both sides are talking withing the
354 same security context; for TLS, this means that even if a bad certificate
355 has been accepted, they remain MitM-proof because both sides must be within
356 the same negotiated session; if someone is terminating one session and
357 proxying data on within a second, authentication will fail.
359 We might not have this available, depending upon TLS implementation,
360 ciphersuite, phase of moon ...
362 If we do, it results in extra SASL mechanisms being available; here,
363 Exim's one-mechanism-per-authenticator potentially causes problems.
364 It depends upon how GNU SASL will implement the PLUS variants of GS2
365 and whether it automatically mandates a switch to the bound PLUS
366 if the data is available. Since default-on, despite being more secure,
367 would then result in mechanism name changes on a library update, we
368 have little choice but to default it off and let the admin choose to
371 if (ob->server_channelbinding)
373 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
375 # ifndef CHANNELBIND_HACK
376 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding);
381 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
386 debug_printf("Auth %s: no channel-binding data available\n",
390 checked_server_condition = FALSE;
392 received = CS initial_data;
394 exim_error = exim_error_override = OK;
397 switch (rc = gsasl_step64(sctx, received, &to_send))
401 goto STOP_INTERACTION;
404 case GSASL_NEEDS_MORE:
407 case GSASL_AUTHENTICATION_ERROR:
408 case GSASL_INTEGRITY_ERROR:
409 case GSASL_NO_AUTHID:
410 case GSASL_NO_ANONYMOUS_TOKEN:
411 case GSASL_NO_AUTHZID:
412 case GSASL_NO_PASSWORD:
413 case GSASL_NO_PASSCODE:
415 case GSASL_BASE64_ERROR:
416 HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
417 gsasl_strerror_name(rc), gsasl_strerror(rc));
418 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
419 "GNU SASL permanent failure: %s (%s)",
420 ablock->name, ob->server_mech,
421 gsasl_strerror_name(rc), gsasl_strerror(rc));
422 if (rc == GSASL_BASE64_ERROR)
423 exim_error_override = BAD64;
424 goto STOP_INTERACTION;
427 auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
428 gsasl_strerror_name(rc), gsasl_strerror(rc));
429 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
430 exim_error_override = DEFER;
431 goto STOP_INTERACTION;
434 if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
435 exim_error = auth_get_no64_data(USS &received, US to_send);
444 break; /* handles * cancelled check */
446 } while (rc == GSASL_NEEDS_MORE);
453 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
455 if (exim_error != OK)
458 if (auth_result != GSASL_OK)
460 HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
461 gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
462 if (exim_error_override != OK)
463 return exim_error_override; /* might be DEFER */
464 if (sasl_error_should_defer) /* overriding auth failure SASL error */
469 /* Auth succeeded, check server_condition unless already done in callback */
470 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
474 /* returns the GSASL status of expanding the Exim string given */
476 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
478 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
481 case OK: return GSASL_OK;
482 case DEFER: sasl_error_should_defer = TRUE;
483 return GSASL_AUTHENTICATION_ERROR;
484 case FAIL: return GSASL_AUTHENTICATION_ERROR;
485 default: log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
486 "Unhandled return from checking %s: %d",
487 ablock->name, label, exim_rc);
491 return GSASL_AUTHENTICATION_ERROR;
495 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
496 auth_instance *ablock)
500 int cbrc = GSASL_NO_CALLBACK;
501 auth_gsasl_options_block *ob =
502 (auth_gsasl_options_block *)(ablock->options_block);
505 debug_printf("GNU SASL callback %d for %s/%s as server\n",
506 prop, ablock->name, ablock->public_name);
508 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
513 case GSASL_VALIDATE_SIMPLE:
514 HDEBUG(D_auth) debug_printf(" VALIDATE_SIMPLE\n");
515 /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
516 propval = US gsasl_property_fast(sctx, GSASL_AUTHID);
517 auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
518 propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
519 auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
520 propval = US gsasl_property_fast(sctx, GSASL_PASSWORD);
521 auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US"";
523 for (int i = 1; i <= 3; ++i)
524 expand_nlength[i] = Ustrlen(expand_nstring[i]);
526 cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
527 checked_server_condition = TRUE;
530 case GSASL_VALIDATE_EXTERNAL:
531 HDEBUG(D_auth) debug_printf(" VALIDATE_EXTERNAL\n");
532 if (!ablock->server_condition)
534 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
535 cbrc = GSASL_AUTHENTICATION_ERROR;
538 propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
540 /* We always set $auth1, even if only to empty string. */
541 auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
542 expand_nlength[1] = Ustrlen(expand_nstring[1]);
545 cbrc = condition_check(ablock,
546 US"server_condition (EXTERNAL)", ablock->server_condition);
547 checked_server_condition = TRUE;
550 case GSASL_VALIDATE_ANONYMOUS:
551 HDEBUG(D_auth) debug_printf(" VALIDATE_ANONYMOUS\n");
552 if (!ablock->server_condition)
554 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
555 cbrc = GSASL_AUTHENTICATION_ERROR;
558 propval = US gsasl_property_fast(sctx, GSASL_ANONYMOUS_TOKEN);
560 /* We always set $auth1, even if only to empty string. */
562 auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
563 expand_nlength[1] = Ustrlen(expand_nstring[1]);
566 cbrc = condition_check(ablock,
567 US"server_condition (ANONYMOUS)", ablock->server_condition);
568 checked_server_condition = TRUE;
571 case GSASL_VALIDATE_GSSAPI:
572 HDEBUG(D_auth) debug_printf(" VALIDATE_GSSAPI\n");
573 /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
574 The display-name is authenticated as part of GSS, the authzid is claimed
575 by the SASL integration after authentication; protected against tampering
576 (if the SASL mechanism supports that, which Kerberos does) but is
577 unverified, same as normal for other mechanisms.
578 First coding, we had these values swapped, but for consistency and prior
579 to the first release of Exim with this authenticator, they've been
580 switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
582 propval = US gsasl_property_fast(sctx, GSASL_GSSAPI_DISPLAY_NAME);
583 auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
584 propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
585 auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
587 for (int i = 1; i <= 2; ++i)
588 expand_nlength[i] = Ustrlen(expand_nstring[i]);
590 /* In this one case, it perhaps makes sense to default back open?
591 But for consistency, let's just mandate server_condition here too. */
593 cbrc = condition_check(ablock,
594 US"server_condition (GSSAPI family)", ablock->server_condition);
595 checked_server_condition = TRUE;
598 case GSASL_SCRAM_ITER:
599 HDEBUG(D_auth) debug_printf(" SCRAM_ITER\n");
600 if (ob->server_scram_iter)
602 tmps = CS expand_string(ob->server_scram_iter);
603 gsasl_property_set(sctx, GSASL_SCRAM_ITER, tmps);
608 case GSASL_SCRAM_SALT:
609 HDEBUG(D_auth) debug_printf(" SCRAM_SALT\n");
610 if (ob->server_scram_iter)
612 tmps = CS expand_string(ob->server_scram_salt);
613 gsasl_property_set(sctx, GSASL_SCRAM_SALT, tmps);
619 HDEBUG(D_auth) debug_printf(" PASSWORD\n");
620 /* DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
621 CRAM-MD5: GSASL_AUTHID
622 PLAIN: GSASL_AUTHID and GSASL_AUTHZID
625 if (ob->server_scram_iter)
627 tmps = CS expand_string(ob->server_scram_iter);
628 gsasl_property_set(sctx, GSASL_SCRAM_ITER, tmps);
630 if (ob->server_scram_salt)
632 tmps = CS expand_string(ob->server_scram_salt);
633 gsasl_property_set(sctx, GSASL_SCRAM_SALT, tmps);
636 /* Asking for GSASL_AUTHZID calls back into us if we use
637 gsasl_property_get(), thus the use of gsasl_property_fast().
638 Do we really want to hardcode limits per mechanism? What happens when
639 a new mechanism is added to the library. It *shouldn't* result in us
640 needing to add more glue, since avoiding that is a large part of the
643 propval = US gsasl_property_fast(sctx, GSASL_AUTHID);
644 auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
645 propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
646 auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
647 propval = US gsasl_property_fast(sctx, GSASL_REALM);
648 auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US"";
650 for (int i = 1; i <= 3; ++i)
651 expand_nlength[i] = Ustrlen(expand_nstring[i]);
653 if (!(tmps = CS expand_string(ob->server_password)))
655 sasl_error_should_defer = f.expand_string_forcedfail ? FALSE : TRUE;
656 HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
657 "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
658 return GSASL_AUTHENTICATION_ERROR;
660 gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
662 /* This is inadequate; don't think Exim's store stacks are geared
663 for memory wiping, so expanding strings will leave stuff laying around.
664 But no need to compound the problem, so get rid of the one we can. */
666 memset(tmps, '\0', strlen(tmps));
671 HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
672 cbrc = GSASL_NO_CALLBACK;
675 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
676 gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
682 /******************************************************************************/
684 #define PROP_OPTIONAL BIT(0)
685 #define PROP_STRINGPREP BIT(1)
689 client_prop(Gsasl_session * sctx, Gsasl_property propnum, uschar * val,
690 const uschar * why, unsigned flags, uschar * buffer, int buffsize)
695 if (flags & PROP_OPTIONAL && !val) return TRUE;
696 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
698 string_format(buffer, buffsize, "%s", expand_string_message);
701 if (!*s) return TRUE;
704 if (flags & PROP_STRINGPREP)
706 if (gsasl_saslprep(CCS s, 0, CSS &t, &rc) != GSASL_OK)
708 string_format(buffer, buffsize, "Bad result from saslprep(%s): %s\n",
709 why, stringprep_strerror(rc));
710 HDEBUG(D_auth) debug_printf("%s\n", buffer);
713 gsasl_property_set(sctx, propnum, CS t);
719 gsasl_property_set(sctx, propnum, CS s);
724 /*************************************************
725 * Client entry point *
726 *************************************************/
728 /* For interface, see auths/README */
732 auth_instance *ablock, /* authenticator block */
733 void * sx, /* connection */
734 int timeout, /* command timeout */
735 uschar *buffer, /* buffer for reading response */
736 int buffsize) /* size of buffer */
738 auth_gsasl_options_block *ob =
739 (auth_gsasl_options_block *)(ablock->options_block);
740 Gsasl_session * sctx = NULL;
741 struct callback_exim_state cb_state;
743 BOOL initial = TRUE, do_stringprep;
744 int rc, yield = FAIL, flags;
747 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
748 ablock->name, ob->server_mech);
753 if (tls_out.channelbinding && ob->client_channelbinding)
755 # ifdef EXPERIMENTAL_TLS_RESUME
756 if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
757 { /* per RFC 7677 section 4 */
758 string_format(buffer, buffsize, "%s",
759 "channel binding not usable on resumed TLS without extended-master-secret");
763 # ifdef CHANNELBIND_HACK
764 /* This is a gross hack to get around the library a) requiring that
765 c-b was already set, at the _start() call, and b) caching a b64'd
766 version of the binding then which it never updates. */
768 gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
773 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
775 string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
776 gsasl_strerror_name(rc), gsasl_strerror(rc));
777 HDEBUG(D_auth) debug_printf("%s\n", buffer);
781 cb_state.ablock = ablock;
782 cb_state.currently = CURRENTLY_CLIENT;
783 gsasl_session_hook_set(sctx, &cb_state);
787 flags = Ustrncmp(ob->server_mech, "SCRAM-", 5) == 0 ? PROP_STRINGPREP : 0;
789 if ( !client_prop(sctx, GSASL_PASSWORD, ob->client_password, US"password",
790 flags, buffer, buffsize)
791 || !client_prop(sctx, GSASL_AUTHID, ob->client_username, US"username",
792 flags, buffer, buffsize)
793 || !client_prop(sctx, GSASL_AUTHZID, ob->client_authz, US"authz",
794 flags | PROP_OPTIONAL, buffer, buffsize)
799 if (tls_out.channelbinding)
800 if (ob->client_channelbinding)
802 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
804 # ifndef CHANNELBIND_HACK
805 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
810 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
814 /* Run the SASL conversation with the server */
821 rc = gsasl_step64(sctx, CS s, CSS &outstr);
824 ? smtp_write_command(sx, SCMD_FLUSH,
825 outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
826 ablock->public_name, outstr) <= 0
828 ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
830 if (outstr && *outstr) free(outstr);
838 if (rc != GSASL_NEEDS_MORE)
842 string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
846 /* expecting a final 2xx from the server, accepting the AUTH */
848 if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
850 break; /* from SASL sequence loop */
853 /* 2xx or 3xx response is acceptable. If 2xx, no further input */
855 if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
856 if (errno == 0 && buffer[0] == '2')
872 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
874 HDEBUG(D_auth) debug_printf("GNU SASL callback %d for %s/%s as client\n",
875 prop, ablock->name, ablock->public_name);
879 HDEBUG(D_auth) debug_printf(" inquired for AUTHZID; not providing one\n");
881 case GSASL_SCRAM_SALTED_PASSWORD:
883 debug_printf(" inquired for SCRAM_SALTED_PASSWORD; not providing one\n");
885 case GSASL_CB_TLS_UNIQUE:
887 debug_printf(" inquired for CB_TLS_UNIQUE, filling in\n");
888 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
891 return GSASL_NO_CALLBACK;
894 /*************************************************
896 *************************************************/
899 auth_gsasl_version_report(FILE *f)
902 runtime = gsasl_check_version(NULL);
903 fprintf(f, "Library version: GNU SASL: Compile: %s\n"
905 GSASL_VERSION, runtime);
908 #endif /*!MACRO_PREDEF*/
909 #endif /* AUTH_GSASL */
911 /* End of gsasl_exim.c */