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_MAJOR == 2
44 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
45 # define EXIM_GSASL_SCRAM_S_KEY
46 # if GSASL_VERSION_MINOR >= 1
47 # define EXIM_GSASL_HAVE_EXPORTER
48 # elif GSASL_VERSION_PATCH >= 1
49 # define EXIM_GSASL_HAVE_EXPORTER
52 #elif GSASL_VERSION_MAJOR == 1
53 # if GSASL_VERSION_MINOR >= 10
54 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
55 # define EXIM_GSASL_SCRAM_S_KEY
57 # elif GSASL_VERSION_MINOR == 9
58 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
60 # if GSASL_VERSION_PATCH >= 1
61 # define EXIM_GSASL_SCRAM_S_KEY
63 # if GSASL_VERSION_PATCH < 2
64 # define CHANNELBIND_HACK
68 # define CHANNELBIND_HACK
72 /* Convenience for testing strings */
74 #define STREQIC(Foo, Bar) (strcmpic((Foo), (Bar)) == 0)
77 /* Authenticator-specific options. */
78 /* I did have server_*_condition options for various mechanisms, but since
79 we only ever handle one mechanism at a time, I didn't see the point in keeping
80 that. In case someone sees a point, I've left the condition_check() API
82 #define LOFF(field) OPT_OFF(auth_gsasl_options_block, field)
84 optionlist auth_gsasl_options[] = {
85 { "client_authz", opt_stringptr, LOFF(client_authz) },
86 { "client_channelbinding", opt_bool, LOFF(client_channelbinding) },
87 { "client_password", opt_stringptr, LOFF(client_password) },
88 { "client_spassword", opt_stringptr, LOFF(client_spassword) },
89 { "client_username", opt_stringptr, LOFF(client_username) },
91 { "server_channelbinding", opt_bool, LOFF(server_channelbinding) },
92 { "server_hostname", opt_stringptr, LOFF(server_hostname) },
93 #ifdef EXIM_GSASL_SCRAM_S_KEY
94 { "server_key", opt_stringptr, LOFF(server_key) },
96 { "server_mech", opt_stringptr, LOFF(server_mech) },
97 { "server_password", opt_stringptr, LOFF(server_password) },
98 { "server_realm", opt_stringptr, LOFF(server_realm) },
99 { "server_scram_iter", opt_stringptr, LOFF(server_scram_iter) },
100 { "server_scram_salt", opt_stringptr, LOFF(server_scram_salt) },
101 #ifdef EXIM_GSASL_SCRAM_S_KEY
102 { "server_skey", opt_stringptr, LOFF(server_s_key) },
104 { "server_service", opt_stringptr, LOFF(server_service) }
107 int auth_gsasl_options_count =
108 sizeof(auth_gsasl_options)/sizeof(optionlist);
110 /* Defaults for the authenticator-specific options. */
111 auth_gsasl_options_block auth_gsasl_option_defaults = {
112 .server_service = US"smtp",
113 .server_hostname = US"$primary_hostname",
114 .server_scram_iter = US"4096",
115 /* all others zero/null */
120 # include "../macro_predef.h"
123 void auth_gsasl_init(auth_instance *ablock) {}
124 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
125 int auth_gsasl_client(auth_instance *ablock, void * sx,
126 int timeout, uschar *buffer, int buffsize) {return 0;}
127 gstring * auth_gsasl_version_report(gstring * g) {return NULL;}
130 auth_gsasl_macros(void)
132 # ifdef EXIM_GSASL_HAVE_SCRAM_SHA_256
133 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_SHA_256");
135 # ifdef EXIM_GSASL_SCRAM_S_KEY
136 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_S_KEY");
140 #else /*!MACRO_PREDEF*/
144 /* "Globals" for managing the gsasl interface. */
146 static Gsasl *gsasl_ctx = NULL;
148 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
150 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
152 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
154 static BOOL sasl_error_should_defer = FALSE;
155 static Gsasl_property callback_loop = 0;
156 static BOOL checked_server_condition = FALSE;
158 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
160 struct callback_exim_state {
161 auth_instance *ablock;
166 /*************************************************
167 * Initialization entry point *
168 *************************************************/
170 /* Called for each instance, after its options have been read, to
171 enable consistency checks to be done, or anything else that needs
175 auth_gsasl_init(auth_instance *ablock)
177 static char * once = NULL;
179 auth_gsasl_options_block *ob =
180 (auth_gsasl_options_block *)(ablock->options_block);
182 /* As per existing Cyrus glue, use the authenticator's public name as
183 the default for the mechanism name; we don't handle multiple mechanisms
184 in one authenticator, but the same driver can be used multiple times. */
186 if (!ob->server_mech)
187 ob->server_mech = string_copy(ablock->public_name);
189 /* Can get multiple session contexts from one library context, so just
190 initialise the once. */
194 if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
195 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
196 "couldn't initialise GNU SASL library: %s (%s)",
197 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
199 gsasl_callback_set(gsasl_ctx, main_callback);
202 /* We don't need this except to log it for debugging. */
204 HDEBUG(D_auth) if (!once)
206 if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
207 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
208 "failed to retrieve list of mechanisms: %s (%s)",
209 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
211 debug_printf("GNU SASL supports: %s\n", once);
214 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
215 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
216 "GNU SASL does not support mechanism \"%s\"",
217 ablock->name, ob->server_mech);
219 if (ablock->server_condition)
220 ablock->server = TRUE;
221 else if( ob->server_mech
222 && !STREQIC(ob->server_mech, US"EXTERNAL")
223 && !STREQIC(ob->server_mech, US"ANONYMOUS")
224 && !STREQIC(ob->server_mech, US"PLAIN")
225 && !STREQIC(ob->server_mech, US"LOGIN")
228 /* At present, for mechanisms we don't panic on absence of server_condition;
229 need to figure out the most generically correct approach to deciding when
230 it's critical and when it isn't. Eg, for simple validation (PLAIN mechanism,
231 etc) it clearly is critical.
234 ablock->server = FALSE;
235 HDEBUG(D_auth) debug_printf("%s authenticator: "
236 "Need server_condition for %s mechanism\n",
237 ablock->name, ob->server_mech);
240 /* This does *not* scale to new SASL mechanisms. Need a better way to ask
241 which properties will be needed. */
243 if ( !ob->server_realm
244 && STREQIC(ob->server_mech, US"DIGEST-MD5"))
246 ablock->server = FALSE;
247 HDEBUG(D_auth) debug_printf("%s authenticator: "
248 "Need server_realm for %s mechanism\n",
249 ablock->name, ob->server_mech);
252 ablock->client = ob->client_username && ob->client_password;
256 /* GNU SASL uses one top-level callback, registered at library level.
257 We dispatch to client and server functions instead. */
260 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
263 struct callback_exim_state *cb_state =
264 (struct callback_exim_state *)gsasl_session_hook_get(sctx);
268 HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
269 #ifdef CHANNELBIND_HACK
270 if (prop == GSASL_CB_TLS_UNIQUE)
273 if ((s = gsasl_callback_hook_get(ctx))) /* Gross hack for early lib vers */
275 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
276 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
280 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE! dummy for now\n");
281 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
286 return GSASL_NO_CALLBACK;
290 debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
291 prop, callback_loop);
293 if (callback_loop > 0)
295 /* Most likely is that we were asked for property FOO, and to
296 expand the string we asked for property BAR to put into an auth
297 variable, but property BAR is not supplied for this mechanism. */
299 debug_printf("Loop, asked for property %d while handling property %d\n",
300 prop, callback_loop);
301 return GSASL_NO_CALLBACK;
303 callback_loop = prop;
305 if (cb_state->currently == CURRENTLY_CLIENT)
306 rc = client_callback(ctx, sctx, prop, cb_state->ablock);
307 else if (cb_state->currently == CURRENTLY_SERVER)
308 rc = server_callback(ctx, sctx, prop, cb_state->ablock);
310 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
311 "unhandled callback state, bug in Exim", cb_state->ablock->name);
319 /*************************************************
320 * Debug service function *
321 *************************************************/
322 static const uschar *
323 gsasl_prop_code_to_name(Gsasl_property prop)
327 case GSASL_AUTHID: return US"AUTHID";
328 case GSASL_AUTHZID: return US"AUTHZID";
329 case GSASL_PASSWORD: return US"PASSWORD";
330 case GSASL_ANONYMOUS_TOKEN: return US"ANONYMOUS_TOKEN";
331 case GSASL_SERVICE: return US"SERVICE";
332 case GSASL_HOSTNAME: return US"HOSTNAME";
333 case GSASL_GSSAPI_DISPLAY_NAME: return US"GSSAPI_DISPLAY_NAME";
334 case GSASL_PASSCODE: return US"PASSCODE";
335 case GSASL_SUGGESTED_PIN: return US"SUGGESTED_PIN";
336 case GSASL_PIN: return US"PIN";
337 case GSASL_REALM: return US"REALM";
338 case GSASL_DIGEST_MD5_HASHED_PASSWORD: return US"DIGEST_MD5_HASHED_PASSWORD";
339 case GSASL_QOPS: return US"QOPS";
340 case GSASL_QOP: return US"QOP";
341 case GSASL_SCRAM_ITER: return US"SCRAM_ITER";
342 case GSASL_SCRAM_SALT: return US"SCRAM_SALT";
343 case GSASL_SCRAM_SALTED_PASSWORD: return US"SCRAM_SALTED_PASSWORD";
344 #ifdef EXIM_GSASL_SCRAM_S_KEY
345 case GSASL_SCRAM_STOREDKEY: return US"SCRAM_STOREDKEY";
346 case GSASL_SCRAM_SERVERKEY: return US"SCRAM_SERVERKEY";
348 #ifdef EXIM_GSASL_HAVE_EXPORTER /* v. 2.1.0 */
349 case GSASL_CB_TLS_EXPORTER: return US"CB_TLS_EXPORTER";
351 case GSASL_CB_TLS_UNIQUE: return US"CB_TLS_UNIQUE";
352 case GSASL_SAML20_IDP_IDENTIFIER: return US"SAML20_IDP_IDENTIFIER";
353 case GSASL_SAML20_REDIRECT_URL: return US"SAML20_REDIRECT_URL";
354 case GSASL_OPENID20_REDIRECT_URL: return US"OPENID20_REDIRECT_URL";
355 case GSASL_OPENID20_OUTCOME_DATA: return US"OPENID20_OUTCOME_DATA";
356 case GSASL_SAML20_AUTHENTICATE_IN_BROWSER: return US"SAML20_AUTHENTICATE_IN_BROWSER";
357 case GSASL_OPENID20_AUTHENTICATE_IN_BROWSER: return US"OPENID20_AUTHENTICATE_IN_BROWSER";
358 case GSASL_VALIDATE_SIMPLE: return US"VALIDATE_SIMPLE";
359 case GSASL_VALIDATE_EXTERNAL: return US"VALIDATE_EXTERNAL";
360 case GSASL_VALIDATE_ANONYMOUS: return US"VALIDATE_ANONYMOUS";
361 case GSASL_VALIDATE_GSSAPI: return US"VALIDATE_GSSAPI";
362 case GSASL_VALIDATE_SECURID: return US"VALIDATE_SECURID";
363 case GSASL_VALIDATE_SAML20: return US"VALIDATE_SAML20";
364 case GSASL_VALIDATE_OPENID20: return US"VALIDATE_OPENID20";
366 return CUS string_sprintf("(unknown prop: %d)", (int)prop);
370 preload_prop(Gsasl_session * sctx, Gsasl_property propcode, const uschar * val)
372 DEBUG(D_auth) debug_printf("preloading prop %s val %s\n",
373 gsasl_prop_code_to_name(propcode), val);
374 gsasl_property_set(sctx, propcode, CCS val);
377 /*************************************************
378 * Server entry point *
379 *************************************************/
381 /* For interface, see auths/README */
384 auth_gsasl_server(auth_instance * ablock, uschar * initial_data)
387 char * to_send, * received;
388 Gsasl_session * sctx = NULL;
389 auth_gsasl_options_block * ob =
390 (auth_gsasl_options_block *)(ablock->options_block);
391 struct callback_exim_state cb_state;
392 int rc, auth_result, exim_error, exim_error_override;
395 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
396 ablock->name, ob->server_mech);
399 if (tls_in.channelbinding && ob->server_channelbinding)
401 # ifndef DISABLE_TLS_RESUME
402 if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
403 { /* per RFC 7677 section 4 */
404 HDEBUG(D_auth) debug_printf(
405 "channel binding not usable on resumed TLS without extended-master-secret");
409 # ifdef CHANNELBIND_HACK
410 /* This is a gross hack to get around the library before 1.9.2
411 a) requiring that c-b was already set, at the _start() call, and
412 b) caching a b64'd version of the binding then which it never updates. */
414 gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
419 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
421 auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
422 gsasl_strerror_name(rc), gsasl_strerror(rc));
423 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
426 /* Hereafter: gsasl_finish(sctx) please */
428 cb_state.ablock = ablock;
429 cb_state.currently = CURRENTLY_SERVER;
430 gsasl_session_hook_set(sctx, &cb_state);
432 tmps = expand_string(ob->server_service);
433 preload_prop(sctx, GSASL_SERVICE, tmps);
434 tmps = expand_string(ob->server_hostname);
435 preload_prop(sctx, GSASL_HOSTNAME, tmps);
436 if (ob->server_realm)
438 tmps = expand_string(ob->server_realm);
440 preload_prop(sctx, GSASL_REALM, tmps);
442 /* We don't support protection layers. */
443 preload_prop(sctx, GSASL_QOPS, US "qop-auth");
446 if (tls_in.channelbinding)
448 /* Some auth mechanisms can ensure that both sides are talking withing the
449 same security context; for TLS, this means that even if a bad certificate
450 has been accepted, they remain MitM-proof because both sides must be within
451 the same negotiated session; if someone is terminating one session and
452 proxying data on within a second, authentication will fail.
454 We might not have this available, depending upon TLS implementation,
455 ciphersuite, phase of moon ...
457 If we do, it results in extra SASL mechanisms being available; here,
458 Exim's one-mechanism-per-authenticator potentially causes problems.
459 It depends upon how GNU SASL will implement the PLUS variants of GS2
460 and whether it automatically mandates a switch to the bound PLUS
461 if the data is available. Since default-on, despite being more secure,
462 would then result in mechanism name changes on a library update, we
463 have little choice but to default it off and let the admin choose to
466 Earlier library versions need this set early, during the _start() call,
467 so we had to misuse gsasl_callback_hook_set/get() as a data transfer
468 mech for the callback done at that time to get the bind-data. More recently
469 the callback is done (if needed) during the first gsasl_stop(). We know
470 the bind-data here so can set it (and should not get a callback).
472 if (ob->server_channelbinding)
474 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
476 # ifndef CHANNELBIND_HACK
478 # ifdef EXIM_GSASL_HAVE_EXPORTER
479 tls_in.channelbind_exporter ? GSASL_CB_TLS_EXPORTER :
482 tls_in.channelbinding);
487 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
492 debug_printf("Auth %s: no channel-binding data available\n",
496 checked_server_condition = FALSE;
498 received = CS initial_data;
500 exim_error = exim_error_override = OK;
503 switch (rc = gsasl_step64(sctx, received, &to_send))
507 goto STOP_INTERACTION;
510 case GSASL_NEEDS_MORE:
513 case GSASL_AUTHENTICATION_ERROR:
514 case GSASL_INTEGRITY_ERROR:
515 case GSASL_NO_AUTHID:
516 case GSASL_NO_ANONYMOUS_TOKEN:
517 case GSASL_NO_AUTHZID:
518 case GSASL_NO_PASSWORD:
519 case GSASL_NO_PASSCODE:
521 case GSASL_BASE64_ERROR:
522 HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
523 gsasl_strerror_name(rc), gsasl_strerror(rc));
524 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
525 "GNU SASL permanent failure: %s (%s)",
526 ablock->name, ob->server_mech,
527 gsasl_strerror_name(rc), gsasl_strerror(rc));
528 if (rc == GSASL_BASE64_ERROR)
529 exim_error_override = BAD64;
530 goto STOP_INTERACTION;
533 auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
534 gsasl_strerror_name(rc), gsasl_strerror(rc));
535 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
536 exim_error_override = DEFER;
537 goto STOP_INTERACTION;
540 /*XXX having our caller send the final smtp "235" is unfortunate; wastes a roundtrip */
541 if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
542 exim_error = auth_get_no64_data(USS &received, US to_send);
551 break; /* handles * cancelled check */
553 } while (rc == GSASL_NEEDS_MORE);
561 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_ITER)))
562 debug_printf(" - itercnt: '%s'\n", s);
563 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALT)))
564 debug_printf(" - salt: '%s'\n", s);
565 #ifdef EXIM_GSASL_SCRAM_S_KEY
566 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SERVERKEY)))
567 debug_printf(" - ServerKey: '%s'\n", s);
568 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_STOREDKEY)))
569 debug_printf(" - StoredKey: '%s'\n", s);
575 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
577 if (exim_error != OK)
580 if (auth_result != GSASL_OK)
582 HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
583 gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
584 if (exim_error_override != OK)
585 return exim_error_override; /* might be DEFER */
586 if (sasl_error_should_defer) /* overriding auth failure SASL error */
591 /* Auth succeeded, check server_condition unless already done in callback */
592 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
596 /* returns the GSASL status of expanding the Exim string given */
598 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
600 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
603 case OK: return GSASL_OK;
604 case DEFER: sasl_error_should_defer = TRUE;
605 return GSASL_AUTHENTICATION_ERROR;
606 case FAIL: return GSASL_AUTHENTICATION_ERROR;
607 default: log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
608 "Unhandled return from checking %s: %d",
609 ablock->name, label, exim_rc);
613 return GSASL_AUTHENTICATION_ERROR;
617 /* Set the "next" $auth[n] and increment expand_nmax */
620 set_exim_authvar_from_prop(Gsasl_session * sctx, Gsasl_property prop)
622 uschar * propval = US gsasl_property_fast(sctx, prop);
623 int i = expand_nmax, j = i + 1;
624 propval = propval ? string_copy(propval) : US"";
625 HDEBUG(D_auth) debug_printf("auth[%d] <= %s'%s'\n",
626 j, gsasl_prop_code_to_name(prop), propval);
627 expand_nstring[j] = propval;
628 expand_nlength[j] = Ustrlen(propval);
629 if (i < AUTH_VARS) auth_vars[i] = propval;
634 set_exim_authvars_from_a_az_r_props(Gsasl_session * sctx)
636 if (expand_nmax > 0 ) return;
638 /* Asking for GSASL_AUTHZID calls back into us if we use
639 gsasl_property_get(), thus the use of gsasl_property_fast().
640 Do we really want to hardcode limits per mechanism? What happens when
641 a new mechanism is added to the library. It *shouldn't* result in us
642 needing to add more glue, since avoiding that is a large part of the
645 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
646 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
647 set_exim_authvar_from_prop(sctx, GSASL_REALM);
652 prop_from_option(Gsasl_session * sctx, Gsasl_property prop,
653 const uschar * option)
655 HDEBUG(D_auth) debug_printf(" %s\n", gsasl_prop_code_to_name(prop));
658 set_exim_authvars_from_a_az_r_props(sctx);
659 option = expand_cstring(option);
660 HDEBUG(D_auth) debug_printf(" '%s'\n", option);
662 gsasl_property_set(sctx, prop, CCS option);
665 HDEBUG(D_auth) debug_printf(" option not set\n");
666 return GSASL_NO_CALLBACK;
670 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
671 auth_instance *ablock)
675 int cbrc = GSASL_NO_CALLBACK;
676 auth_gsasl_options_block * ob =
677 (auth_gsasl_options_block *)(ablock->options_block);
679 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as server\n",
680 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
682 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
687 case GSASL_VALIDATE_SIMPLE:
688 /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
689 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
690 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
691 set_exim_authvar_from_prop(sctx, GSASL_PASSWORD);
693 cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
694 checked_server_condition = TRUE;
697 case GSASL_VALIDATE_EXTERNAL:
698 if (!ablock->server_condition)
700 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
701 cbrc = GSASL_AUTHENTICATION_ERROR;
704 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
706 cbrc = condition_check(ablock,
707 US"server_condition (EXTERNAL)", ablock->server_condition);
708 checked_server_condition = TRUE;
711 case GSASL_VALIDATE_ANONYMOUS:
712 if (!ablock->server_condition)
714 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
715 cbrc = GSASL_AUTHENTICATION_ERROR;
718 set_exim_authvar_from_prop(sctx, GSASL_ANONYMOUS_TOKEN);
720 cbrc = condition_check(ablock,
721 US"server_condition (ANONYMOUS)", ablock->server_condition);
722 checked_server_condition = TRUE;
725 case GSASL_VALIDATE_GSSAPI:
726 /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
727 The display-name is authenticated as part of GSS, the authzid is claimed
728 by the SASL integration after authentication; protected against tampering
729 (if the SASL mechanism supports that, which Kerberos does) but is
730 unverified, same as normal for other mechanisms.
731 First coding, we had these values swapped, but for consistency and prior
732 to the first release of Exim with this authenticator, they've been
733 switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
735 set_exim_authvar_from_prop(sctx, GSASL_GSSAPI_DISPLAY_NAME);
736 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
738 /* In this one case, it perhaps makes sense to default back open?
739 But for consistency, let's just mandate server_condition here too. */
741 cbrc = condition_check(ablock,
742 US"server_condition (GSSAPI family)", ablock->server_condition);
743 checked_server_condition = TRUE;
746 case GSASL_SCRAM_ITER:
747 cbrc = prop_from_option(sctx, prop, ob->server_scram_iter);
750 case GSASL_SCRAM_SALT:
751 cbrc = prop_from_option(sctx, prop, ob->server_scram_salt);
754 #ifdef EXIM_GSASL_SCRAM_S_KEY
755 case GSASL_SCRAM_STOREDKEY:
756 cbrc = prop_from_option(sctx, prop, ob->server_s_key);
759 case GSASL_SCRAM_SERVERKEY:
760 cbrc = prop_from_option(sctx, prop, ob->server_key);
765 /* SCRAM-*: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
766 DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
767 CRAM-MD5: GSASL_AUTHID
768 PLAIN: GSASL_AUTHID and GSASL_AUTHZID
771 set_exim_authvars_from_a_az_r_props(sctx);
773 if (!(s = ob->server_password))
775 HDEBUG(D_auth) debug_printf("option not set\n");
778 if (!(tmps = CS expand_string(s)))
780 sasl_error_should_defer = !f.expand_string_forcedfail;
781 HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
782 "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
783 return GSASL_AUTHENTICATION_ERROR;
785 HDEBUG(D_auth) debug_printf(" set\n");
786 gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
788 /* This is inadequate; don't think Exim's store stacks are geared
789 for memory wiping, so expanding strings will leave stuff laying around.
790 But no need to compound the problem, so get rid of the one we can. */
792 if (US tmps != s) memset(tmps, '\0', strlen(tmps));
797 HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
798 cbrc = GSASL_NO_CALLBACK;
801 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
802 gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
808 /******************************************************************************/
810 #define PROP_OPTIONAL BIT(0)
813 set_client_prop(Gsasl_session * sctx, Gsasl_property prop, uschar * val,
814 unsigned flags, uschar * buffer, int buffsize)
818 if (!val) return !!(flags & PROP_OPTIONAL);
819 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
821 string_format(buffer, buffsize, "%s", expand_string_message);
826 HDEBUG(D_auth) debug_printf("%s: set %s = '%s'\n", __FUNCTION__,
827 gsasl_prop_code_to_name(prop), s);
828 gsasl_property_set(sctx, prop, CS s);
834 /*************************************************
835 * Client entry point *
836 *************************************************/
838 /* For interface, see auths/README */
842 auth_instance * ablock, /* authenticator block */
843 void * sx, /* connection */
844 int timeout, /* command timeout */
845 uschar * buffer, /* buffer for reading response */
846 int buffsize) /* size of buffer */
848 auth_gsasl_options_block * ob =
849 (auth_gsasl_options_block *)(ablock->options_block);
850 Gsasl_session * sctx = NULL;
851 struct callback_exim_state cb_state;
854 int rc, yield = FAIL;
857 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
858 ablock->name, ob->server_mech);
863 if (tls_out.channelbinding && ob->client_channelbinding)
865 # ifndef DISABLE_TLS_RESUME
866 if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
867 { /* Per RFC 7677 section 4. See also RFC 7627, "Triple Handshake"
868 vulnerability, and https://www.mitls.org/pages/attacks/3SHAKE */
869 string_format(buffer, buffsize, "%s",
870 "channel binding not usable on resumed TLS without extended-master-secret");
874 # ifdef CHANNELBIND_HACK
875 /* This is a gross hack to get around the library before 1.9.2
876 a) requiring that c-b was already set, at the _start() call, and
877 b) caching a b64'd version of the binding then which it never updates. */
879 gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
884 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
886 string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
887 gsasl_strerror_name(rc), gsasl_strerror(rc));
888 HDEBUG(D_auth) debug_printf("%s\n", buffer);
892 cb_state.ablock = ablock;
893 cb_state.currently = CURRENTLY_CLIENT;
894 gsasl_session_hook_set(sctx, &cb_state);
898 if ( !set_client_prop(sctx, GSASL_PASSWORD, ob->client_password,
900 || !set_client_prop(sctx, GSASL_AUTHID, ob->client_username,
902 || !set_client_prop(sctx, GSASL_AUTHZID, ob->client_authz,
903 PROP_OPTIONAL, buffer, buffsize)
908 if (tls_out.channelbinding)
909 if (ob->client_channelbinding)
911 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
913 # ifndef CHANNELBIND_HACK
915 # ifdef EXIM_GSASL_HAVE_EXPORTER
916 tls_out.channelbind_exporter ? GSASL_CB_TLS_EXPORTER :
919 tls_out.channelbinding);
924 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
928 /* Run the SASL conversation with the server */
935 rc = gsasl_step64(sctx, CS s, CSS &outstr);
937 if (rc == GSASL_NEEDS_MORE || rc == GSASL_OK)
940 ? smtp_write_command(sx, SCMD_FLUSH,
941 outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
942 ablock->public_name, outstr) <= 0
944 ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
955 if (rc != GSASL_NEEDS_MORE)
959 string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
963 /* expecting a final 2xx from the server, accepting the AUTH */
965 if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
967 break; /* from SASL sequence loop */
970 /* 2xx or 3xx response is acceptable. If 2xx, no further input */
972 if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
973 if (errno == 0 && buffer[0] == '2')
987 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
988 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
989 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
990 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD);
998 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
1000 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n",
1001 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
1004 #ifdef EXIM_GSASL_HAVE_EXPORTER
1005 case GSASL_CB_TLS_EXPORTER: /* Should never get called for this, as pre-set */
1006 if (!tls_out.channelbind_exporter) break;
1007 HDEBUG(D_auth) debug_printf(" filling in\n");
1008 gsasl_property_set(sctx, GSASL_CB_TLS_EXPORTER, CCS tls_out.channelbinding);
1011 case GSASL_CB_TLS_UNIQUE: /* Should never get called for this, as pre-set */
1012 #ifdef EXIM_GSASL_HAVE_EXPORTER
1013 if (tls_out.channelbind_exporter) break;
1015 HDEBUG(D_auth) debug_printf(" filling in\n");
1016 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
1018 case GSASL_SCRAM_SALTED_PASSWORD:
1020 uschar * client_spassword =
1021 ((auth_gsasl_options_block *) ablock->options_block)->client_spassword;
1023 HDEBUG(D_auth) if (!client_spassword)
1024 debug_printf(" client_spassword option unset\n");
1025 if (client_spassword)
1028 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
1029 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
1030 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
1031 set_client_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD, client_spassword,
1032 0, dummy, sizeof(dummy));
1033 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
1040 debug_printf(" not providing one\n");
1043 return GSASL_NO_CALLBACK;
1046 /*************************************************
1048 *************************************************/
1051 auth_gsasl_version_report(gstring * g)
1053 return string_fmt_append(g, "Library version: GNU SASL: Compile: %s\n"
1055 GSASL_VERSION, gsasl_check_version(NULL));
1061 void auth_gsasl_macros(void) {}
1063 #endif /*!MACRO_PREDEF*/
1064 #endif /* AUTH_GSASL */
1066 /* End of gsasl_exim.c */