1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2019 - 2023 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
10 /* Copyright (c) Twitter Inc 2012
11 Author: Phil Pennock <pdp@exim.org> */
12 /* Copyright (c) Phil Pennock 2012 */
14 /* Interface to GNU SASL library for generic authentication. */
18 GNU SASL does not provide authentication data itself, so we have to expose
19 that decision to configuration. For some mechanisms, we need to act much
20 like plaintext. For others, we only need to be able to provide some
21 evaluated data on demand. There's no abstracted way (ie, without hardcoding
22 knowledge of authenticators here) to know which need what properties; we
23 can't query a session or the library for "we will need these for mechanism X".
25 So: we always require server_condition, even if sometimes it will just be
26 set as "yes". We do provide a number of other hooks, which might not make
27 sense in all contexts. For some, we can do checks at init time.
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_MAJOR == 2
45 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
46 # define EXIM_GSASL_SCRAM_S_KEY
47 # if GSASL_VERSION_MINOR >= 1
48 # define EXIM_GSASL_HAVE_EXPORTER
49 # elif GSASL_VERSION_PATCH >= 1
50 # define EXIM_GSASL_HAVE_EXPORTER
53 #elif GSASL_VERSION_MAJOR == 1
54 # if GSASL_VERSION_MINOR >= 10
55 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
56 # define EXIM_GSASL_SCRAM_S_KEY
58 # elif GSASL_VERSION_MINOR == 9
59 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
61 # if GSASL_VERSION_PATCH >= 1
62 # define EXIM_GSASL_SCRAM_S_KEY
64 # if GSASL_VERSION_PATCH < 2
65 # define CHANNELBIND_HACK
69 # define CHANNELBIND_HACK
73 /* Convenience for testing strings */
75 #define STREQIC(Foo, Bar) (strcmpic((Foo), (Bar)) == 0)
78 /* Authenticator-specific options. */
79 /* I did have server_*_condition options for various mechanisms, but since
80 we only ever handle one mechanism at a time, I didn't see the point in keeping
81 that. In case someone sees a point, I've left the condition_check() API
83 #define LOFF(field) OPT_OFF(auth_gsasl_options_block, field)
85 optionlist auth_gsasl_options[] = {
86 { "client_authz", opt_stringptr, LOFF(client_authz) },
87 { "client_channelbinding", opt_bool, LOFF(client_channelbinding) },
88 { "client_password", opt_stringptr, LOFF(client_password) },
89 { "client_spassword", opt_stringptr, LOFF(client_spassword) },
90 { "client_username", opt_stringptr, LOFF(client_username) },
92 { "server_channelbinding", opt_bool, LOFF(server_channelbinding) },
93 { "server_hostname", opt_stringptr, LOFF(server_hostname) },
94 #ifdef EXIM_GSASL_SCRAM_S_KEY
95 { "server_key", opt_stringptr, LOFF(server_key) },
97 { "server_mech", opt_stringptr, LOFF(server_mech) },
98 { "server_password", opt_stringptr, LOFF(server_password) },
99 { "server_realm", opt_stringptr, LOFF(server_realm) },
100 { "server_scram_iter", opt_stringptr, LOFF(server_scram_iter) },
101 { "server_scram_salt", opt_stringptr, LOFF(server_scram_salt) },
102 #ifdef EXIM_GSASL_SCRAM_S_KEY
103 { "server_skey", opt_stringptr, LOFF(server_s_key) },
105 { "server_service", opt_stringptr, LOFF(server_service) }
108 int auth_gsasl_options_count =
109 sizeof(auth_gsasl_options)/sizeof(optionlist);
111 /* Defaults for the authenticator-specific options. */
112 auth_gsasl_options_block auth_gsasl_option_defaults = {
113 .server_service = US"smtp",
114 .server_hostname = US"$primary_hostname",
115 .server_scram_iter = US"4096",
116 /* all others zero/null */
121 # include "../macro_predef.h"
124 void auth_gsasl_init(auth_instance *ablock) {}
125 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
126 int auth_gsasl_client(auth_instance *ablock, void * sx,
127 int timeout, uschar *buffer, int buffsize) {return 0;}
128 gstring * auth_gsasl_version_report(gstring * g) {return NULL;}
131 auth_gsasl_macros(void)
133 # ifdef EXIM_GSASL_HAVE_SCRAM_SHA_256
134 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_SHA_256");
136 # ifdef EXIM_GSASL_SCRAM_S_KEY
137 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_S_KEY");
141 #else /*!MACRO_PREDEF*/
145 /* "Globals" for managing the gsasl interface. */
147 static Gsasl *gsasl_ctx = NULL;
149 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
151 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
153 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
155 static BOOL sasl_error_should_defer = FALSE;
156 static Gsasl_property callback_loop = 0;
157 static BOOL checked_server_condition = FALSE;
159 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
161 struct callback_exim_state {
162 auth_instance *ablock;
167 /*************************************************
168 * Initialization entry point *
169 *************************************************/
171 /* Called for each instance, after its options have been read, to
172 enable consistency checks to be done, or anything else that needs
176 auth_gsasl_init(auth_instance *ablock)
178 static char * once = NULL;
180 auth_gsasl_options_block *ob =
181 (auth_gsasl_options_block *)(ablock->options_block);
183 /* As per existing Cyrus glue, use the authenticator's public name as
184 the default for the mechanism name; we don't handle multiple mechanisms
185 in one authenticator, but the same driver can be used multiple times. */
187 if (!ob->server_mech)
188 ob->server_mech = string_copy(ablock->public_name);
190 /* Can get multiple session contexts from one library context, so just
191 initialise the once. */
195 if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
196 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
197 "couldn't initialise GNU SASL library: %s (%s)",
198 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
200 gsasl_callback_set(gsasl_ctx, main_callback);
203 /* We don't need this except to log it for debugging. */
205 HDEBUG(D_auth) if (!once)
207 if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
208 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
209 "failed to retrieve list of mechanisms: %s (%s)",
210 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
212 debug_printf("GNU SASL supports: %s\n", once);
215 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
216 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
217 "GNU SASL does not support mechanism \"%s\"",
218 ablock->name, ob->server_mech);
220 if (ablock->server_condition)
221 ablock->server = TRUE;
222 else if( ob->server_mech
223 && !STREQIC(ob->server_mech, US"EXTERNAL")
224 && !STREQIC(ob->server_mech, US"ANONYMOUS")
225 && !STREQIC(ob->server_mech, US"PLAIN")
226 && !STREQIC(ob->server_mech, US"LOGIN")
229 /* At present, for mechanisms we don't panic on absence of server_condition;
230 need to figure out the most generically correct approach to deciding when
231 it's critical and when it isn't. Eg, for simple validation (PLAIN mechanism,
232 etc) it clearly is critical.
235 ablock->server = FALSE;
236 HDEBUG(D_auth) debug_printf("%s authenticator: "
237 "Need server_condition for %s mechanism\n",
238 ablock->name, ob->server_mech);
241 /* This does *not* scale to new SASL mechanisms. Need a better way to ask
242 which properties will be needed. */
244 if ( !ob->server_realm
245 && STREQIC(ob->server_mech, US"DIGEST-MD5"))
247 ablock->server = FALSE;
248 HDEBUG(D_auth) debug_printf("%s authenticator: "
249 "Need server_realm for %s mechanism\n",
250 ablock->name, ob->server_mech);
253 ablock->client = ob->client_username && ob->client_password;
257 /* GNU SASL uses one top-level callback, registered at library level.
258 We dispatch to client and server functions instead. */
261 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
264 struct callback_exim_state *cb_state =
265 (struct callback_exim_state *)gsasl_session_hook_get(sctx);
269 HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
270 #ifdef CHANNELBIND_HACK
271 if (prop == GSASL_CB_TLS_UNIQUE)
274 if ((s = gsasl_callback_hook_get(ctx))) /* Gross hack for early lib vers */
276 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
277 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
281 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE! dummy for now\n");
282 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
287 return GSASL_NO_CALLBACK;
291 debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
292 prop, callback_loop);
294 if (callback_loop > 0)
296 /* Most likely is that we were asked for property FOO, and to
297 expand the string we asked for property BAR to put into an auth
298 variable, but property BAR is not supplied for this mechanism. */
300 debug_printf("Loop, asked for property %d while handling property %d\n",
301 prop, callback_loop);
302 return GSASL_NO_CALLBACK;
304 callback_loop = prop;
306 if (cb_state->currently == CURRENTLY_CLIENT)
307 rc = client_callback(ctx, sctx, prop, cb_state->ablock);
308 else if (cb_state->currently == CURRENTLY_SERVER)
309 rc = server_callback(ctx, sctx, prop, cb_state->ablock);
311 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
312 "unhandled callback state, bug in Exim", cb_state->ablock->name);
320 /*************************************************
321 * Debug service function *
322 *************************************************/
323 static const uschar *
324 gsasl_prop_code_to_name(Gsasl_property prop)
328 case GSASL_AUTHID: return US"AUTHID";
329 case GSASL_AUTHZID: return US"AUTHZID";
330 case GSASL_PASSWORD: return US"PASSWORD";
331 case GSASL_ANONYMOUS_TOKEN: return US"ANONYMOUS_TOKEN";
332 case GSASL_SERVICE: return US"SERVICE";
333 case GSASL_HOSTNAME: return US"HOSTNAME";
334 case GSASL_GSSAPI_DISPLAY_NAME: return US"GSSAPI_DISPLAY_NAME";
335 case GSASL_PASSCODE: return US"PASSCODE";
336 case GSASL_SUGGESTED_PIN: return US"SUGGESTED_PIN";
337 case GSASL_PIN: return US"PIN";
338 case GSASL_REALM: return US"REALM";
339 case GSASL_DIGEST_MD5_HASHED_PASSWORD: return US"DIGEST_MD5_HASHED_PASSWORD";
340 case GSASL_QOPS: return US"QOPS";
341 case GSASL_QOP: return US"QOP";
342 case GSASL_SCRAM_ITER: return US"SCRAM_ITER";
343 case GSASL_SCRAM_SALT: return US"SCRAM_SALT";
344 case GSASL_SCRAM_SALTED_PASSWORD: return US"SCRAM_SALTED_PASSWORD";
345 #ifdef EXIM_GSASL_SCRAM_S_KEY
346 case GSASL_SCRAM_STOREDKEY: return US"SCRAM_STOREDKEY";
347 case GSASL_SCRAM_SERVERKEY: return US"SCRAM_SERVERKEY";
349 #ifdef EXIM_GSASL_HAVE_EXPORTER /* v. 2.1.0 */
350 case GSASL_CB_TLS_EXPORTER: return US"CB_TLS_EXPORTER";
352 case GSASL_CB_TLS_UNIQUE: return US"CB_TLS_UNIQUE";
353 case GSASL_SAML20_IDP_IDENTIFIER: return US"SAML20_IDP_IDENTIFIER";
354 case GSASL_SAML20_REDIRECT_URL: return US"SAML20_REDIRECT_URL";
355 case GSASL_OPENID20_REDIRECT_URL: return US"OPENID20_REDIRECT_URL";
356 case GSASL_OPENID20_OUTCOME_DATA: return US"OPENID20_OUTCOME_DATA";
357 case GSASL_SAML20_AUTHENTICATE_IN_BROWSER: return US"SAML20_AUTHENTICATE_IN_BROWSER";
358 case GSASL_OPENID20_AUTHENTICATE_IN_BROWSER: return US"OPENID20_AUTHENTICATE_IN_BROWSER";
359 case GSASL_VALIDATE_SIMPLE: return US"VALIDATE_SIMPLE";
360 case GSASL_VALIDATE_EXTERNAL: return US"VALIDATE_EXTERNAL";
361 case GSASL_VALIDATE_ANONYMOUS: return US"VALIDATE_ANONYMOUS";
362 case GSASL_VALIDATE_GSSAPI: return US"VALIDATE_GSSAPI";
363 case GSASL_VALIDATE_SECURID: return US"VALIDATE_SECURID";
364 case GSASL_VALIDATE_SAML20: return US"VALIDATE_SAML20";
365 case GSASL_VALIDATE_OPENID20: return US"VALIDATE_OPENID20";
367 return CUS string_sprintf("(unknown prop: %d)", (int)prop);
371 preload_prop(Gsasl_session * sctx, Gsasl_property propcode, const uschar * val)
373 DEBUG(D_auth) debug_printf("preloading prop %s val %s\n",
374 gsasl_prop_code_to_name(propcode), val);
375 gsasl_property_set(sctx, propcode, CCS val);
378 /*************************************************
379 * Server entry point *
380 *************************************************/
382 /* For interface, see auths/README */
385 auth_gsasl_server(auth_instance * ablock, uschar * initial_data)
388 char * to_send, * received;
389 Gsasl_session * sctx = NULL;
390 auth_gsasl_options_block * ob =
391 (auth_gsasl_options_block *)(ablock->options_block);
392 struct callback_exim_state cb_state;
393 int rc, auth_result, exim_error, exim_error_override;
396 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
397 ablock->name, ob->server_mech);
400 if (tls_in.channelbinding && ob->server_channelbinding)
402 # ifndef DISABLE_TLS_RESUME
403 if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
404 { /* per RFC 7677 section 4 */
405 HDEBUG(D_auth) debug_printf(
406 "channel binding not usable on resumed TLS without extended-master-secret");
410 # ifdef CHANNELBIND_HACK
411 /* This is a gross hack to get around the library before 1.9.2
412 a) requiring that c-b was already set, at the _start() call, and
413 b) caching a b64'd version of the binding then which it never updates. */
415 gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
420 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
422 auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
423 gsasl_strerror_name(rc), gsasl_strerror(rc));
424 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
427 /* Hereafter: gsasl_finish(sctx) please */
429 cb_state.ablock = ablock;
430 cb_state.currently = CURRENTLY_SERVER;
431 gsasl_session_hook_set(sctx, &cb_state);
433 tmps = expand_string(ob->server_service);
434 preload_prop(sctx, GSASL_SERVICE, tmps);
435 tmps = expand_string(ob->server_hostname);
436 preload_prop(sctx, GSASL_HOSTNAME, tmps);
437 if (ob->server_realm)
439 tmps = expand_string(ob->server_realm);
441 preload_prop(sctx, GSASL_REALM, tmps);
443 /* We don't support protection layers. */
444 preload_prop(sctx, GSASL_QOPS, US "qop-auth");
447 if (tls_in.channelbinding)
449 /* Some auth mechanisms can ensure that both sides are talking withing the
450 same security context; for TLS, this means that even if a bad certificate
451 has been accepted, they remain MitM-proof because both sides must be within
452 the same negotiated session; if someone is terminating one session and
453 proxying data on within a second, authentication will fail.
455 We might not have this available, depending upon TLS implementation,
456 ciphersuite, phase of moon ...
458 If we do, it results in extra SASL mechanisms being available; here,
459 Exim's one-mechanism-per-authenticator potentially causes problems.
460 It depends upon how GNU SASL will implement the PLUS variants of GS2
461 and whether it automatically mandates a switch to the bound PLUS
462 if the data is available. Since default-on, despite being more secure,
463 would then result in mechanism name changes on a library update, we
464 have little choice but to default it off and let the admin choose to
467 Earlier library versions need this set early, during the _start() call,
468 so we had to misuse gsasl_callback_hook_set/get() as a data transfer
469 mech for the callback done at that time to get the bind-data. More recently
470 the callback is done (if needed) during the first gsasl_stop(). We know
471 the bind-data here so can set it (and should not get a callback).
473 if (ob->server_channelbinding)
475 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
477 # ifndef CHANNELBIND_HACK
479 # ifdef EXIM_GSASL_HAVE_EXPORTER
480 tls_in.channelbind_exporter ? GSASL_CB_TLS_EXPORTER :
483 tls_in.channelbinding);
488 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
493 debug_printf("Auth %s: no channel-binding data available\n",
497 checked_server_condition = FALSE;
499 received = CS initial_data;
501 exim_error = exim_error_override = OK;
504 switch (rc = gsasl_step64(sctx, received, &to_send))
508 goto STOP_INTERACTION;
511 case GSASL_NEEDS_MORE:
514 case GSASL_AUTHENTICATION_ERROR:
515 case GSASL_INTEGRITY_ERROR:
516 case GSASL_NO_AUTHID:
517 case GSASL_NO_ANONYMOUS_TOKEN:
518 case GSASL_NO_AUTHZID:
519 case GSASL_NO_PASSWORD:
520 case GSASL_NO_PASSCODE:
522 case GSASL_BASE64_ERROR:
523 HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
524 gsasl_strerror_name(rc), gsasl_strerror(rc));
525 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
526 "GNU SASL permanent failure: %s (%s)",
527 ablock->name, ob->server_mech,
528 gsasl_strerror_name(rc), gsasl_strerror(rc));
529 if (rc == GSASL_BASE64_ERROR)
530 exim_error_override = BAD64;
531 goto STOP_INTERACTION;
534 auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
535 gsasl_strerror_name(rc), gsasl_strerror(rc));
536 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
537 exim_error_override = DEFER;
538 goto STOP_INTERACTION;
541 /*XXX having our caller send the final smtp "235" is unfortunate; wastes a roundtrip */
542 if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
543 exim_error = auth_get_no64_data(USS &received, US to_send);
552 break; /* handles * cancelled check */
554 } while (rc == GSASL_NEEDS_MORE);
562 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_ITER)))
563 debug_printf(" - itercnt: '%s'\n", s);
564 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALT)))
565 debug_printf(" - salt: '%s'\n", s);
566 #ifdef EXIM_GSASL_SCRAM_S_KEY
567 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SERVERKEY)))
568 debug_printf(" - ServerKey: '%s'\n", s);
569 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_STOREDKEY)))
570 debug_printf(" - StoredKey: '%s'\n", s);
576 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
578 if (exim_error != OK)
581 if (auth_result != GSASL_OK)
583 HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
584 gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
585 if (exim_error_override != OK)
586 return exim_error_override; /* might be DEFER */
587 if (sasl_error_should_defer) /* overriding auth failure SASL error */
592 /* Auth succeeded, check server_condition unless already done in callback */
593 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
597 /* returns the GSASL status of expanding the Exim string given */
599 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
601 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
604 case OK: return GSASL_OK;
605 case DEFER: sasl_error_should_defer = TRUE;
606 return GSASL_AUTHENTICATION_ERROR;
607 case FAIL: return GSASL_AUTHENTICATION_ERROR;
608 default: log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
609 "Unhandled return from checking %s: %d",
610 ablock->name, label, exim_rc);
614 return GSASL_AUTHENTICATION_ERROR;
618 /* Set the "next" $auth[n] and increment expand_nmax */
621 set_exim_authvar_from_prop(Gsasl_session * sctx, Gsasl_property prop)
623 uschar * propval = US gsasl_property_fast(sctx, prop);
624 int i = expand_nmax, j = i + 1;
625 propval = propval ? string_copy(propval) : US"";
626 HDEBUG(D_auth) debug_printf("auth[%d] <= %s'%s'\n",
627 j, gsasl_prop_code_to_name(prop), propval);
628 expand_nstring[j] = propval;
629 expand_nlength[j] = Ustrlen(propval);
630 if (i < AUTH_VARS) auth_vars[i] = propval;
635 set_exim_authvars_from_a_az_r_props(Gsasl_session * sctx)
637 if (expand_nmax > 0 ) return;
639 /* Asking for GSASL_AUTHZID calls back into us if we use
640 gsasl_property_get(), thus the use of gsasl_property_fast().
641 Do we really want to hardcode limits per mechanism? What happens when
642 a new mechanism is added to the library. It *shouldn't* result in us
643 needing to add more glue, since avoiding that is a large part of the
646 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
647 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
648 set_exim_authvar_from_prop(sctx, GSASL_REALM);
653 prop_from_option(Gsasl_session * sctx, Gsasl_property prop,
654 const uschar * option)
656 HDEBUG(D_auth) debug_printf(" %s\n", gsasl_prop_code_to_name(prop));
659 set_exim_authvars_from_a_az_r_props(sctx);
660 option = expand_cstring(option);
661 HDEBUG(D_auth) debug_printf(" '%s'\n", option);
663 gsasl_property_set(sctx, prop, CCS option);
666 HDEBUG(D_auth) debug_printf(" option not set\n");
667 return GSASL_NO_CALLBACK;
671 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
672 auth_instance *ablock)
676 int cbrc = GSASL_NO_CALLBACK;
677 auth_gsasl_options_block * ob =
678 (auth_gsasl_options_block *)(ablock->options_block);
680 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as server\n",
681 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
683 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
688 case GSASL_VALIDATE_SIMPLE:
689 /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
690 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
691 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
692 set_exim_authvar_from_prop(sctx, GSASL_PASSWORD);
694 cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
695 checked_server_condition = TRUE;
698 case GSASL_VALIDATE_EXTERNAL:
699 if (!ablock->server_condition)
701 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
702 cbrc = GSASL_AUTHENTICATION_ERROR;
705 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
707 cbrc = condition_check(ablock,
708 US"server_condition (EXTERNAL)", ablock->server_condition);
709 checked_server_condition = TRUE;
712 case GSASL_VALIDATE_ANONYMOUS:
713 if (!ablock->server_condition)
715 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
716 cbrc = GSASL_AUTHENTICATION_ERROR;
719 set_exim_authvar_from_prop(sctx, GSASL_ANONYMOUS_TOKEN);
721 cbrc = condition_check(ablock,
722 US"server_condition (ANONYMOUS)", ablock->server_condition);
723 checked_server_condition = TRUE;
726 case GSASL_VALIDATE_GSSAPI:
727 /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
728 The display-name is authenticated as part of GSS, the authzid is claimed
729 by the SASL integration after authentication; protected against tampering
730 (if the SASL mechanism supports that, which Kerberos does) but is
731 unverified, same as normal for other mechanisms.
732 First coding, we had these values swapped, but for consistency and prior
733 to the first release of Exim with this authenticator, they've been
734 switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
736 set_exim_authvar_from_prop(sctx, GSASL_GSSAPI_DISPLAY_NAME);
737 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
739 /* In this one case, it perhaps makes sense to default back open?
740 But for consistency, let's just mandate server_condition here too. */
742 cbrc = condition_check(ablock,
743 US"server_condition (GSSAPI family)", ablock->server_condition);
744 checked_server_condition = TRUE;
747 case GSASL_SCRAM_ITER:
748 cbrc = prop_from_option(sctx, prop, ob->server_scram_iter);
751 case GSASL_SCRAM_SALT:
752 cbrc = prop_from_option(sctx, prop, ob->server_scram_salt);
755 #ifdef EXIM_GSASL_SCRAM_S_KEY
756 case GSASL_SCRAM_STOREDKEY:
757 cbrc = prop_from_option(sctx, prop, ob->server_s_key);
760 case GSASL_SCRAM_SERVERKEY:
761 cbrc = prop_from_option(sctx, prop, ob->server_key);
766 /* SCRAM-*: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
767 DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
768 CRAM-MD5: GSASL_AUTHID
769 PLAIN: GSASL_AUTHID and GSASL_AUTHZID
772 set_exim_authvars_from_a_az_r_props(sctx);
774 if (!(s = ob->server_password))
776 HDEBUG(D_auth) debug_printf("option not set\n");
779 if (!(tmps = CS expand_string(s)))
781 sasl_error_should_defer = !f.expand_string_forcedfail;
782 HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
783 "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
784 return GSASL_AUTHENTICATION_ERROR;
786 HDEBUG(D_auth) debug_printf(" set\n");
787 gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
789 /* This is inadequate; don't think Exim's store stacks are geared
790 for memory wiping, so expanding strings will leave stuff laying around.
791 But no need to compound the problem, so get rid of the one we can. */
793 if (US tmps != s) memset(tmps, '\0', strlen(tmps));
798 HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
799 cbrc = GSASL_NO_CALLBACK;
802 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
803 gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
809 /******************************************************************************/
811 #define PROP_OPTIONAL BIT(0)
814 set_client_prop(Gsasl_session * sctx, Gsasl_property prop, uschar * val,
815 unsigned flags, uschar * buffer, int buffsize)
819 if (!val) return !!(flags & PROP_OPTIONAL);
820 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
822 string_format(buffer, buffsize, "%s", expand_string_message);
827 HDEBUG(D_auth) debug_printf("%s: set %s = '%s'\n", __FUNCTION__,
828 gsasl_prop_code_to_name(prop), s);
829 gsasl_property_set(sctx, prop, CS s);
835 /*************************************************
836 * Client entry point *
837 *************************************************/
839 /* For interface, see auths/README */
843 auth_instance * ablock, /* authenticator block */
844 void * sx, /* connection */
845 int timeout, /* command timeout */
846 uschar * buffer, /* buffer for reading response */
847 int buffsize) /* size of buffer */
849 auth_gsasl_options_block * ob =
850 (auth_gsasl_options_block *)(ablock->options_block);
851 Gsasl_session * sctx = NULL;
852 struct callback_exim_state cb_state;
855 int rc, yield = FAIL;
858 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
859 ablock->name, ob->server_mech);
864 if (tls_out.channelbinding && ob->client_channelbinding)
866 # ifndef DISABLE_TLS_RESUME
867 if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
868 { /* Per RFC 7677 section 4. See also RFC 7627, "Triple Handshake"
869 vulnerability, and https://www.mitls.org/pages/attacks/3SHAKE */
870 string_format(buffer, buffsize, "%s",
871 "channel binding not usable on resumed TLS without extended-master-secret");
875 # ifdef CHANNELBIND_HACK
876 /* This is a gross hack to get around the library before 1.9.2
877 a) requiring that c-b was already set, at the _start() call, and
878 b) caching a b64'd version of the binding then which it never updates. */
880 gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
885 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
887 string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
888 gsasl_strerror_name(rc), gsasl_strerror(rc));
889 HDEBUG(D_auth) debug_printf("%s\n", buffer);
893 cb_state.ablock = ablock;
894 cb_state.currently = CURRENTLY_CLIENT;
895 gsasl_session_hook_set(sctx, &cb_state);
899 if ( !set_client_prop(sctx, GSASL_PASSWORD, ob->client_password,
901 || !set_client_prop(sctx, GSASL_AUTHID, ob->client_username,
903 || !set_client_prop(sctx, GSASL_AUTHZID, ob->client_authz,
904 PROP_OPTIONAL, buffer, buffsize)
909 if (tls_out.channelbinding)
910 if (ob->client_channelbinding)
912 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
914 # ifndef CHANNELBIND_HACK
916 # ifdef EXIM_GSASL_HAVE_EXPORTER
917 tls_out.channelbind_exporter ? GSASL_CB_TLS_EXPORTER :
920 tls_out.channelbinding);
925 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
929 /* Run the SASL conversation with the server */
936 rc = gsasl_step64(sctx, CS s, CSS &outstr);
938 if (rc == GSASL_NEEDS_MORE || rc == GSASL_OK)
941 ? smtp_write_command(sx, SCMD_FLUSH,
942 outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
943 ablock->public_name, outstr) <= 0
945 ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
956 if (rc != GSASL_NEEDS_MORE)
960 string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
964 /* expecting a final 2xx from the server, accepting the AUTH */
966 if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
968 break; /* from SASL sequence loop */
971 /* 2xx or 3xx response is acceptable. If 2xx, no further input */
973 if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
974 if (errno == 0 && buffer[0] == '2')
988 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
989 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
990 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
991 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD);
999 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
1001 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n",
1002 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
1005 #ifdef EXIM_GSASL_HAVE_EXPORTER
1006 case GSASL_CB_TLS_EXPORTER: /* Should never get called for this, as pre-set */
1007 if (!tls_out.channelbind_exporter) break;
1008 HDEBUG(D_auth) debug_printf(" filling in\n");
1009 gsasl_property_set(sctx, GSASL_CB_TLS_EXPORTER, CCS tls_out.channelbinding);
1012 case GSASL_CB_TLS_UNIQUE: /* Should never get called for this, as pre-set */
1013 #ifdef EXIM_GSASL_HAVE_EXPORTER
1014 if (tls_out.channelbind_exporter) break;
1016 HDEBUG(D_auth) debug_printf(" filling in\n");
1017 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
1019 case GSASL_SCRAM_SALTED_PASSWORD:
1021 uschar * client_spassword =
1022 ((auth_gsasl_options_block *) ablock->options_block)->client_spassword;
1024 HDEBUG(D_auth) if (!client_spassword)
1025 debug_printf(" client_spassword option unset\n");
1026 if (client_spassword)
1029 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
1030 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
1031 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
1032 set_client_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD, client_spassword,
1033 0, dummy, sizeof(dummy));
1034 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
1041 debug_printf(" not providing one\n");
1044 return GSASL_NO_CALLBACK;
1047 /*************************************************
1049 *************************************************/
1052 auth_gsasl_version_report(gstring * g)
1054 return string_fmt_append(g, "Library version: GNU SASL: Compile: %s\n"
1056 GSASL_VERSION, gsasl_check_version(NULL));
1062 void auth_gsasl_macros(void) {}
1064 #endif /*!MACRO_PREDEF*/
1065 #endif /* AUTH_GSASL */
1067 /* End of gsasl_exim.c */