1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2019-2020 */
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
61 /* Authenticator-specific options. */
62 /* I did have server_*_condition options for various mechanisms, but since
63 we only ever handle one mechanism at a time, I didn't see the point in keeping
64 that. In case someone sees a point, I've left the condition_check() API
66 #define LOFF(field) OPT_OFF(auth_gsasl_options_block, field)
68 optionlist auth_gsasl_options[] = {
69 { "client_authz", opt_stringptr, LOFF(client_authz) },
70 { "client_channelbinding", opt_bool, LOFF(client_channelbinding) },
71 { "client_password", opt_stringptr, LOFF(client_password) },
72 { "client_spassword", opt_stringptr, LOFF(client_spassword) },
73 { "client_username", opt_stringptr, LOFF(client_username) },
75 { "server_channelbinding", opt_bool, LOFF(server_channelbinding) },
76 { "server_hostname", opt_stringptr, LOFF(server_hostname) },
77 #ifdef EXIM_GSASL_SCRAM_S_KEY
78 { "server_key", opt_stringptr, LOFF(server_key) },
80 { "server_mech", opt_stringptr, LOFF(server_mech) },
81 { "server_password", opt_stringptr, LOFF(server_password) },
82 { "server_realm", opt_stringptr, LOFF(server_realm) },
83 { "server_scram_iter", opt_stringptr, LOFF(server_scram_iter) },
84 { "server_scram_salt", opt_stringptr, LOFF(server_scram_salt) },
85 #ifdef EXIM_GSASL_SCRAM_S_KEY
86 { "server_skey", opt_stringptr, LOFF(server_s_key) },
88 { "server_service", opt_stringptr, LOFF(server_service) }
91 int auth_gsasl_options_count =
92 sizeof(auth_gsasl_options)/sizeof(optionlist);
94 /* Defaults for the authenticator-specific options. */
95 auth_gsasl_options_block auth_gsasl_option_defaults = {
96 .server_service = US"smtp",
97 .server_hostname = US"$primary_hostname",
98 .server_scram_iter = US"4096",
99 /* all others zero/null */
104 # include "../macro_predef.h"
107 void auth_gsasl_init(auth_instance *ablock) {}
108 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
109 int auth_gsasl_client(auth_instance *ablock, void * sx,
110 int timeout, uschar *buffer, int buffsize) {return 0;}
111 void auth_gsasl_version_report(FILE *f) {}
114 auth_gsasl_macros(void)
116 # ifdef EXIM_GSASL_HAVE_SCRAM_SHA_256
117 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_SHA_256");
119 # ifdef EXIM_GSASL_SCRAM_S_KEY
120 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_S_KEY");
124 #else /*!MACRO_PREDEF*/
128 /* "Globals" for managing the gsasl interface. */
130 static Gsasl *gsasl_ctx = NULL;
132 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
134 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
136 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
138 static BOOL sasl_error_should_defer = FALSE;
139 static Gsasl_property callback_loop = 0;
140 static BOOL checked_server_condition = FALSE;
142 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
144 struct callback_exim_state {
145 auth_instance *ablock;
150 /*************************************************
151 * Initialization entry point *
152 *************************************************/
154 /* Called for each instance, after its options have been read, to
155 enable consistency checks to be done, or anything else that needs
159 auth_gsasl_init(auth_instance *ablock)
161 static char * once = NULL;
163 auth_gsasl_options_block *ob =
164 (auth_gsasl_options_block *)(ablock->options_block);
166 /* As per existing Cyrus glue, use the authenticator's public name as
167 the default for the mechanism name; we don't handle multiple mechanisms
168 in one authenticator, but the same driver can be used multiple times. */
170 if (!ob->server_mech)
171 ob->server_mech = string_copy(ablock->public_name);
173 /* Can get multiple session contexts from one library context, so just
174 initialise the once. */
178 if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
179 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
180 "couldn't initialise GNU SASL library: %s (%s)",
181 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
183 gsasl_callback_set(gsasl_ctx, main_callback);
186 /* We don't need this except to log it for debugging. */
188 HDEBUG(D_auth) if (!once)
190 if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
191 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
192 "failed to retrieve list of mechanisms: %s (%s)",
193 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
195 debug_printf("GNU SASL supports: %s\n", once);
198 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
199 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
200 "GNU SASL does not support mechanism \"%s\"",
201 ablock->name, ob->server_mech);
203 ablock->server = TRUE;
205 if ( !ablock->server_condition
206 && ( streqic(ob->server_mech, US"EXTERNAL")
207 || streqic(ob->server_mech, US"ANONYMOUS")
208 || streqic(ob->server_mech, US"PLAIN")
209 || streqic(ob->server_mech, US"LOGIN")
212 ablock->server = FALSE;
213 HDEBUG(D_auth) debug_printf("%s authenticator: "
214 "Need server_condition for %s mechanism\n",
215 ablock->name, ob->server_mech);
218 /* This does *not* scale to new SASL mechanisms. Need a better way to ask
219 which properties will be needed. */
221 if ( !ob->server_realm
222 && streqic(ob->server_mech, US"DIGEST-MD5"))
224 ablock->server = FALSE;
225 HDEBUG(D_auth) debug_printf("%s authenticator: "
226 "Need server_realm for %s mechanism\n",
227 ablock->name, ob->server_mech);
230 /* At present, for mechanisms we don't panic on absence of server_condition;
231 need to figure out the most generically correct approach to deciding when
232 it's critical and when it isn't. Eg, for simple validation (PLAIN mechanism,
233 etc) it clearly is critical.
236 ablock->client = ob->client_username && ob->client_password;
240 /* GNU SASL uses one top-level callback, registered at library level.
241 We dispatch to client and server functions instead. */
244 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
247 struct callback_exim_state *cb_state =
248 (struct callback_exim_state *)gsasl_session_hook_get(sctx);
252 HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
253 #ifdef CHANNELBIND_HACK
254 if (prop == GSASL_CB_TLS_UNIQUE)
257 if ((s = gsasl_callback_hook_get(ctx)))
259 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
260 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
264 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE! dummy for now\n");
265 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
270 return GSASL_NO_CALLBACK;
274 debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
275 prop, callback_loop);
277 if (callback_loop > 0)
279 /* Most likely is that we were asked for property FOO, and to
280 expand the string we asked for property BAR to put into an auth
281 variable, but property BAR is not supplied for this mechanism. */
283 debug_printf("Loop, asked for property %d while handling property %d\n",
284 prop, callback_loop);
285 return GSASL_NO_CALLBACK;
287 callback_loop = prop;
289 if (cb_state->currently == CURRENTLY_CLIENT)
290 rc = client_callback(ctx, sctx, prop, cb_state->ablock);
291 else if (cb_state->currently == CURRENTLY_SERVER)
292 rc = server_callback(ctx, sctx, prop, cb_state->ablock);
294 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
295 "unhandled callback state, bug in Exim", cb_state->ablock->name);
303 /*************************************************
304 * Debug service function *
305 *************************************************/
306 static const uschar *
307 gsasl_prop_code_to_name(Gsasl_property prop)
311 case GSASL_AUTHID: return US"AUTHID";
312 case GSASL_AUTHZID: return US"AUTHZID";
313 case GSASL_PASSWORD: return US"PASSWORD";
314 case GSASL_ANONYMOUS_TOKEN: return US"ANONYMOUS_TOKEN";
315 case GSASL_SERVICE: return US"SERVICE";
316 case GSASL_HOSTNAME: return US"HOSTNAME";
317 case GSASL_GSSAPI_DISPLAY_NAME: return US"GSSAPI_DISPLAY_NAME";
318 case GSASL_PASSCODE: return US"PASSCODE";
319 case GSASL_SUGGESTED_PIN: return US"SUGGESTED_PIN";
320 case GSASL_PIN: return US"PIN";
321 case GSASL_REALM: return US"REALM";
322 case GSASL_DIGEST_MD5_HASHED_PASSWORD: return US"DIGEST_MD5_HASHED_PASSWORD";
323 case GSASL_QOPS: return US"QOPS";
324 case GSASL_QOP: return US"QOP";
325 case GSASL_SCRAM_ITER: return US"SCRAM_ITER";
326 case GSASL_SCRAM_SALT: return US"SCRAM_SALT";
327 case GSASL_SCRAM_SALTED_PASSWORD: return US"SCRAM_SALTED_PASSWORD";
328 #ifdef EXIM_GSASL_SCRAM_S_KEY
329 case GSASL_SCRAM_STOREDKEY: return US"SCRAM_STOREDKEY";
330 case GSASL_SCRAM_SERVERKEY: return US"SCRAM_SERVERKEY";
332 case GSASL_CB_TLS_UNIQUE: return US"CB_TLS_UNIQUE";
333 case GSASL_SAML20_IDP_IDENTIFIER: return US"SAML20_IDP_IDENTIFIER";
334 case GSASL_SAML20_REDIRECT_URL: return US"SAML20_REDIRECT_URL";
335 case GSASL_OPENID20_REDIRECT_URL: return US"OPENID20_REDIRECT_URL";
336 case GSASL_OPENID20_OUTCOME_DATA: return US"OPENID20_OUTCOME_DATA";
337 case GSASL_SAML20_AUTHENTICATE_IN_BROWSER: return US"SAML20_AUTHENTICATE_IN_BROWSER";
338 case GSASL_OPENID20_AUTHENTICATE_IN_BROWSER: return US"OPENID20_AUTHENTICATE_IN_BROWSER";
339 case GSASL_VALIDATE_SIMPLE: return US"VALIDATE_SIMPLE";
340 case GSASL_VALIDATE_EXTERNAL: return US"VALIDATE_EXTERNAL";
341 case GSASL_VALIDATE_ANONYMOUS: return US"VALIDATE_ANONYMOUS";
342 case GSASL_VALIDATE_GSSAPI: return US"VALIDATE_GSSAPI";
343 case GSASL_VALIDATE_SECURID: return US"VALIDATE_SECURID";
344 case GSASL_VALIDATE_SAML20: return US"VALIDATE_SAML20";
345 case GSASL_VALIDATE_OPENID20: return US"VALIDATE_OPENID20";
347 return CUS string_sprintf("(unknown prop: %d)", (int)prop);
350 /*************************************************
351 * Server entry point *
352 *************************************************/
354 /* For interface, see auths/README */
357 auth_gsasl_server(auth_instance *ablock, uschar *initial_data)
360 char *to_send, *received;
361 Gsasl_session *sctx = NULL;
362 auth_gsasl_options_block *ob =
363 (auth_gsasl_options_block *)(ablock->options_block);
364 struct callback_exim_state cb_state;
365 int rc, auth_result, exim_error, exim_error_override;
368 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
369 ablock->name, ob->server_mech);
372 if (tls_in.channelbinding && ob->server_channelbinding)
374 # ifndef DISABLE_TLS_RESUME
375 if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
376 { /* per RFC 7677 section 4 */
377 HDEBUG(D_auth) debug_printf(
378 "channel binding not usable on resumed TLS without extended-master-secret");
382 # ifdef CHANNELBIND_HACK
383 /* This is a gross hack to get around the library before 1.9.2
384 a) requiring that c-b was already set, at the _start() call, and
385 b) caching a b64'd version of the binding then which it never updates. */
387 gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
392 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
394 auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
395 gsasl_strerror_name(rc), gsasl_strerror(rc));
396 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
399 /* Hereafter: gsasl_finish(sctx) please */
401 cb_state.ablock = ablock;
402 cb_state.currently = CURRENTLY_SERVER;
403 gsasl_session_hook_set(sctx, &cb_state);
405 tmps = CS expand_string(ob->server_service);
406 gsasl_property_set(sctx, GSASL_SERVICE, tmps);
407 tmps = CS expand_string(ob->server_hostname);
408 gsasl_property_set(sctx, GSASL_HOSTNAME, tmps);
409 if (ob->server_realm)
411 tmps = CS expand_string(ob->server_realm);
413 gsasl_property_set(sctx, GSASL_REALM, tmps);
415 /* We don't support protection layers. */
416 gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
419 if (tls_in.channelbinding)
421 /* Some auth mechanisms can ensure that both sides are talking withing the
422 same security context; for TLS, this means that even if a bad certificate
423 has been accepted, they remain MitM-proof because both sides must be within
424 the same negotiated session; if someone is terminating one session and
425 proxying data on within a second, authentication will fail.
427 We might not have this available, depending upon TLS implementation,
428 ciphersuite, phase of moon ...
430 If we do, it results in extra SASL mechanisms being available; here,
431 Exim's one-mechanism-per-authenticator potentially causes problems.
432 It depends upon how GNU SASL will implement the PLUS variants of GS2
433 and whether it automatically mandates a switch to the bound PLUS
434 if the data is available. Since default-on, despite being more secure,
435 would then result in mechanism name changes on a library update, we
436 have little choice but to default it off and let the admin choose to
439 Earlier library versions need this set early, during the _start() call,
440 so we had to misuse gsasl_callback_hook_set/get() as a data transfer
441 mech for the callback done at that time to get the bind-data. More recently
442 the callback is done (if needed) during the first gsasl_stop(). We know
443 the bind-data here so can set it (and should not get a callback).
445 if (ob->server_channelbinding)
447 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
449 # ifndef CHANNELBIND_HACK
450 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding);
455 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
460 debug_printf("Auth %s: no channel-binding data available\n",
464 checked_server_condition = FALSE;
466 received = CS initial_data;
468 exim_error = exim_error_override = OK;
471 switch (rc = gsasl_step64(sctx, received, &to_send))
475 goto STOP_INTERACTION;
478 case GSASL_NEEDS_MORE:
481 case GSASL_AUTHENTICATION_ERROR:
482 case GSASL_INTEGRITY_ERROR:
483 case GSASL_NO_AUTHID:
484 case GSASL_NO_ANONYMOUS_TOKEN:
485 case GSASL_NO_AUTHZID:
486 case GSASL_NO_PASSWORD:
487 case GSASL_NO_PASSCODE:
489 case GSASL_BASE64_ERROR:
490 HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
491 gsasl_strerror_name(rc), gsasl_strerror(rc));
492 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
493 "GNU SASL permanent failure: %s (%s)",
494 ablock->name, ob->server_mech,
495 gsasl_strerror_name(rc), gsasl_strerror(rc));
496 if (rc == GSASL_BASE64_ERROR)
497 exim_error_override = BAD64;
498 goto STOP_INTERACTION;
501 auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
502 gsasl_strerror_name(rc), gsasl_strerror(rc));
503 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
504 exim_error_override = DEFER;
505 goto STOP_INTERACTION;
508 /*XXX having our caller send the final smtp "235" is unfortunate; wastes a roundtrip */
509 if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
510 exim_error = auth_get_no64_data(USS &received, US to_send);
519 break; /* handles * cancelled check */
521 } while (rc == GSASL_NEEDS_MORE);
529 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_ITER)))
530 debug_printf(" - itercnt: '%s'\n", s);
531 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALT)))
532 debug_printf(" - salt: '%s'\n", s);
533 #ifdef EXIM_GSASL_SCRAM_S_KEY
534 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SERVERKEY)))
535 debug_printf(" - ServerKey: '%s'\n", s);
536 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_STOREDKEY)))
537 debug_printf(" - StoredKey: '%s'\n", s);
543 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
545 if (exim_error != OK)
548 if (auth_result != GSASL_OK)
550 HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
551 gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
552 if (exim_error_override != OK)
553 return exim_error_override; /* might be DEFER */
554 if (sasl_error_should_defer) /* overriding auth failure SASL error */
559 /* Auth succeeded, check server_condition unless already done in callback */
560 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
564 /* returns the GSASL status of expanding the Exim string given */
566 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
568 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
571 case OK: return GSASL_OK;
572 case DEFER: sasl_error_should_defer = TRUE;
573 return GSASL_AUTHENTICATION_ERROR;
574 case FAIL: return GSASL_AUTHENTICATION_ERROR;
575 default: log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
576 "Unhandled return from checking %s: %d",
577 ablock->name, label, exim_rc);
581 return GSASL_AUTHENTICATION_ERROR;
585 /* Set the "next" $auth[n] and increment expand_nmax */
588 set_exim_authvar_from_prop(Gsasl_session * sctx, Gsasl_property prop)
590 uschar * propval = US gsasl_property_fast(sctx, prop);
591 int i = expand_nmax, j = i + 1;
592 propval = propval ? string_copy(propval) : US"";
593 HDEBUG(D_auth) debug_printf("auth[%d] <= %s'%s'\n",
594 j, gsasl_prop_code_to_name(prop), propval);
595 expand_nstring[j] = propval;
596 expand_nlength[j] = Ustrlen(propval);
597 if (i < AUTH_VARS) auth_vars[i] = propval;
602 set_exim_authvars_from_a_az_r_props(Gsasl_session * sctx)
604 if (expand_nmax > 0 ) return;
606 /* Asking for GSASL_AUTHZID calls back into us if we use
607 gsasl_property_get(), thus the use of gsasl_property_fast().
608 Do we really want to hardcode limits per mechanism? What happens when
609 a new mechanism is added to the library. It *shouldn't* result in us
610 needing to add more glue, since avoiding that is a large part of the
613 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
614 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
615 set_exim_authvar_from_prop(sctx, GSASL_REALM);
620 prop_from_option(Gsasl_session * sctx, Gsasl_property prop,
621 const uschar * option)
623 HDEBUG(D_auth) debug_printf(" %s\n", gsasl_prop_code_to_name(prop));
626 set_exim_authvars_from_a_az_r_props(sctx);
627 option = expand_cstring(option);
628 HDEBUG(D_auth) debug_printf(" '%s'\n", option);
630 gsasl_property_set(sctx, prop, CCS option);
633 HDEBUG(D_auth) debug_printf(" option not set\n");
634 return GSASL_NO_CALLBACK;
638 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
639 auth_instance *ablock)
643 int cbrc = GSASL_NO_CALLBACK;
644 auth_gsasl_options_block *ob =
645 (auth_gsasl_options_block *)(ablock->options_block);
647 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as server\n",
648 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
650 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
655 case GSASL_VALIDATE_SIMPLE:
656 /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
657 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
658 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
659 set_exim_authvar_from_prop(sctx, GSASL_PASSWORD);
661 cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
662 checked_server_condition = TRUE;
665 case GSASL_VALIDATE_EXTERNAL:
666 if (!ablock->server_condition)
668 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
669 cbrc = GSASL_AUTHENTICATION_ERROR;
672 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
674 cbrc = condition_check(ablock,
675 US"server_condition (EXTERNAL)", ablock->server_condition);
676 checked_server_condition = TRUE;
679 case GSASL_VALIDATE_ANONYMOUS:
680 if (!ablock->server_condition)
682 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
683 cbrc = GSASL_AUTHENTICATION_ERROR;
686 set_exim_authvar_from_prop(sctx, GSASL_ANONYMOUS_TOKEN);
688 cbrc = condition_check(ablock,
689 US"server_condition (ANONYMOUS)", ablock->server_condition);
690 checked_server_condition = TRUE;
693 case GSASL_VALIDATE_GSSAPI:
694 /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
695 The display-name is authenticated as part of GSS, the authzid is claimed
696 by the SASL integration after authentication; protected against tampering
697 (if the SASL mechanism supports that, which Kerberos does) but is
698 unverified, same as normal for other mechanisms.
699 First coding, we had these values swapped, but for consistency and prior
700 to the first release of Exim with this authenticator, they've been
701 switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
703 set_exim_authvar_from_prop(sctx, GSASL_GSSAPI_DISPLAY_NAME);
704 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
706 /* In this one case, it perhaps makes sense to default back open?
707 But for consistency, let's just mandate server_condition here too. */
709 cbrc = condition_check(ablock,
710 US"server_condition (GSSAPI family)", ablock->server_condition);
711 checked_server_condition = TRUE;
714 case GSASL_SCRAM_ITER:
715 cbrc = prop_from_option(sctx, prop, ob->server_scram_iter);
718 case GSASL_SCRAM_SALT:
719 cbrc = prop_from_option(sctx, prop, ob->server_scram_salt);
722 #ifdef EXIM_GSASL_SCRAM_S_KEY
723 case GSASL_SCRAM_STOREDKEY:
724 cbrc = prop_from_option(sctx, prop, ob->server_s_key);
727 case GSASL_SCRAM_SERVERKEY:
728 cbrc = prop_from_option(sctx, prop, ob->server_key);
733 /* SCRAM-*: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
734 DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
735 CRAM-MD5: GSASL_AUTHID
736 PLAIN: GSASL_AUTHID and GSASL_AUTHZID
739 set_exim_authvars_from_a_az_r_props(sctx);
741 if (!(s = ob->server_password))
743 HDEBUG(D_auth) debug_printf("option not set\n");
746 if (!(tmps = CS expand_string(s)))
748 sasl_error_should_defer = !f.expand_string_forcedfail;
749 HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
750 "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
751 return GSASL_AUTHENTICATION_ERROR;
753 HDEBUG(D_auth) debug_printf(" set\n");
754 gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
756 /* This is inadequate; don't think Exim's store stacks are geared
757 for memory wiping, so expanding strings will leave stuff laying around.
758 But no need to compound the problem, so get rid of the one we can. */
760 if (US tmps != s) memset(tmps, '\0', strlen(tmps));
765 HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
766 cbrc = GSASL_NO_CALLBACK;
769 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
770 gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
776 /******************************************************************************/
778 #define PROP_OPTIONAL BIT(0)
781 set_client_prop(Gsasl_session * sctx, Gsasl_property prop, uschar * val,
782 unsigned flags, uschar * buffer, int buffsize)
787 if (!val) return !!(flags & PROP_OPTIONAL);
788 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
790 string_format(buffer, buffsize, "%s", expand_string_message);
795 HDEBUG(D_auth) debug_printf("%s: set %s = '%s'\n", __FUNCTION__,
796 gsasl_prop_code_to_name(prop), s);
797 gsasl_property_set(sctx, prop, CS s);
803 /*************************************************
804 * Client entry point *
805 *************************************************/
807 /* For interface, see auths/README */
811 auth_instance *ablock, /* authenticator block */
812 void * sx, /* connection */
813 int timeout, /* command timeout */
814 uschar *buffer, /* buffer for reading response */
815 int buffsize) /* size of buffer */
817 auth_gsasl_options_block *ob =
818 (auth_gsasl_options_block *)(ablock->options_block);
819 Gsasl_session * sctx = NULL;
820 struct callback_exim_state cb_state;
823 int rc, yield = FAIL;
826 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
827 ablock->name, ob->server_mech);
832 if (tls_out.channelbinding && ob->client_channelbinding)
834 # ifndef DISABLE_TLS_RESUME
835 if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
836 { /* Per RFC 7677 section 4. See also RFC 7627, "Triple Handshake"
837 vulnerability, and https://www.mitls.org/pages/attacks/3SHAKE */
838 string_format(buffer, buffsize, "%s",
839 "channel binding not usable on resumed TLS without extended-master-secret");
843 # ifdef CHANNELBIND_HACK
844 /* This is a gross hack to get around the library before 1.9.2
845 a) requiring that c-b was already set, at the _start() call, and
846 b) caching a b64'd version of the binding then which it never updates. */
848 gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
853 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
855 string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
856 gsasl_strerror_name(rc), gsasl_strerror(rc));
857 HDEBUG(D_auth) debug_printf("%s\n", buffer);
861 cb_state.ablock = ablock;
862 cb_state.currently = CURRENTLY_CLIENT;
863 gsasl_session_hook_set(sctx, &cb_state);
867 if ( !set_client_prop(sctx, GSASL_PASSWORD, ob->client_password,
869 || !set_client_prop(sctx, GSASL_AUTHID, ob->client_username,
871 || !set_client_prop(sctx, GSASL_AUTHZID, ob->client_authz,
872 PROP_OPTIONAL, buffer, buffsize)
877 if (tls_out.channelbinding)
878 if (ob->client_channelbinding)
880 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
882 # ifndef CHANNELBIND_HACK
883 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
888 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
892 /* Run the SASL conversation with the server */
899 rc = gsasl_step64(sctx, CS s, CSS &outstr);
902 ? smtp_write_command(sx, SCMD_FLUSH,
903 outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
904 ablock->public_name, outstr) <= 0
906 ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
908 if (outstr && *outstr) free(outstr);
916 if (rc != GSASL_NEEDS_MORE)
920 string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
924 /* expecting a final 2xx from the server, accepting the AUTH */
926 if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
928 break; /* from SASL sequence loop */
931 /* 2xx or 3xx response is acceptable. If 2xx, no further input */
933 if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
934 if (errno == 0 && buffer[0] == '2')
948 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
949 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
950 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
951 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD);
959 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
961 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n",
962 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
965 case GSASL_CB_TLS_UNIQUE: /*XXX should never get called for this */
967 debug_printf(" filling in\n");
968 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
970 case GSASL_SCRAM_SALTED_PASSWORD:
972 uschar * client_spassword =
973 ((auth_gsasl_options_block *) ablock->options_block)->client_spassword;
975 HDEBUG(D_auth) if (!client_spassword)
976 debug_printf(" client_spassword option unset\n");
977 if (client_spassword)
980 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
981 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
982 set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
983 set_client_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD, client_spassword,
984 0, dummy, sizeof(dummy));
985 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
992 debug_printf(" not providing one\n");
995 return GSASL_NO_CALLBACK;
998 /*************************************************
1000 *************************************************/
1003 auth_gsasl_version_report(FILE *f)
1005 const char *runtime;
1006 runtime = gsasl_check_version(NULL);
1007 fprintf(f, "Library version: GNU SASL: Compile: %s\n"
1009 GSASL_VERSION, runtime);
1015 void auth_gsasl_macros(void) {}
1017 #endif /*!MACRO_PREDEF*/
1018 #endif /* AUTH_GSASL */
1020 /* End of gsasl_exim.c */