1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2019 - 2022 */
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.
32 /* dummy function to satisfy compilers when we link in an "empty" file. */
33 static void dummy(int x);
34 static void dummy2(int x) { dummy(x-1); }
35 static void dummy(int x) { dummy2(x-1); }
39 #include "gsasl_exim.h"
42 #if GSASL_VERSION_MINOR >= 10
43 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
44 # define EXIM_GSASL_SCRAM_S_KEY
46 #elif GSASL_VERSION_MINOR == 9
47 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
49 # if GSASL_VERSION_PATCH >= 1
50 # define EXIM_GSASL_SCRAM_S_KEY
52 # if GSASL_VERSION_PATCH < 2
53 # define CHANNELBIND_HACK
57 # define CHANNELBIND_HACK
60 /* Convenience for testing strings */
62 #define STREQIC(Foo, Bar) (strcmpic((Foo), (Bar)) == 0)
65 /* Authenticator-specific options. */
66 /* I did have server_*_condition options for various mechanisms, but since
67 we only ever handle one mechanism at a time, I didn't see the point in keeping
68 that. In case someone sees a point, I've left the condition_check() API
70 #define LOFF(field) OPT_OFF(auth_gsasl_options_block, field)
72 optionlist auth_gsasl_options[] = {
73 { "client_authz", opt_stringptr, LOFF(client_authz) },
74 { "client_channelbinding", opt_bool, LOFF(client_channelbinding) },
75 { "client_password", opt_stringptr, LOFF(client_password) },
76 { "client_spassword", opt_stringptr, LOFF(client_spassword) },
77 { "client_username", opt_stringptr, LOFF(client_username) },
79 { "server_channelbinding", opt_bool, LOFF(server_channelbinding) },
80 { "server_hostname", opt_stringptr, LOFF(server_hostname) },
81 #ifdef EXIM_GSASL_SCRAM_S_KEY
82 { "server_key", opt_stringptr, LOFF(server_key) },
84 { "server_mech", opt_stringptr, LOFF(server_mech) },
85 { "server_password", opt_stringptr, LOFF(server_password) },
86 { "server_realm", opt_stringptr, LOFF(server_realm) },
87 { "server_scram_iter", opt_stringptr, LOFF(server_scram_iter) },
88 { "server_scram_salt", opt_stringptr, LOFF(server_scram_salt) },
89 #ifdef EXIM_GSASL_SCRAM_S_KEY
90 { "server_skey", opt_stringptr, LOFF(server_s_key) },
92 { "server_service", opt_stringptr, LOFF(server_service) }
95 int auth_gsasl_options_count =
96 sizeof(auth_gsasl_options)/sizeof(optionlist);
98 /* Defaults for the authenticator-specific options. */
99 auth_gsasl_options_block auth_gsasl_option_defaults = {
100 .server_service = US"smtp",
101 .server_hostname = US"$primary_hostname",
102 .server_scram_iter = US"4096",
103 /* all others zero/null */
108 # include "../macro_predef.h"
111 void auth_gsasl_init(auth_instance *ablock) {}
112 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
113 int auth_gsasl_client(auth_instance *ablock, void * sx,
114 int timeout, uschar *buffer, int buffsize) {return 0;}
115 gstring * auth_gsasl_version_report(gstring * g) {return NULL;}
118 auth_gsasl_macros(void)
120 # ifdef EXIM_GSASL_HAVE_SCRAM_SHA_256
121 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_SHA_256");
123 # ifdef EXIM_GSASL_SCRAM_S_KEY
124 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_S_KEY");
128 #else /*!MACRO_PREDEF*/
132 /* "Globals" for managing the gsasl interface. */
134 static Gsasl *gsasl_ctx = NULL;
136 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
138 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
140 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
142 static BOOL sasl_error_should_defer = FALSE;
143 static Gsasl_property callback_loop = 0;
144 static BOOL checked_server_condition = FALSE;
146 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
148 struct callback_exim_state {
149 auth_instance *ablock;
154 /*************************************************
155 * Initialization entry point *
156 *************************************************/
158 /* Called for each instance, after its options have been read, to
159 enable consistency checks to be done, or anything else that needs
163 auth_gsasl_init(auth_instance *ablock)
165 static char * once = NULL;
167 auth_gsasl_options_block *ob =
168 (auth_gsasl_options_block *)(ablock->options_block);
170 /* As per existing Cyrus glue, use the authenticator's public name as
171 the default for the mechanism name; we don't handle multiple mechanisms
172 in one authenticator, but the same driver can be used multiple times. */
174 if (!ob->server_mech)
175 ob->server_mech = string_copy(ablock->public_name);
177 /* Can get multiple session contexts from one library context, so just
178 initialise the once. */
182 if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
183 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
184 "couldn't initialise GNU SASL library: %s (%s)",
185 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
187 gsasl_callback_set(gsasl_ctx, main_callback);
190 /* We don't need this except to log it for debugging. */
192 HDEBUG(D_auth) if (!once)
194 if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
195 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
196 "failed to retrieve list of mechanisms: %s (%s)",
197 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
199 debug_printf("GNU SASL supports: %s\n", once);
202 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
203 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
204 "GNU SASL does not support mechanism \"%s\"",
205 ablock->name, ob->server_mech);
207 if (ablock->server_condition)
208 ablock->server = TRUE;
209 else if( ob->server_mech
210 && !STREQIC(ob->server_mech, US"EXTERNAL")
211 && !STREQIC(ob->server_mech, US"ANONYMOUS")
212 && !STREQIC(ob->server_mech, US"PLAIN")
213 && !STREQIC(ob->server_mech, US"LOGIN")
216 /* At present, for mechanisms we don't panic on absence of server_condition;
217 need to figure out the most generically correct approach to deciding when
218 it's critical and when it isn't. Eg, for simple validation (PLAIN mechanism,
219 etc) it clearly is critical.
222 ablock->server = FALSE;
223 HDEBUG(D_auth) debug_printf("%s authenticator: "
224 "Need server_condition for %s mechanism\n",
225 ablock->name, ob->server_mech);
228 /* This does *not* scale to new SASL mechanisms. Need a better way to ask
229 which properties will be needed. */
231 if ( !ob->server_realm
232 && STREQIC(ob->server_mech, US"DIGEST-MD5"))
234 ablock->server = FALSE;
235 HDEBUG(D_auth) debug_printf("%s authenticator: "
236 "Need server_realm for %s mechanism\n",
237 ablock->name, ob->server_mech);
240 ablock->client = ob->client_username && ob->client_password;
244 /* GNU SASL uses one top-level callback, registered at library level.
245 We dispatch to client and server functions instead. */
248 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
251 struct callback_exim_state *cb_state =
252 (struct callback_exim_state *)gsasl_session_hook_get(sctx);
256 HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
257 #ifdef CHANNELBIND_HACK
258 if (prop == GSASL_CB_TLS_UNIQUE)
261 if ((s = gsasl_callback_hook_get(ctx)))
263 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
264 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
268 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE! dummy for now\n");
269 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
274 return GSASL_NO_CALLBACK;
278 debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
279 prop, callback_loop);
281 if (callback_loop > 0)
283 /* Most likely is that we were asked for property FOO, and to
284 expand the string we asked for property BAR to put into an auth
285 variable, but property BAR is not supplied for this mechanism. */
287 debug_printf("Loop, asked for property %d while handling property %d\n",
288 prop, callback_loop);
289 return GSASL_NO_CALLBACK;
291 callback_loop = prop;
293 if (cb_state->currently == CURRENTLY_CLIENT)
294 rc = client_callback(ctx, sctx, prop, cb_state->ablock);
295 else if (cb_state->currently == CURRENTLY_SERVER)
296 rc = server_callback(ctx, sctx, prop, cb_state->ablock);
298 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
299 "unhandled callback state, bug in Exim", cb_state->ablock->name);
307 /*************************************************
308 * Debug service function *
309 *************************************************/
310 static const uschar *
311 gsasl_prop_code_to_name(Gsasl_property prop)
315 case GSASL_AUTHID: return US"AUTHID";
316 case GSASL_AUTHZID: return US"AUTHZID";
317 case GSASL_PASSWORD: return US"PASSWORD";
318 case GSASL_ANONYMOUS_TOKEN: return US"ANONYMOUS_TOKEN";
319 case GSASL_SERVICE: return US"SERVICE";
320 case GSASL_HOSTNAME: return US"HOSTNAME";
321 case GSASL_GSSAPI_DISPLAY_NAME: return US"GSSAPI_DISPLAY_NAME";
322 case GSASL_PASSCODE: return US"PASSCODE";
323 case GSASL_SUGGESTED_PIN: return US"SUGGESTED_PIN";
324 case GSASL_PIN: return US"PIN";
325 case GSASL_REALM: return US"REALM";
326 case GSASL_DIGEST_MD5_HASHED_PASSWORD: return US"DIGEST_MD5_HASHED_PASSWORD";
327 case GSASL_QOPS: return US"QOPS";
328 case GSASL_QOP: return US"QOP";
329 case GSASL_SCRAM_ITER: return US"SCRAM_ITER";
330 case GSASL_SCRAM_SALT: return US"SCRAM_SALT";
331 case GSASL_SCRAM_SALTED_PASSWORD: return US"SCRAM_SALTED_PASSWORD";
332 #ifdef EXIM_GSASL_SCRAM_S_KEY
333 case GSASL_SCRAM_STOREDKEY: return US"SCRAM_STOREDKEY";
334 case GSASL_SCRAM_SERVERKEY: return US"SCRAM_SERVERKEY";
336 case GSASL_CB_TLS_UNIQUE: return US"CB_TLS_UNIQUE";
337 case GSASL_SAML20_IDP_IDENTIFIER: return US"SAML20_IDP_IDENTIFIER";
338 case GSASL_SAML20_REDIRECT_URL: return US"SAML20_REDIRECT_URL";
339 case GSASL_OPENID20_REDIRECT_URL: return US"OPENID20_REDIRECT_URL";
340 case GSASL_OPENID20_OUTCOME_DATA: return US"OPENID20_OUTCOME_DATA";
341 case GSASL_SAML20_AUTHENTICATE_IN_BROWSER: return US"SAML20_AUTHENTICATE_IN_BROWSER";
342 case GSASL_OPENID20_AUTHENTICATE_IN_BROWSER: return US"OPENID20_AUTHENTICATE_IN_BROWSER";
343 case GSASL_VALIDATE_SIMPLE: return US"VALIDATE_SIMPLE";
344 case GSASL_VALIDATE_EXTERNAL: return US"VALIDATE_EXTERNAL";
345 case GSASL_VALIDATE_ANONYMOUS: return US"VALIDATE_ANONYMOUS";
346 case GSASL_VALIDATE_GSSAPI: return US"VALIDATE_GSSAPI";
347 case GSASL_VALIDATE_SECURID: return US"VALIDATE_SECURID";
348 case GSASL_VALIDATE_SAML20: return US"VALIDATE_SAML20";
349 case GSASL_VALIDATE_OPENID20: return US"VALIDATE_OPENID20";
351 return CUS string_sprintf("(unknown prop: %d)", (int)prop);
354 /*************************************************
355 * Server entry point *
356 *************************************************/
358 /* For interface, see auths/README */
361 auth_gsasl_server(auth_instance *ablock, uschar *initial_data)
364 char *to_send, *received;
365 Gsasl_session *sctx = NULL;
366 auth_gsasl_options_block *ob =
367 (auth_gsasl_options_block *)(ablock->options_block);
368 struct callback_exim_state cb_state;
369 int rc, auth_result, exim_error, exim_error_override;
372 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
373 ablock->name, ob->server_mech);
376 if (tls_in.channelbinding && ob->server_channelbinding)
378 # ifndef DISABLE_TLS_RESUME
379 if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
380 { /* per RFC 7677 section 4 */
381 HDEBUG(D_auth) debug_printf(
382 "channel binding not usable on resumed TLS without extended-master-secret");
386 # ifdef CHANNELBIND_HACK
387 /* This is a gross hack to get around the library before 1.9.2
388 a) requiring that c-b was already set, at the _start() call, and
389 b) caching a b64'd version of the binding then which it never updates. */
391 gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
396 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
398 auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
399 gsasl_strerror_name(rc), gsasl_strerror(rc));
400 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
403 /* Hereafter: gsasl_finish(sctx) please */
405 cb_state.ablock = ablock;
406 cb_state.currently = CURRENTLY_SERVER;
407 gsasl_session_hook_set(sctx, &cb_state);
409 tmps = CS expand_string(ob->server_service);
410 gsasl_property_set(sctx, GSASL_SERVICE, tmps);
411 tmps = CS expand_string(ob->server_hostname);
412 gsasl_property_set(sctx, GSASL_HOSTNAME, tmps);
413 if (ob->server_realm)
415 tmps = CS expand_string(ob->server_realm);
417 gsasl_property_set(sctx, GSASL_REALM, tmps);
419 /* We don't support protection layers. */
420 gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
423 if (tls_in.channelbinding)
425 /* Some auth mechanisms can ensure that both sides are talking withing the
426 same security context; for TLS, this means that even if a bad certificate
427 has been accepted, they remain MitM-proof because both sides must be within
428 the same negotiated session; if someone is terminating one session and
429 proxying data on within a second, authentication will fail.
431 We might not have this available, depending upon TLS implementation,
432 ciphersuite, phase of moon ...
434 If we do, it results in extra SASL mechanisms being available; here,
435 Exim's one-mechanism-per-authenticator potentially causes problems.
436 It depends upon how GNU SASL will implement the PLUS variants of GS2
437 and whether it automatically mandates a switch to the bound PLUS
438 if the data is available. Since default-on, despite being more secure,
439 would then result in mechanism name changes on a library update, we
440 have little choice but to default it off and let the admin choose to
443 Earlier library versions need this set early, during the _start() call,
444 so we had to misuse gsasl_callback_hook_set/get() as a data transfer
445 mech for the callback done at that time to get the bind-data. More recently
446 the callback is done (if needed) during the first gsasl_stop(). We know
447 the bind-data here so can set it (and should not get a callback).
449 if (ob->server_channelbinding)
451 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
453 # ifndef CHANNELBIND_HACK
454 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding);
459 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
464 debug_printf("Auth %s: no channel-binding data available\n",
468 checked_server_condition = FALSE;
470 received = CS initial_data;
472 exim_error = exim_error_override = OK;
475 switch (rc = gsasl_step64(sctx, received, &to_send))
479 goto STOP_INTERACTION;
482 case GSASL_NEEDS_MORE:
485 case GSASL_AUTHENTICATION_ERROR:
486 case GSASL_INTEGRITY_ERROR:
487 case GSASL_NO_AUTHID:
488 case GSASL_NO_ANONYMOUS_TOKEN:
489 case GSASL_NO_AUTHZID:
490 case GSASL_NO_PASSWORD:
491 case GSASL_NO_PASSCODE:
493 case GSASL_BASE64_ERROR:
494 HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
495 gsasl_strerror_name(rc), gsasl_strerror(rc));
496 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
497 "GNU SASL permanent failure: %s (%s)",
498 ablock->name, ob->server_mech,
499 gsasl_strerror_name(rc), gsasl_strerror(rc));
500 if (rc == GSASL_BASE64_ERROR)
501 exim_error_override = BAD64;
502 goto STOP_INTERACTION;
505 auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
506 gsasl_strerror_name(rc), gsasl_strerror(rc));
507 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
508 exim_error_override = DEFER;
509 goto STOP_INTERACTION;
512 /*XXX having our caller send the final smtp "235" is unfortunate; wastes a roundtrip */
513 if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
514 exim_error = auth_get_no64_data(USS &received, US to_send);
523 break; /* handles * cancelled check */
525 } while (rc == GSASL_NEEDS_MORE);
533 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_ITER)))
534 debug_printf(" - itercnt: '%s'\n", s);
535 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALT)))
536 debug_printf(" - salt: '%s'\n", s);
537 #ifdef EXIM_GSASL_SCRAM_S_KEY
538 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SERVERKEY)))
539 debug_printf(" - ServerKey: '%s'\n", s);
540 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_STOREDKEY)))
541 debug_printf(" - StoredKey: '%s'\n", s);
547 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
549 if (exim_error != OK)
552 if (auth_result != GSASL_OK)
554 HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
555 gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
556 if (exim_error_override != OK)
557 return exim_error_override; /* might be DEFER */
558 if (sasl_error_should_defer) /* overriding auth failure SASL error */
563 /* Auth succeeded, check server_condition unless already done in callback */
564 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
568 /* returns the GSASL status of expanding the Exim string given */
570 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
572 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
575 case OK: return GSASL_OK;
576 case DEFER: sasl_error_should_defer = TRUE;
577 return GSASL_AUTHENTICATION_ERROR;
578 case FAIL: return GSASL_AUTHENTICATION_ERROR;
579 default: log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
580 "Unhandled return from checking %s: %d",
581 ablock->name, label, exim_rc);
585 return GSASL_AUTHENTICATION_ERROR;
589 /* Set the "next" $auth[n] and increment expand_nmax */
592 set_exim_authvar_from_prop(Gsasl_session * sctx, Gsasl_property prop)
594 uschar * propval = US gsasl_property_fast(sctx, prop);
595 int i = expand_nmax, j = i + 1;
596 propval = propval ? string_copy(propval) : US"";
597 HDEBUG(D_auth) debug_printf("auth[%d] <= %s'%s'\n",
598 j, gsasl_prop_code_to_name(prop), propval);
599 expand_nstring[j] = propval;
600 expand_nlength[j] = Ustrlen(propval);
601 if (i < AUTH_VARS) auth_vars[i] = propval;
606 set_exim_authvars_from_a_az_r_props(Gsasl_session * sctx)
608 if (expand_nmax > 0 ) return;
610 /* Asking for GSASL_AUTHZID calls back into us if we use
611 gsasl_property_get(), thus the use of gsasl_property_fast().
612 Do we really want to hardcode limits per mechanism? What happens when
613 a new mechanism is added to the library. It *shouldn't* result in us
614 needing to add more glue, since avoiding that is a large part of the
617 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
618 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
619 set_exim_authvar_from_prop(sctx, GSASL_REALM);
624 prop_from_option(Gsasl_session * sctx, Gsasl_property prop,
625 const uschar * option)
627 HDEBUG(D_auth) debug_printf(" %s\n", gsasl_prop_code_to_name(prop));
630 set_exim_authvars_from_a_az_r_props(sctx);
631 option = expand_cstring(option);
632 HDEBUG(D_auth) debug_printf(" '%s'\n", option);
634 gsasl_property_set(sctx, prop, CCS option);
637 HDEBUG(D_auth) debug_printf(" option not set\n");
638 return GSASL_NO_CALLBACK;
642 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
643 auth_instance *ablock)
647 int cbrc = GSASL_NO_CALLBACK;
648 auth_gsasl_options_block * ob =
649 (auth_gsasl_options_block *)(ablock->options_block);
651 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as server\n",
652 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
654 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
659 case GSASL_VALIDATE_SIMPLE:
660 /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
661 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
662 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
663 set_exim_authvar_from_prop(sctx, GSASL_PASSWORD);
665 cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
666 checked_server_condition = TRUE;
669 case GSASL_VALIDATE_EXTERNAL:
670 if (!ablock->server_condition)
672 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
673 cbrc = GSASL_AUTHENTICATION_ERROR;
676 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
678 cbrc = condition_check(ablock,
679 US"server_condition (EXTERNAL)", ablock->server_condition);
680 checked_server_condition = TRUE;
683 case GSASL_VALIDATE_ANONYMOUS:
684 if (!ablock->server_condition)
686 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
687 cbrc = GSASL_AUTHENTICATION_ERROR;
690 set_exim_authvar_from_prop(sctx, GSASL_ANONYMOUS_TOKEN);
692 cbrc = condition_check(ablock,
693 US"server_condition (ANONYMOUS)", ablock->server_condition);
694 checked_server_condition = TRUE;
697 case GSASL_VALIDATE_GSSAPI:
698 /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
699 The display-name is authenticated as part of GSS, the authzid is claimed
700 by the SASL integration after authentication; protected against tampering
701 (if the SASL mechanism supports that, which Kerberos does) but is
702 unverified, same as normal for other mechanisms.
703 First coding, we had these values swapped, but for consistency and prior
704 to the first release of Exim with this authenticator, they've been
705 switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
707 set_exim_authvar_from_prop(sctx, GSASL_GSSAPI_DISPLAY_NAME);
708 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
710 /* In this one case, it perhaps makes sense to default back open?
711 But for consistency, let's just mandate server_condition here too. */
713 cbrc = condition_check(ablock,
714 US"server_condition (GSSAPI family)", ablock->server_condition);
715 checked_server_condition = TRUE;
718 case GSASL_SCRAM_ITER:
719 cbrc = prop_from_option(sctx, prop, ob->server_scram_iter);
722 case GSASL_SCRAM_SALT:
723 cbrc = prop_from_option(sctx, prop, ob->server_scram_salt);
726 #ifdef EXIM_GSASL_SCRAM_S_KEY
727 case GSASL_SCRAM_STOREDKEY:
728 cbrc = prop_from_option(sctx, prop, ob->server_s_key);
731 case GSASL_SCRAM_SERVERKEY:
732 cbrc = prop_from_option(sctx, prop, ob->server_key);
737 /* SCRAM-*: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
738 DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
739 CRAM-MD5: GSASL_AUTHID
740 PLAIN: GSASL_AUTHID and GSASL_AUTHZID
743 set_exim_authvars_from_a_az_r_props(sctx);
745 if (!(s = ob->server_password))
747 HDEBUG(D_auth) debug_printf("option not set\n");
750 if (!(tmps = CS expand_string(s)))
752 sasl_error_should_defer = !f.expand_string_forcedfail;
753 HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
754 "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
755 return GSASL_AUTHENTICATION_ERROR;
757 HDEBUG(D_auth) debug_printf(" set\n");
758 gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
760 /* This is inadequate; don't think Exim's store stacks are geared
761 for memory wiping, so expanding strings will leave stuff laying around.
762 But no need to compound the problem, so get rid of the one we can. */
764 if (US tmps != s) memset(tmps, '\0', strlen(tmps));
769 HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
770 cbrc = GSASL_NO_CALLBACK;
773 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
774 gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
780 /******************************************************************************/
782 #define PROP_OPTIONAL BIT(0)
785 set_client_prop(Gsasl_session * sctx, Gsasl_property prop, uschar * val,
786 unsigned flags, uschar * buffer, int buffsize)
790 if (!val) return !!(flags & PROP_OPTIONAL);
791 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
793 string_format(buffer, buffsize, "%s", expand_string_message);
798 HDEBUG(D_auth) debug_printf("%s: set %s = '%s'\n", __FUNCTION__,
799 gsasl_prop_code_to_name(prop), s);
800 gsasl_property_set(sctx, prop, CS s);
806 /*************************************************
807 * Client entry point *
808 *************************************************/
810 /* For interface, see auths/README */
814 auth_instance *ablock, /* authenticator block */
815 void * sx, /* connection */
816 int timeout, /* command timeout */
817 uschar *buffer, /* buffer for reading response */
818 int buffsize) /* size of buffer */
820 auth_gsasl_options_block *ob =
821 (auth_gsasl_options_block *)(ablock->options_block);
822 Gsasl_session * sctx = NULL;
823 struct callback_exim_state cb_state;
826 int rc, yield = FAIL;
829 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
830 ablock->name, ob->server_mech);
835 if (tls_out.channelbinding && ob->client_channelbinding)
837 # ifndef DISABLE_TLS_RESUME
838 if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
839 { /* Per RFC 7677 section 4. See also RFC 7627, "Triple Handshake"
840 vulnerability, and https://www.mitls.org/pages/attacks/3SHAKE */
841 string_format(buffer, buffsize, "%s",
842 "channel binding not usable on resumed TLS without extended-master-secret");
846 # ifdef CHANNELBIND_HACK
847 /* This is a gross hack to get around the library before 1.9.2
848 a) requiring that c-b was already set, at the _start() call, and
849 b) caching a b64'd version of the binding then which it never updates. */
851 gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
856 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
858 string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
859 gsasl_strerror_name(rc), gsasl_strerror(rc));
860 HDEBUG(D_auth) debug_printf("%s\n", buffer);
864 cb_state.ablock = ablock;
865 cb_state.currently = CURRENTLY_CLIENT;
866 gsasl_session_hook_set(sctx, &cb_state);
870 if ( !set_client_prop(sctx, GSASL_PASSWORD, ob->client_password,
872 || !set_client_prop(sctx, GSASL_AUTHID, ob->client_username,
874 || !set_client_prop(sctx, GSASL_AUTHZID, ob->client_authz,
875 PROP_OPTIONAL, buffer, buffsize)
880 if (tls_out.channelbinding)
881 if (ob->client_channelbinding)
883 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
885 # ifndef CHANNELBIND_HACK
886 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
891 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
895 /* Run the SASL conversation with the server */
902 rc = gsasl_step64(sctx, CS s, CSS &outstr);
904 if (rc == GSASL_NEEDS_MORE || rc == GSASL_OK)
907 ? smtp_write_command(sx, SCMD_FLUSH,
908 outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
909 ablock->public_name, outstr) <= 0
911 ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
922 if (rc != GSASL_NEEDS_MORE)
926 string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
930 /* expecting a final 2xx from the server, accepting the AUTH */
932 if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
934 break; /* from SASL sequence loop */
937 /* 2xx or 3xx response is acceptable. If 2xx, no further input */
939 if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
940 if (errno == 0 && buffer[0] == '2')
954 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
955 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
956 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
957 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD);
965 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
967 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n",
968 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
971 case GSASL_CB_TLS_UNIQUE: /*XXX should never get called for this */
973 debug_printf(" filling in\n");
974 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
976 case GSASL_SCRAM_SALTED_PASSWORD:
978 uschar * client_spassword =
979 ((auth_gsasl_options_block *) ablock->options_block)->client_spassword;
981 HDEBUG(D_auth) if (!client_spassword)
982 debug_printf(" client_spassword option unset\n");
983 if (client_spassword)
986 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
987 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
988 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
989 set_client_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD, client_spassword,
990 0, dummy, sizeof(dummy));
991 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
998 debug_printf(" not providing one\n");
1001 return GSASL_NO_CALLBACK;
1004 /*************************************************
1006 *************************************************/
1009 auth_gsasl_version_report(gstring * g)
1011 return string_fmt_append(g, "Library version: GNU SASL: Compile: %s\n"
1013 GSASL_VERSION, gsasl_check_version(NULL));
1019 void auth_gsasl_macros(void) {}
1021 #endif /*!MACRO_PREDEF*/
1022 #endif /* AUTH_GSASL */
1024 /* End of gsasl_exim.c */