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.
30 #define CHANNELBIND_HACK
33 /* dummy function to satisfy compilers when we link in an "empty" file. */
34 static void dummy(int x);
35 static void dummy2(int x) { dummy(x-1); }
36 static void dummy(int x) { dummy2(x-1); }
40 #include "gsasl_exim.h"
43 #if GSASL_VERSION_MINOR >= 9
44 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
46 # if GSASL_VERSION_PATCH >= 1
47 # define EXIM_GSASL_SCRAM_S_KEY
52 /* Authenticator-specific options. */
53 /* I did have server_*_condition options for various mechanisms, but since
54 we only ever handle one mechanism at a time, I didn't see the point in keeping
55 that. In case someone sees a point, I've left the condition_check() API
57 #define LOFF(field) OPT_OFF(auth_gsasl_options_block, field)
59 optionlist auth_gsasl_options[] = {
60 { "client_authz", opt_stringptr, LOFF(client_authz) },
61 { "client_channelbinding", opt_bool, LOFF(client_channelbinding) },
62 { "client_password", opt_stringptr, LOFF(client_password) },
63 { "client_spassword", opt_stringptr, LOFF(client_spassword) },
64 { "client_username", opt_stringptr, LOFF(client_username) },
66 { "server_channelbinding", opt_bool, LOFF(server_channelbinding) },
67 { "server_hostname", opt_stringptr, LOFF(server_hostname) },
68 #ifdef EXIM_GSASL_SCRAM_S_KEY
69 { "server_key", opt_stringptr, LOFF(server_key) },
71 { "server_mech", opt_stringptr, LOFF(server_mech) },
72 { "server_password", opt_stringptr, LOFF(server_password) },
73 { "server_realm", opt_stringptr, LOFF(server_realm) },
74 { "server_scram_iter", opt_stringptr, LOFF(server_scram_iter) },
75 { "server_scram_salt", opt_stringptr, LOFF(server_scram_salt) },
76 #ifdef EXIM_GSASL_SCRAM_S_KEY
77 { "server_skey", opt_stringptr, LOFF(server_s_key) },
79 { "server_service", opt_stringptr, LOFF(server_service) }
82 int auth_gsasl_options_count =
83 sizeof(auth_gsasl_options)/sizeof(optionlist);
85 /* Defaults for the authenticator-specific options. */
86 auth_gsasl_options_block auth_gsasl_option_defaults = {
87 .server_service = US"smtp",
88 .server_hostname = US"$primary_hostname",
89 .server_scram_iter = US"4096",
90 /* all others zero/null */
95 # include "../macro_predef.h"
98 void auth_gsasl_init(auth_instance *ablock) {}
99 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
100 int auth_gsasl_client(auth_instance *ablock, void * sx,
101 int timeout, uschar *buffer, int buffsize) {return 0;}
102 void auth_gsasl_version_report(FILE *f) {}
105 auth_gsasl_macros(void)
107 # ifdef EXIM_GSASL_HAVE_SCRAM_SHA_256
108 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_SHA_256");
110 # ifdef EXIM_GSASL_SCRAM_S_KEY
111 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_S_KEY");
115 #else /*!MACRO_PREDEF*/
119 /* "Globals" for managing the gsasl interface. */
121 static Gsasl *gsasl_ctx = NULL;
123 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
125 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
127 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
129 static BOOL sasl_error_should_defer = FALSE;
130 static Gsasl_property callback_loop = 0;
131 static BOOL checked_server_condition = FALSE;
133 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
135 struct callback_exim_state {
136 auth_instance *ablock;
141 /*************************************************
142 * Initialization entry point *
143 *************************************************/
145 /* Called for each instance, after its options have been read, to
146 enable consistency checks to be done, or anything else that needs
150 auth_gsasl_init(auth_instance *ablock)
152 static char * once = NULL;
154 auth_gsasl_options_block *ob =
155 (auth_gsasl_options_block *)(ablock->options_block);
157 /* As per existing Cyrus glue, use the authenticator's public name as
158 the default for the mechanism name; we don't handle multiple mechanisms
159 in one authenticator, but the same driver can be used multiple times. */
161 if (!ob->server_mech)
162 ob->server_mech = string_copy(ablock->public_name);
164 /* Can get multiple session contexts from one library context, so just
165 initialise the once. */
169 if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
170 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
171 "couldn't initialise GNU SASL library: %s (%s)",
172 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
174 gsasl_callback_set(gsasl_ctx, main_callback);
177 /* We don't need this except to log it for debugging. */
179 HDEBUG(D_auth) if (!once)
181 if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
182 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
183 "failed to retrieve list of mechanisms: %s (%s)",
184 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
186 debug_printf("GNU SASL supports: %s\n", once);
189 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
190 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
191 "GNU SASL does not support mechanism \"%s\"",
192 ablock->name, ob->server_mech);
194 ablock->server = TRUE;
196 if ( !ablock->server_condition
197 && ( streqic(ob->server_mech, US"EXTERNAL")
198 || streqic(ob->server_mech, US"ANONYMOUS")
199 || streqic(ob->server_mech, US"PLAIN")
200 || streqic(ob->server_mech, US"LOGIN")
203 ablock->server = FALSE;
204 HDEBUG(D_auth) debug_printf("%s authenticator: "
205 "Need server_condition for %s mechanism\n",
206 ablock->name, ob->server_mech);
209 /* This does *not* scale to new SASL mechanisms. Need a better way to ask
210 which properties will be needed. */
212 if ( !ob->server_realm
213 && streqic(ob->server_mech, US"DIGEST-MD5"))
215 ablock->server = FALSE;
216 HDEBUG(D_auth) debug_printf("%s authenticator: "
217 "Need server_realm for %s mechanism\n",
218 ablock->name, ob->server_mech);
221 /* At present, for mechanisms we don't panic on absence of server_condition;
222 need to figure out the most generically correct approach to deciding when
223 it's critical and when it isn't. Eg, for simple validation (PLAIN mechanism,
224 etc) it clearly is critical.
227 ablock->client = ob->client_username && ob->client_password;
231 /* GNU SASL uses one top-level callback, registered at library level.
232 We dispatch to client and server functions instead. */
235 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
238 struct callback_exim_state *cb_state =
239 (struct callback_exim_state *)gsasl_session_hook_get(sctx);
243 HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
244 #ifdef CHANNELBIND_HACK
245 if (prop == GSASL_CB_TLS_UNIQUE)
248 if ((s = gsasl_callback_hook_get(ctx)))
250 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
251 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
255 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE! dummy for now\n");
256 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
261 return GSASL_NO_CALLBACK;
265 debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
266 prop, callback_loop);
268 if (callback_loop > 0)
270 /* Most likely is that we were asked for property FOO, and to
271 expand the string we asked for property BAR to put into an auth
272 variable, but property BAR is not supplied for this mechanism. */
274 debug_printf("Loop, asked for property %d while handling property %d\n",
275 prop, callback_loop);
276 return GSASL_NO_CALLBACK;
278 callback_loop = prop;
280 if (cb_state->currently == CURRENTLY_CLIENT)
281 rc = client_callback(ctx, sctx, prop, cb_state->ablock);
282 else if (cb_state->currently == CURRENTLY_SERVER)
283 rc = server_callback(ctx, sctx, prop, cb_state->ablock);
285 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
286 "unhandled callback state, bug in Exim", cb_state->ablock->name);
294 /*************************************************
295 * Debug service function *
296 *************************************************/
297 static const uschar *
298 gsasl_prop_code_to_name(Gsasl_property prop)
302 case GSASL_AUTHID: return US"AUTHID";
303 case GSASL_AUTHZID: return US"AUTHZID";
304 case GSASL_PASSWORD: return US"PASSWORD";
305 case GSASL_ANONYMOUS_TOKEN: return US"ANONYMOUS_TOKEN";
306 case GSASL_SERVICE: return US"SERVICE";
307 case GSASL_HOSTNAME: return US"HOSTNAME";
308 case GSASL_GSSAPI_DISPLAY_NAME: return US"GSSAPI_DISPLAY_NAME";
309 case GSASL_PASSCODE: return US"PASSCODE";
310 case GSASL_SUGGESTED_PIN: return US"SUGGESTED_PIN";
311 case GSASL_PIN: return US"PIN";
312 case GSASL_REALM: return US"REALM";
313 case GSASL_DIGEST_MD5_HASHED_PASSWORD: return US"DIGEST_MD5_HASHED_PASSWORD";
314 case GSASL_QOPS: return US"QOPS";
315 case GSASL_QOP: return US"QOP";
316 case GSASL_SCRAM_ITER: return US"SCRAM_ITER";
317 case GSASL_SCRAM_SALT: return US"SCRAM_SALT";
318 case GSASL_SCRAM_SALTED_PASSWORD: return US"SCRAM_SALTED_PASSWORD";
319 #ifdef EXIM_GSASL_SCRAM_S_KEY
320 case GSASL_SCRAM_STOREDKEY: return US"SCRAM_STOREDKEY";
321 case GSASL_SCRAM_SERVERKEY: return US"SCRAM_SERVERKEY";
323 case GSASL_CB_TLS_UNIQUE: return US"CB_TLS_UNIQUE";
324 case GSASL_SAML20_IDP_IDENTIFIER: return US"SAML20_IDP_IDENTIFIER";
325 case GSASL_SAML20_REDIRECT_URL: return US"SAML20_REDIRECT_URL";
326 case GSASL_OPENID20_REDIRECT_URL: return US"OPENID20_REDIRECT_URL";
327 case GSASL_OPENID20_OUTCOME_DATA: return US"OPENID20_OUTCOME_DATA";
328 case GSASL_SAML20_AUTHENTICATE_IN_BROWSER: return US"SAML20_AUTHENTICATE_IN_BROWSER";
329 case GSASL_OPENID20_AUTHENTICATE_IN_BROWSER: return US"OPENID20_AUTHENTICATE_IN_BROWSER";
330 #ifdef EXIM_GSASL_SCRAM_S_KEY
331 case GSASL_SCRAM_CLIENTKEY: return US"SCRAM_CLIENTKEY";
333 case GSASL_VALIDATE_SIMPLE: return US"VALIDATE_SIMPLE";
334 case GSASL_VALIDATE_EXTERNAL: return US"VALIDATE_EXTERNAL";
335 case GSASL_VALIDATE_ANONYMOUS: return US"VALIDATE_ANONYMOUS";
336 case GSASL_VALIDATE_GSSAPI: return US"VALIDATE_GSSAPI";
337 case GSASL_VALIDATE_SECURID: return US"VALIDATE_SECURID";
338 case GSASL_VALIDATE_SAML20: return US"VALIDATE_SAML20";
339 case GSASL_VALIDATE_OPENID20: return US"VALIDATE_OPENID20";
341 return CUS string_sprintf("(unknown prop: %d)", (int)prop);
344 /*************************************************
345 * Server entry point *
346 *************************************************/
348 /* For interface, see auths/README */
351 auth_gsasl_server(auth_instance *ablock, uschar *initial_data)
354 char *to_send, *received;
355 Gsasl_session *sctx = NULL;
356 auth_gsasl_options_block *ob =
357 (auth_gsasl_options_block *)(ablock->options_block);
358 struct callback_exim_state cb_state;
359 int rc, auth_result, exim_error, exim_error_override;
362 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
363 ablock->name, ob->server_mech);
366 if (tls_in.channelbinding && ob->server_channelbinding)
368 # ifdef EXPERIMENTAL_TLS_RESUME
369 if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
370 { /* per RFC 7677 section 4 */
371 HDEBUG(D_auth) debug_printf(
372 "channel binding not usable on resumed TLS without extended-master-secret");
376 # ifdef CHANNELBIND_HACK
377 /* This is a gross hack to get around the library a) requiring that
378 c-b was already set, at the _start() call, and b) caching a b64'd
379 version of the binding then which it never updates. */
381 gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
386 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
388 auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
389 gsasl_strerror_name(rc), gsasl_strerror(rc));
390 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
393 /* Hereafter: gsasl_finish(sctx) please */
395 cb_state.ablock = ablock;
396 cb_state.currently = CURRENTLY_SERVER;
397 gsasl_session_hook_set(sctx, &cb_state);
399 tmps = CS expand_string(ob->server_service);
400 gsasl_property_set(sctx, GSASL_SERVICE, tmps);
401 tmps = CS expand_string(ob->server_hostname);
402 gsasl_property_set(sctx, GSASL_HOSTNAME, tmps);
403 if (ob->server_realm)
405 tmps = CS expand_string(ob->server_realm);
407 gsasl_property_set(sctx, GSASL_REALM, tmps);
409 /* We don't support protection layers. */
410 gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
413 if (tls_in.channelbinding)
415 /* Some auth mechanisms can ensure that both sides are talking withing the
416 same security context; for TLS, this means that even if a bad certificate
417 has been accepted, they remain MitM-proof because both sides must be within
418 the same negotiated session; if someone is terminating one session and
419 proxying data on within a second, authentication will fail.
421 We might not have this available, depending upon TLS implementation,
422 ciphersuite, phase of moon ...
424 If we do, it results in extra SASL mechanisms being available; here,
425 Exim's one-mechanism-per-authenticator potentially causes problems.
426 It depends upon how GNU SASL will implement the PLUS variants of GS2
427 and whether it automatically mandates a switch to the bound PLUS
428 if the data is available. Since default-on, despite being more secure,
429 would then result in mechanism name changes on a library update, we
430 have little choice but to default it off and let the admin choose to
433 if (ob->server_channelbinding)
435 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
437 # ifndef CHANNELBIND_HACK
438 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding);
443 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
448 debug_printf("Auth %s: no channel-binding data available\n",
452 checked_server_condition = FALSE;
454 received = CS initial_data;
456 exim_error = exim_error_override = OK;
459 switch (rc = gsasl_step64(sctx, received, &to_send))
463 goto STOP_INTERACTION;
466 case GSASL_NEEDS_MORE:
469 case GSASL_AUTHENTICATION_ERROR:
470 case GSASL_INTEGRITY_ERROR:
471 case GSASL_NO_AUTHID:
472 case GSASL_NO_ANONYMOUS_TOKEN:
473 case GSASL_NO_AUTHZID:
474 case GSASL_NO_PASSWORD:
475 case GSASL_NO_PASSCODE:
477 case GSASL_BASE64_ERROR:
478 HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
479 gsasl_strerror_name(rc), gsasl_strerror(rc));
480 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
481 "GNU SASL permanent failure: %s (%s)",
482 ablock->name, ob->server_mech,
483 gsasl_strerror_name(rc), gsasl_strerror(rc));
484 if (rc == GSASL_BASE64_ERROR)
485 exim_error_override = BAD64;
486 goto STOP_INTERACTION;
489 auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
490 gsasl_strerror_name(rc), gsasl_strerror(rc));
491 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
492 exim_error_override = DEFER;
493 goto STOP_INTERACTION;
496 /*XXX having our caller send the final smtp "235" is unfortunate; wastes a roundtrip */
497 if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
498 exim_error = auth_get_no64_data(USS &received, US to_send);
507 break; /* handles * cancelled check */
509 } while (rc == GSASL_NEEDS_MORE);
517 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_ITER)))
518 debug_printf(" - itercnt: '%s'\n", s);
519 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALT)))
520 debug_printf(" - salt: '%s'\n", s);
521 #ifdef EXIM_GSASL_SCRAM_S_KEY
522 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SERVERKEY)))
523 debug_printf(" - ServerKey: '%s'\n", s);
524 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_STOREDKEY)))
525 debug_printf(" - StoredKey: '%s'\n", s);
531 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
533 if (exim_error != OK)
536 if (auth_result != GSASL_OK)
538 HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
539 gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
540 if (exim_error_override != OK)
541 return exim_error_override; /* might be DEFER */
542 if (sasl_error_should_defer) /* overriding auth failure SASL error */
547 /* Auth succeeded, check server_condition unless already done in callback */
548 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
552 /* returns the GSASL status of expanding the Exim string given */
554 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
556 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
559 case OK: return GSASL_OK;
560 case DEFER: sasl_error_should_defer = TRUE;
561 return GSASL_AUTHENTICATION_ERROR;
562 case FAIL: return GSASL_AUTHENTICATION_ERROR;
563 default: log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
564 "Unhandled return from checking %s: %d",
565 ablock->name, label, exim_rc);
569 return GSASL_AUTHENTICATION_ERROR;
574 set_exim_authvar_from_prop(Gsasl_session * sctx, Gsasl_property prop)
576 uschar * propval = US gsasl_property_fast(sctx, prop);
577 int i = expand_nmax, j = i + 1;
578 propval = propval ? string_copy(propval) : US"";
579 auth_vars[i] = expand_nstring[j] = propval;
580 expand_nlength[j] = Ustrlen(propval);
585 set_exim_authvars_from_a_az_r_props(Gsasl_session * sctx)
587 if (expand_nmax > 0 ) return;
589 /* Asking for GSASL_AUTHZID calls back into us if we use
590 gsasl_property_get(), thus the use of gsasl_property_fast().
591 Do we really want to hardcode limits per mechanism? What happens when
592 a new mechanism is added to the library. It *shouldn't* result in us
593 needing to add more glue, since avoiding that is a large part of the
596 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
597 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
598 set_exim_authvar_from_prop(sctx, GSASL_REALM);
603 prop_from_option(Gsasl_session * sctx, Gsasl_property prop,
604 const uschar * option)
606 HDEBUG(D_auth) debug_printf(" %s\n", gsasl_prop_code_to_name(prop));
609 set_exim_authvars_from_a_az_r_props(sctx);
610 option = expand_cstring(option);
611 HDEBUG(D_auth) debug_printf(" '%s'\n", option);
613 gsasl_property_set(sctx, prop, CCS option);
616 HDEBUG(D_auth) debug_printf(" option not set\n");
617 return GSASL_NO_CALLBACK;
621 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
622 auth_instance *ablock)
626 int cbrc = GSASL_NO_CALLBACK;
627 auth_gsasl_options_block *ob =
628 (auth_gsasl_options_block *)(ablock->options_block);
630 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as server\n",
631 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
633 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
638 case GSASL_VALIDATE_SIMPLE:
639 /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
640 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
641 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
642 set_exim_authvar_from_prop(sctx, GSASL_PASSWORD);
644 cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
645 checked_server_condition = TRUE;
648 case GSASL_VALIDATE_EXTERNAL:
649 if (!ablock->server_condition)
651 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
652 cbrc = GSASL_AUTHENTICATION_ERROR;
655 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
657 cbrc = condition_check(ablock,
658 US"server_condition (EXTERNAL)", ablock->server_condition);
659 checked_server_condition = TRUE;
662 case GSASL_VALIDATE_ANONYMOUS:
663 if (!ablock->server_condition)
665 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
666 cbrc = GSASL_AUTHENTICATION_ERROR;
669 set_exim_authvar_from_prop(sctx, GSASL_ANONYMOUS_TOKEN);
671 cbrc = condition_check(ablock,
672 US"server_condition (ANONYMOUS)", ablock->server_condition);
673 checked_server_condition = TRUE;
676 case GSASL_VALIDATE_GSSAPI:
677 /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
678 The display-name is authenticated as part of GSS, the authzid is claimed
679 by the SASL integration after authentication; protected against tampering
680 (if the SASL mechanism supports that, which Kerberos does) but is
681 unverified, same as normal for other mechanisms.
682 First coding, we had these values swapped, but for consistency and prior
683 to the first release of Exim with this authenticator, they've been
684 switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
686 set_exim_authvar_from_prop(sctx, GSASL_GSSAPI_DISPLAY_NAME);
687 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
689 /* In this one case, it perhaps makes sense to default back open?
690 But for consistency, let's just mandate server_condition here too. */
692 cbrc = condition_check(ablock,
693 US"server_condition (GSSAPI family)", ablock->server_condition);
694 checked_server_condition = TRUE;
697 case GSASL_SCRAM_ITER:
698 cbrc = prop_from_option(sctx, prop, ob->server_scram_iter);
701 case GSASL_SCRAM_SALT:
702 cbrc = prop_from_option(sctx, prop, ob->server_scram_salt);
705 #ifdef EXIM_GSASL_SCRAM_S_KEY
706 case GSASL_SCRAM_STOREDKEY:
707 cbrc = prop_from_option(sctx, prop, ob->server_s_key);
710 case GSASL_SCRAM_SERVERKEY:
711 cbrc = prop_from_option(sctx, prop, ob->server_key);
716 /* SCRAM-*: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
717 DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
718 CRAM-MD5: GSASL_AUTHID
719 PLAIN: GSASL_AUTHID and GSASL_AUTHZID
722 set_exim_authvars_from_a_az_r_props(sctx);
724 if (!(s = ob->server_password))
726 HDEBUG(D_auth) debug_printf("option not set\n");
729 if (!(tmps = CS expand_string(s)))
731 sasl_error_should_defer = !f.expand_string_forcedfail;
732 HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
733 "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
734 return GSASL_AUTHENTICATION_ERROR;
736 HDEBUG(D_auth) debug_printf(" set\n");
737 gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
739 /* This is inadequate; don't think Exim's store stacks are geared
740 for memory wiping, so expanding strings will leave stuff laying around.
741 But no need to compound the problem, so get rid of the one we can. */
743 memset(tmps, '\0', strlen(tmps));
748 HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
749 cbrc = GSASL_NO_CALLBACK;
752 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
753 gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
759 /******************************************************************************/
761 #define PROP_OPTIONAL BIT(0)
764 set_client_prop(Gsasl_session * sctx, Gsasl_property prop, uschar * val,
765 unsigned flags, uschar * buffer, int buffsize)
770 if (!val) return !!(flags & PROP_OPTIONAL);
771 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
773 string_format(buffer, buffsize, "%s", expand_string_message);
778 HDEBUG(D_auth) debug_printf("%s: set %s = '%s'\n", __FUNCTION__,
779 gsasl_prop_code_to_name(prop), s);
780 gsasl_property_set(sctx, prop, CS s);
786 /*************************************************
787 * Client entry point *
788 *************************************************/
790 /* For interface, see auths/README */
794 auth_instance *ablock, /* authenticator block */
795 void * sx, /* connection */
796 int timeout, /* command timeout */
797 uschar *buffer, /* buffer for reading response */
798 int buffsize) /* size of buffer */
800 auth_gsasl_options_block *ob =
801 (auth_gsasl_options_block *)(ablock->options_block);
802 Gsasl_session * sctx = NULL;
803 struct callback_exim_state cb_state;
806 int rc, yield = FAIL;
809 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
810 ablock->name, ob->server_mech);
815 if (tls_out.channelbinding && ob->client_channelbinding)
817 # ifdef EXPERIMENTAL_TLS_RESUME
818 if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
819 { /* per RFC 7677 section 4 */
820 string_format(buffer, buffsize, "%s",
821 "channel binding not usable on resumed TLS without extended-master-secret");
825 # ifdef CHANNELBIND_HACK
826 /* This is a gross hack to get around the library a) requiring that
827 c-b was already set, at the _start() call, and b) caching a b64'd
828 version of the binding then which it never updates. */
830 gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
835 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
837 string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
838 gsasl_strerror_name(rc), gsasl_strerror(rc));
839 HDEBUG(D_auth) debug_printf("%s\n", buffer);
843 cb_state.ablock = ablock;
844 cb_state.currently = CURRENTLY_CLIENT;
845 gsasl_session_hook_set(sctx, &cb_state);
849 if ( !set_client_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD, ob->client_spassword,
852 !set_client_prop(sctx, GSASL_PASSWORD, ob->client_password,
854 || !set_client_prop(sctx, GSASL_AUTHID, ob->client_username,
856 || !set_client_prop(sctx, GSASL_AUTHZID, ob->client_authz,
857 PROP_OPTIONAL, buffer, buffsize)
862 if (tls_out.channelbinding)
863 if (ob->client_channelbinding)
865 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
867 # ifndef CHANNELBIND_HACK
868 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
873 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
877 /* Run the SASL conversation with the server */
884 rc = gsasl_step64(sctx, CS s, CSS &outstr);
887 ? smtp_write_command(sx, SCMD_FLUSH,
888 outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
889 ablock->public_name, outstr) <= 0
891 ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
893 if (outstr && *outstr) free(outstr);
901 if (rc != GSASL_NEEDS_MORE)
905 string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
909 /* expecting a final 2xx from the server, accepting the AUTH */
911 if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
913 break; /* from SASL sequence loop */
916 /* 2xx or 3xx response is acceptable. If 2xx, no further input */
918 if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
919 if (errno == 0 && buffer[0] == '2')
932 const uschar * s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALTED_PASSWORD);
933 if (s) debug_printf(" - SaltedPassword: '%s'\n", s);
941 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
943 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n",
944 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
947 case GSASL_CB_TLS_UNIQUE:
949 debug_printf(" filling in\n");
950 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
954 debug_printf(" not providing one\n");
957 return GSASL_NO_CALLBACK;
960 /*************************************************
962 *************************************************/
965 auth_gsasl_version_report(FILE *f)
968 runtime = gsasl_check_version(NULL);
969 fprintf(f, "Library version: GNU SASL: Compile: %s\n"
971 GSASL_VERSION, runtime);
977 void auth_gsasl_macros(void) {}
979 #endif /*!MACRO_PREDEF*/
980 #endif /* AUTH_GSASL */
982 /* End of gsasl_exim.c */