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 #if GSASL_VERSION_MINOR >= 9
44 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
48 /* Authenticator-specific options. */
49 /* I did have server_*_condition options for various mechanisms, but since
50 we only ever handle one mechanism at a time, I didn't see the point in keeping
51 that. In case someone sees a point, I've left the condition_check() API
53 optionlist auth_gsasl_options[] = {
54 { "client_authz", opt_stringptr,
55 (void *)(offsetof(auth_gsasl_options_block, client_authz)) },
56 { "client_channelbinding", opt_bool,
57 (void *)(offsetof(auth_gsasl_options_block, client_channelbinding)) },
58 { "client_password", opt_stringptr,
59 (void *)(offsetof(auth_gsasl_options_block, client_password)) },
60 { "client_username", opt_stringptr,
61 (void *)(offsetof(auth_gsasl_options_block, client_username)) },
63 { "server_channelbinding", opt_bool,
64 (void *)(offsetof(auth_gsasl_options_block, server_channelbinding)) },
65 { "server_hostname", opt_stringptr,
66 (void *)(offsetof(auth_gsasl_options_block, server_hostname)) },
67 { "server_mech", opt_stringptr,
68 (void *)(offsetof(auth_gsasl_options_block, server_mech)) },
69 { "server_password", opt_stringptr,
70 (void *)(offsetof(auth_gsasl_options_block, server_password)) },
71 { "server_realm", opt_stringptr,
72 (void *)(offsetof(auth_gsasl_options_block, server_realm)) },
73 { "server_scram_iter", opt_stringptr,
74 (void *)(offsetof(auth_gsasl_options_block, server_scram_iter)) },
75 { "server_scram_salt", opt_stringptr,
76 (void *)(offsetof(auth_gsasl_options_block, server_scram_salt)) },
77 { "server_service", opt_stringptr,
78 (void *)(offsetof(auth_gsasl_options_block, server_service)) }
80 /* GSASL_SCRAM_SALTED_PASSWORD documented only for client, so not implementing
81 hooks to avoid cleartext passwords in the Exim server. */
83 int auth_gsasl_options_count =
84 sizeof(auth_gsasl_options)/sizeof(optionlist);
86 /* Defaults for the authenticator-specific options. */
87 auth_gsasl_options_block auth_gsasl_option_defaults = {
88 .server_service = US"smtp",
89 .server_hostname = US"$primary_hostname",
90 .server_scram_iter = US"4096",
91 /* all others zero/null */
98 void auth_gsasl_init(auth_instance *ablock) {}
99 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
100 int auth_gsasl_client(auth_instance *ablock, void * sx,
101 int timeout, uschar *buffer, int buffsize) {return 0;}
102 void auth_gsasl_version_report(FILE *f) {}
105 auth_gsasl_macros(void)
107 # ifdef EXIM_GSASL_HAVE_SCRAM_SHA_256
108 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_SHA_256");
112 #else /*!MACRO_PREDEF*/
116 /* "Globals" for managing the gsasl interface. */
118 static Gsasl *gsasl_ctx = NULL;
120 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
122 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
124 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
126 static BOOL sasl_error_should_defer = FALSE;
127 static Gsasl_property callback_loop = 0;
128 static BOOL checked_server_condition = FALSE;
130 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
132 struct callback_exim_state {
133 auth_instance *ablock;
138 /*************************************************
139 * Initialization entry point *
140 *************************************************/
142 /* Called for each instance, after its options have been read, to
143 enable consistency checks to be done, or anything else that needs
147 auth_gsasl_init(auth_instance *ablock)
149 static char * once = NULL;
151 auth_gsasl_options_block *ob =
152 (auth_gsasl_options_block *)(ablock->options_block);
154 /* As per existing Cyrus glue, use the authenticator's public name as
155 the default for the mechanism name; we don't handle multiple mechanisms
156 in one authenticator, but the same driver can be used multiple times. */
158 if (!ob->server_mech)
159 ob->server_mech = string_copy(ablock->public_name);
161 /* Can get multiple session contexts from one library context, so just
162 initialise the once. */
166 if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
167 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
168 "couldn't initialise GNU SASL library: %s (%s)",
169 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
171 gsasl_callback_set(gsasl_ctx, main_callback);
174 /* We don't need this except to log it for debugging. */
176 HDEBUG(D_auth) if (!once)
178 if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
179 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
180 "failed to retrieve list of mechanisms: %s (%s)",
181 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
183 debug_printf("GNU SASL supports: %s\n", once);
186 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
187 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
188 "GNU SASL does not support mechanism \"%s\"",
189 ablock->name, ob->server_mech);
191 ablock->server = TRUE;
193 if ( !ablock->server_condition
194 && ( streqic(ob->server_mech, US"EXTERNAL")
195 || streqic(ob->server_mech, US"ANONYMOUS")
196 || streqic(ob->server_mech, US"PLAIN")
197 || streqic(ob->server_mech, US"LOGIN")
200 ablock->server = FALSE;
201 HDEBUG(D_auth) debug_printf("%s authenticator: "
202 "Need server_condition for %s mechanism\n",
203 ablock->name, ob->server_mech);
206 /* This does *not* scale to new SASL mechanisms. Need a better way to ask
207 which properties will be needed. */
209 if ( !ob->server_realm
210 && streqic(ob->server_mech, US"DIGEST-MD5"))
212 ablock->server = FALSE;
213 HDEBUG(D_auth) debug_printf("%s authenticator: "
214 "Need server_realm for %s mechanism\n",
215 ablock->name, ob->server_mech);
218 /* At present, for mechanisms we don't panic on absence of server_condition;
219 need to figure out the most generically correct approach to deciding when
220 it's critical and when it isn't. Eg, for simple validation (PLAIN mechanism,
221 etc) it clearly is critical.
224 ablock->client = ob->client_username && ob->client_password;
228 /* GNU SASL uses one top-level callback, registered at library level.
229 We dispatch to client and server functions instead. */
232 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
235 struct callback_exim_state *cb_state =
236 (struct callback_exim_state *)gsasl_session_hook_get(sctx);
240 HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
241 #ifdef CHANNELBIND_HACK
242 if (prop == GSASL_CB_TLS_UNIQUE)
245 if ((s = gsasl_callback_hook_get(ctx)))
247 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
248 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
252 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE! dummy for now\n");
253 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
258 return GSASL_NO_CALLBACK;
262 debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
263 prop, callback_loop);
265 if (callback_loop > 0)
267 /* Most likely is that we were asked for property FOO, and to
268 expand the string we asked for property BAR to put into an auth
269 variable, but property BAR is not supplied for this mechanism. */
271 debug_printf("Loop, asked for property %d while handling property %d\n",
272 prop, callback_loop);
273 return GSASL_NO_CALLBACK;
275 callback_loop = prop;
277 if (cb_state->currently == CURRENTLY_CLIENT)
278 rc = client_callback(ctx, sctx, prop, cb_state->ablock);
279 else if (cb_state->currently == CURRENTLY_SERVER)
280 rc = server_callback(ctx, sctx, prop, cb_state->ablock);
282 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
283 "unhandled callback state, bug in Exim", cb_state->ablock->name);
291 /*************************************************
292 * Server entry point *
293 *************************************************/
295 /* For interface, see auths/README */
298 auth_gsasl_server(auth_instance *ablock, uschar *initial_data)
301 char *to_send, *received;
302 Gsasl_session *sctx = NULL;
303 auth_gsasl_options_block *ob =
304 (auth_gsasl_options_block *)(ablock->options_block);
305 struct callback_exim_state cb_state;
306 int rc, auth_result, exim_error, exim_error_override;
309 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
310 ablock->name, ob->server_mech);
313 if (tls_in.channelbinding && ob->server_channelbinding)
315 # ifdef EXPERIMENTAL_TLS_RESUME
316 if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
317 { /* per RFC 7677 section 4 */
318 HDEBUG(D_auth) debug_printf(
319 "channel binding not usable on resumed TLS without extended-master-secret");
323 # ifdef CHANNELBIND_HACK
324 /* This is a gross hack to get around the library a) requiring that
325 c-b was already set, at the _start() call, and b) caching a b64'd
326 version of the binding then which it never updates. */
328 gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
333 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
335 auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
336 gsasl_strerror_name(rc), gsasl_strerror(rc));
337 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
340 /* Hereafter: gsasl_finish(sctx) please */
342 cb_state.ablock = ablock;
343 cb_state.currently = CURRENTLY_SERVER;
344 gsasl_session_hook_set(sctx, &cb_state);
346 tmps = CS expand_string(ob->server_service);
347 gsasl_property_set(sctx, GSASL_SERVICE, tmps);
348 tmps = CS expand_string(ob->server_hostname);
349 gsasl_property_set(sctx, GSASL_HOSTNAME, tmps);
350 if (ob->server_realm)
352 tmps = CS expand_string(ob->server_realm);
354 gsasl_property_set(sctx, GSASL_REALM, tmps);
356 /* We don't support protection layers. */
357 gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
360 if (tls_in.channelbinding)
362 /* Some auth mechanisms can ensure that both sides are talking withing the
363 same security context; for TLS, this means that even if a bad certificate
364 has been accepted, they remain MitM-proof because both sides must be within
365 the same negotiated session; if someone is terminating one session and
366 proxying data on within a second, authentication will fail.
368 We might not have this available, depending upon TLS implementation,
369 ciphersuite, phase of moon ...
371 If we do, it results in extra SASL mechanisms being available; here,
372 Exim's one-mechanism-per-authenticator potentially causes problems.
373 It depends upon how GNU SASL will implement the PLUS variants of GS2
374 and whether it automatically mandates a switch to the bound PLUS
375 if the data is available. Since default-on, despite being more secure,
376 would then result in mechanism name changes on a library update, we
377 have little choice but to default it off and let the admin choose to
380 if (ob->server_channelbinding)
382 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
384 # ifndef CHANNELBIND_HACK
385 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding);
390 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
395 debug_printf("Auth %s: no channel-binding data available\n",
399 checked_server_condition = FALSE;
401 received = CS initial_data;
403 exim_error = exim_error_override = OK;
406 switch (rc = gsasl_step64(sctx, received, &to_send))
410 goto STOP_INTERACTION;
413 case GSASL_NEEDS_MORE:
416 case GSASL_AUTHENTICATION_ERROR:
417 case GSASL_INTEGRITY_ERROR:
418 case GSASL_NO_AUTHID:
419 case GSASL_NO_ANONYMOUS_TOKEN:
420 case GSASL_NO_AUTHZID:
421 case GSASL_NO_PASSWORD:
422 case GSASL_NO_PASSCODE:
424 case GSASL_BASE64_ERROR:
425 HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
426 gsasl_strerror_name(rc), gsasl_strerror(rc));
427 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
428 "GNU SASL permanent failure: %s (%s)",
429 ablock->name, ob->server_mech,
430 gsasl_strerror_name(rc), gsasl_strerror(rc));
431 if (rc == GSASL_BASE64_ERROR)
432 exim_error_override = BAD64;
433 goto STOP_INTERACTION;
436 auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
437 gsasl_strerror_name(rc), gsasl_strerror(rc));
438 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
439 exim_error_override = DEFER;
440 goto STOP_INTERACTION;
443 if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
444 exim_error = auth_get_no64_data(USS &received, US to_send);
453 break; /* handles * cancelled check */
455 } while (rc == GSASL_NEEDS_MORE);
462 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
464 if (exim_error != OK)
467 if (auth_result != GSASL_OK)
469 HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
470 gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
471 if (exim_error_override != OK)
472 return exim_error_override; /* might be DEFER */
473 if (sasl_error_should_defer) /* overriding auth failure SASL error */
478 /* Auth succeeded, check server_condition unless already done in callback */
479 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
483 /* returns the GSASL status of expanding the Exim string given */
485 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
487 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
490 case OK: return GSASL_OK;
491 case DEFER: sasl_error_should_defer = TRUE;
492 return GSASL_AUTHENTICATION_ERROR;
493 case FAIL: return GSASL_AUTHENTICATION_ERROR;
494 default: log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
495 "Unhandled return from checking %s: %d",
496 ablock->name, label, exim_rc);
500 return GSASL_AUTHENTICATION_ERROR;
504 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
505 auth_instance *ablock)
509 int cbrc = GSASL_NO_CALLBACK;
510 auth_gsasl_options_block *ob =
511 (auth_gsasl_options_block *)(ablock->options_block);
514 debug_printf("GNU SASL callback %d for %s/%s as server\n",
515 prop, ablock->name, ablock->public_name);
517 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
522 case GSASL_VALIDATE_SIMPLE:
523 HDEBUG(D_auth) debug_printf(" VALIDATE_SIMPLE\n");
524 /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
525 propval = US gsasl_property_fast(sctx, GSASL_AUTHID);
526 auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
527 propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
528 auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
529 propval = US gsasl_property_fast(sctx, GSASL_PASSWORD);
530 auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US"";
532 for (int i = 1; i <= 3; ++i)
533 expand_nlength[i] = Ustrlen(expand_nstring[i]);
535 cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
536 checked_server_condition = TRUE;
539 case GSASL_VALIDATE_EXTERNAL:
540 HDEBUG(D_auth) debug_printf(" VALIDATE_EXTERNAL\n");
541 if (!ablock->server_condition)
543 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
544 cbrc = GSASL_AUTHENTICATION_ERROR;
547 propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
549 /* We always set $auth1, even if only to empty string. */
550 auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
551 expand_nlength[1] = Ustrlen(expand_nstring[1]);
554 cbrc = condition_check(ablock,
555 US"server_condition (EXTERNAL)", ablock->server_condition);
556 checked_server_condition = TRUE;
559 case GSASL_VALIDATE_ANONYMOUS:
560 HDEBUG(D_auth) debug_printf(" VALIDATE_ANONYMOUS\n");
561 if (!ablock->server_condition)
563 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
564 cbrc = GSASL_AUTHENTICATION_ERROR;
567 propval = US gsasl_property_fast(sctx, GSASL_ANONYMOUS_TOKEN);
569 /* We always set $auth1, even if only to empty string. */
571 auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
572 expand_nlength[1] = Ustrlen(expand_nstring[1]);
575 cbrc = condition_check(ablock,
576 US"server_condition (ANONYMOUS)", ablock->server_condition);
577 checked_server_condition = TRUE;
580 case GSASL_VALIDATE_GSSAPI:
581 HDEBUG(D_auth) debug_printf(" VALIDATE_GSSAPI\n");
582 /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
583 The display-name is authenticated as part of GSS, the authzid is claimed
584 by the SASL integration after authentication; protected against tampering
585 (if the SASL mechanism supports that, which Kerberos does) but is
586 unverified, same as normal for other mechanisms.
587 First coding, we had these values swapped, but for consistency and prior
588 to the first release of Exim with this authenticator, they've been
589 switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
591 propval = US gsasl_property_fast(sctx, GSASL_GSSAPI_DISPLAY_NAME);
592 auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
593 propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
594 auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
596 for (int i = 1; i <= 2; ++i)
597 expand_nlength[i] = Ustrlen(expand_nstring[i]);
599 /* In this one case, it perhaps makes sense to default back open?
600 But for consistency, let's just mandate server_condition here too. */
602 cbrc = condition_check(ablock,
603 US"server_condition (GSSAPI family)", ablock->server_condition);
604 checked_server_condition = TRUE;
607 case GSASL_SCRAM_ITER:
608 HDEBUG(D_auth) debug_printf(" SCRAM_ITER\n");
609 if (ob->server_scram_iter)
611 tmps = CS expand_string(ob->server_scram_iter);
612 gsasl_property_set(sctx, GSASL_SCRAM_ITER, tmps);
617 case GSASL_SCRAM_SALT:
618 HDEBUG(D_auth) debug_printf(" SCRAM_SALT\n");
619 if (ob->server_scram_iter)
621 tmps = CS expand_string(ob->server_scram_salt);
622 gsasl_property_set(sctx, GSASL_SCRAM_SALT, tmps);
628 HDEBUG(D_auth) debug_printf(" PASSWORD\n");
629 /* DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
630 CRAM-MD5: GSASL_AUTHID
631 PLAIN: GSASL_AUTHID and GSASL_AUTHZID
634 if (ob->server_scram_iter)
636 tmps = CS expand_string(ob->server_scram_iter);
637 gsasl_property_set(sctx, GSASL_SCRAM_ITER, tmps);
639 if (ob->server_scram_salt)
641 tmps = CS expand_string(ob->server_scram_salt);
642 gsasl_property_set(sctx, GSASL_SCRAM_SALT, tmps);
645 /* Asking for GSASL_AUTHZID calls back into us if we use
646 gsasl_property_get(), thus the use of gsasl_property_fast().
647 Do we really want to hardcode limits per mechanism? What happens when
648 a new mechanism is added to the library. It *shouldn't* result in us
649 needing to add more glue, since avoiding that is a large part of the
652 propval = US gsasl_property_fast(sctx, GSASL_AUTHID);
653 auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
654 propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
655 auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
656 propval = US gsasl_property_fast(sctx, GSASL_REALM);
657 auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US"";
659 for (int i = 1; i <= 3; ++i)
660 expand_nlength[i] = Ustrlen(expand_nstring[i]);
662 if (!ob->server_password)
664 if (!(tmps = CS expand_string(ob->server_password)))
666 sasl_error_should_defer = f.expand_string_forcedfail ? FALSE : TRUE;
667 HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
668 "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
669 return GSASL_AUTHENTICATION_ERROR;
671 gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
673 /* This is inadequate; don't think Exim's store stacks are geared
674 for memory wiping, so expanding strings will leave stuff laying around.
675 But no need to compound the problem, so get rid of the one we can. */
677 memset(tmps, '\0', strlen(tmps));
682 HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
683 cbrc = GSASL_NO_CALLBACK;
686 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
687 gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
693 /******************************************************************************/
695 #define PROP_OPTIONAL BIT(0)
698 client_prop(Gsasl_session * sctx, Gsasl_property propnum, uschar * val,
699 const uschar * why, unsigned flags, uschar * buffer, int buffsize)
704 if (flags & PROP_OPTIONAL && !val) return TRUE;
705 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
707 string_format(buffer, buffsize, "%s", expand_string_message);
710 if (*s) gsasl_property_set(sctx, propnum, CS s);
714 /*************************************************
715 * Client entry point *
716 *************************************************/
718 /* For interface, see auths/README */
722 auth_instance *ablock, /* authenticator block */
723 void * sx, /* connection */
724 int timeout, /* command timeout */
725 uschar *buffer, /* buffer for reading response */
726 int buffsize) /* size of buffer */
728 auth_gsasl_options_block *ob =
729 (auth_gsasl_options_block *)(ablock->options_block);
730 Gsasl_session * sctx = NULL;
731 struct callback_exim_state cb_state;
734 int rc, yield = FAIL;
737 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
738 ablock->name, ob->server_mech);
743 if (tls_out.channelbinding && ob->client_channelbinding)
745 # ifdef EXPERIMENTAL_TLS_RESUME
746 if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
747 { /* per RFC 7677 section 4 */
748 string_format(buffer, buffsize, "%s",
749 "channel binding not usable on resumed TLS without extended-master-secret");
753 # ifdef CHANNELBIND_HACK
754 /* This is a gross hack to get around the library a) requiring that
755 c-b was already set, at the _start() call, and b) caching a b64'd
756 version of the binding then which it never updates. */
758 gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
763 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
765 string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
766 gsasl_strerror_name(rc), gsasl_strerror(rc));
767 HDEBUG(D_auth) debug_printf("%s\n", buffer);
771 cb_state.ablock = ablock;
772 cb_state.currently = CURRENTLY_CLIENT;
773 gsasl_session_hook_set(sctx, &cb_state);
777 if ( !client_prop(sctx, GSASL_PASSWORD, ob->client_password, US"password",
779 || !client_prop(sctx, GSASL_AUTHID, ob->client_username, US"username",
781 || !client_prop(sctx, GSASL_AUTHZID, ob->client_authz, US"authz",
782 PROP_OPTIONAL, buffer, buffsize)
787 if (tls_out.channelbinding)
788 if (ob->client_channelbinding)
790 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
792 # ifndef CHANNELBIND_HACK
793 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
798 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
802 /* Run the SASL conversation with the server */
809 rc = gsasl_step64(sctx, CS s, CSS &outstr);
812 ? smtp_write_command(sx, SCMD_FLUSH,
813 outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
814 ablock->public_name, outstr) <= 0
816 ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
818 if (outstr && *outstr) free(outstr);
826 if (rc != GSASL_NEEDS_MORE)
830 string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
834 /* expecting a final 2xx from the server, accepting the AUTH */
836 if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
838 break; /* from SASL sequence loop */
841 /* 2xx or 3xx response is acceptable. If 2xx, no further input */
843 if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
844 if (errno == 0 && buffer[0] == '2')
860 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
862 HDEBUG(D_auth) debug_printf("GNU SASL callback %d for %s/%s as client\n",
863 prop, ablock->name, ablock->public_name);
867 HDEBUG(D_auth) debug_printf(" inquired for AUTHZID; not providing one\n");
869 case GSASL_SCRAM_SALTED_PASSWORD:
871 debug_printf(" inquired for SCRAM_SALTED_PASSWORD; not providing one\n");
873 case GSASL_CB_TLS_UNIQUE:
875 debug_printf(" inquired for CB_TLS_UNIQUE, filling in\n");
876 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
879 return GSASL_NO_CALLBACK;
882 /*************************************************
884 *************************************************/
887 auth_gsasl_version_report(FILE *f)
890 runtime = gsasl_check_version(NULL);
891 fprintf(f, "Library version: GNU SASL: Compile: %s\n"
893 GSASL_VERSION, runtime);
899 void auth_gsasl_macros(void) {}
901 #endif /*!MACRO_PREDEF*/
902 #endif /* AUTH_GSASL */
904 /* End of gsasl_exim.c */