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 >= 9
43 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
45 # if GSASL_VERSION_PATCH >= 1
46 # define EXIM_GSASL_SCRAM_S_KEY
48 # if GSASL_VERSION_PATCH < 2
49 # define CHANNELBIND_HACK
52 # define CHANNELBIND_HACK
56 /* Authenticator-specific options. */
57 /* I did have server_*_condition options for various mechanisms, but since
58 we only ever handle one mechanism at a time, I didn't see the point in keeping
59 that. In case someone sees a point, I've left the condition_check() API
61 #define LOFF(field) OPT_OFF(auth_gsasl_options_block, field)
63 optionlist auth_gsasl_options[] = {
64 { "client_authz", opt_stringptr, LOFF(client_authz) },
65 { "client_channelbinding", opt_bool, LOFF(client_channelbinding) },
66 { "client_password", opt_stringptr, LOFF(client_password) },
67 { "client_spassword", opt_stringptr, LOFF(client_spassword) },
68 { "client_username", opt_stringptr, LOFF(client_username) },
70 { "server_channelbinding", opt_bool, LOFF(server_channelbinding) },
71 { "server_hostname", opt_stringptr, LOFF(server_hostname) },
72 #ifdef EXIM_GSASL_SCRAM_S_KEY
73 { "server_key", opt_stringptr, LOFF(server_key) },
75 { "server_mech", opt_stringptr, LOFF(server_mech) },
76 { "server_password", opt_stringptr, LOFF(server_password) },
77 { "server_realm", opt_stringptr, LOFF(server_realm) },
78 { "server_scram_iter", opt_stringptr, LOFF(server_scram_iter) },
79 { "server_scram_salt", opt_stringptr, LOFF(server_scram_salt) },
80 #ifdef EXIM_GSASL_SCRAM_S_KEY
81 { "server_skey", opt_stringptr, LOFF(server_s_key) },
83 { "server_service", opt_stringptr, LOFF(server_service) }
86 int auth_gsasl_options_count =
87 sizeof(auth_gsasl_options)/sizeof(optionlist);
89 /* Defaults for the authenticator-specific options. */
90 auth_gsasl_options_block auth_gsasl_option_defaults = {
91 .server_service = US"smtp",
92 .server_hostname = US"$primary_hostname",
93 .server_scram_iter = US"4096",
94 /* all others zero/null */
99 # include "../macro_predef.h"
102 void auth_gsasl_init(auth_instance *ablock) {}
103 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
104 int auth_gsasl_client(auth_instance *ablock, void * sx,
105 int timeout, uschar *buffer, int buffsize) {return 0;}
106 void auth_gsasl_version_report(FILE *f) {}
109 auth_gsasl_macros(void)
111 # ifdef EXIM_GSASL_HAVE_SCRAM_SHA_256
112 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_SHA_256");
114 # ifdef EXIM_GSASL_SCRAM_S_KEY
115 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_S_KEY");
119 #else /*!MACRO_PREDEF*/
123 /* "Globals" for managing the gsasl interface. */
125 static Gsasl *gsasl_ctx = NULL;
127 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
129 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
131 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
133 static BOOL sasl_error_should_defer = FALSE;
134 static Gsasl_property callback_loop = 0;
135 static BOOL checked_server_condition = FALSE;
137 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
139 struct callback_exim_state {
140 auth_instance *ablock;
145 /*************************************************
146 * Initialization entry point *
147 *************************************************/
149 /* Called for each instance, after its options have been read, to
150 enable consistency checks to be done, or anything else that needs
154 auth_gsasl_init(auth_instance *ablock)
156 static char * once = NULL;
158 auth_gsasl_options_block *ob =
159 (auth_gsasl_options_block *)(ablock->options_block);
161 /* As per existing Cyrus glue, use the authenticator's public name as
162 the default for the mechanism name; we don't handle multiple mechanisms
163 in one authenticator, but the same driver can be used multiple times. */
165 if (!ob->server_mech)
166 ob->server_mech = string_copy(ablock->public_name);
168 /* Can get multiple session contexts from one library context, so just
169 initialise the once. */
173 if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
174 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
175 "couldn't initialise GNU SASL library: %s (%s)",
176 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
178 gsasl_callback_set(gsasl_ctx, main_callback);
181 /* We don't need this except to log it for debugging. */
183 HDEBUG(D_auth) if (!once)
185 if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
186 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
187 "failed to retrieve list of mechanisms: %s (%s)",
188 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
190 debug_printf("GNU SASL supports: %s\n", once);
193 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
194 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
195 "GNU SASL does not support mechanism \"%s\"",
196 ablock->name, ob->server_mech);
198 ablock->server = TRUE;
200 if ( !ablock->server_condition
201 && ( streqic(ob->server_mech, US"EXTERNAL")
202 || streqic(ob->server_mech, US"ANONYMOUS")
203 || streqic(ob->server_mech, US"PLAIN")
204 || streqic(ob->server_mech, US"LOGIN")
207 ablock->server = FALSE;
208 HDEBUG(D_auth) debug_printf("%s authenticator: "
209 "Need server_condition for %s mechanism\n",
210 ablock->name, ob->server_mech);
213 /* This does *not* scale to new SASL mechanisms. Need a better way to ask
214 which properties will be needed. */
216 if ( !ob->server_realm
217 && streqic(ob->server_mech, US"DIGEST-MD5"))
219 ablock->server = FALSE;
220 HDEBUG(D_auth) debug_printf("%s authenticator: "
221 "Need server_realm for %s mechanism\n",
222 ablock->name, ob->server_mech);
225 /* At present, for mechanisms we don't panic on absence of server_condition;
226 need to figure out the most generically correct approach to deciding when
227 it's critical and when it isn't. Eg, for simple validation (PLAIN mechanism,
228 etc) it clearly is critical.
231 ablock->client = ob->client_username && ob->client_password;
235 /* GNU SASL uses one top-level callback, registered at library level.
236 We dispatch to client and server functions instead. */
239 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
242 struct callback_exim_state *cb_state =
243 (struct callback_exim_state *)gsasl_session_hook_get(sctx);
247 HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
248 #ifdef CHANNELBIND_HACK
249 if (prop == GSASL_CB_TLS_UNIQUE)
252 if ((s = gsasl_callback_hook_get(ctx)))
254 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
255 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
259 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE! dummy for now\n");
260 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
265 return GSASL_NO_CALLBACK;
269 debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
270 prop, callback_loop);
272 if (callback_loop > 0)
274 /* Most likely is that we were asked for property FOO, and to
275 expand the string we asked for property BAR to put into an auth
276 variable, but property BAR is not supplied for this mechanism. */
278 debug_printf("Loop, asked for property %d while handling property %d\n",
279 prop, callback_loop);
280 return GSASL_NO_CALLBACK;
282 callback_loop = prop;
284 if (cb_state->currently == CURRENTLY_CLIENT)
285 rc = client_callback(ctx, sctx, prop, cb_state->ablock);
286 else if (cb_state->currently == CURRENTLY_SERVER)
287 rc = server_callback(ctx, sctx, prop, cb_state->ablock);
289 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
290 "unhandled callback state, bug in Exim", cb_state->ablock->name);
298 /*************************************************
299 * Debug service function *
300 *************************************************/
301 static const uschar *
302 gsasl_prop_code_to_name(Gsasl_property prop)
306 case GSASL_AUTHID: return US"AUTHID";
307 case GSASL_AUTHZID: return US"AUTHZID";
308 case GSASL_PASSWORD: return US"PASSWORD";
309 case GSASL_ANONYMOUS_TOKEN: return US"ANONYMOUS_TOKEN";
310 case GSASL_SERVICE: return US"SERVICE";
311 case GSASL_HOSTNAME: return US"HOSTNAME";
312 case GSASL_GSSAPI_DISPLAY_NAME: return US"GSSAPI_DISPLAY_NAME";
313 case GSASL_PASSCODE: return US"PASSCODE";
314 case GSASL_SUGGESTED_PIN: return US"SUGGESTED_PIN";
315 case GSASL_PIN: return US"PIN";
316 case GSASL_REALM: return US"REALM";
317 case GSASL_DIGEST_MD5_HASHED_PASSWORD: return US"DIGEST_MD5_HASHED_PASSWORD";
318 case GSASL_QOPS: return US"QOPS";
319 case GSASL_QOP: return US"QOP";
320 case GSASL_SCRAM_ITER: return US"SCRAM_ITER";
321 case GSASL_SCRAM_SALT: return US"SCRAM_SALT";
322 case GSASL_SCRAM_SALTED_PASSWORD: return US"SCRAM_SALTED_PASSWORD";
323 #ifdef EXIM_GSASL_SCRAM_S_KEY
324 case GSASL_SCRAM_STOREDKEY: return US"SCRAM_STOREDKEY";
325 case GSASL_SCRAM_SERVERKEY: return US"SCRAM_SERVERKEY";
327 case GSASL_CB_TLS_UNIQUE: return US"CB_TLS_UNIQUE";
328 case GSASL_SAML20_IDP_IDENTIFIER: return US"SAML20_IDP_IDENTIFIER";
329 case GSASL_SAML20_REDIRECT_URL: return US"SAML20_REDIRECT_URL";
330 case GSASL_OPENID20_REDIRECT_URL: return US"OPENID20_REDIRECT_URL";
331 case GSASL_OPENID20_OUTCOME_DATA: return US"OPENID20_OUTCOME_DATA";
332 case GSASL_SAML20_AUTHENTICATE_IN_BROWSER: return US"SAML20_AUTHENTICATE_IN_BROWSER";
333 case GSASL_OPENID20_AUTHENTICATE_IN_BROWSER: return US"OPENID20_AUTHENTICATE_IN_BROWSER";
334 #ifdef EXIM_GSASL_SCRAM_S_KEY
335 case GSASL_SCRAM_CLIENTKEY: return US"SCRAM_CLIENTKEY";
337 case GSASL_VALIDATE_SIMPLE: return US"VALIDATE_SIMPLE";
338 case GSASL_VALIDATE_EXTERNAL: return US"VALIDATE_EXTERNAL";
339 case GSASL_VALIDATE_ANONYMOUS: return US"VALIDATE_ANONYMOUS";
340 case GSASL_VALIDATE_GSSAPI: return US"VALIDATE_GSSAPI";
341 case GSASL_VALIDATE_SECURID: return US"VALIDATE_SECURID";
342 case GSASL_VALIDATE_SAML20: return US"VALIDATE_SAML20";
343 case GSASL_VALIDATE_OPENID20: return US"VALIDATE_OPENID20";
345 return CUS string_sprintf("(unknown prop: %d)", (int)prop);
348 /*************************************************
349 * Server entry point *
350 *************************************************/
352 /* For interface, see auths/README */
355 auth_gsasl_server(auth_instance *ablock, uschar *initial_data)
358 char *to_send, *received;
359 Gsasl_session *sctx = NULL;
360 auth_gsasl_options_block *ob =
361 (auth_gsasl_options_block *)(ablock->options_block);
362 struct callback_exim_state cb_state;
363 int rc, auth_result, exim_error, exim_error_override;
366 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
367 ablock->name, ob->server_mech);
370 if (tls_in.channelbinding && ob->server_channelbinding)
372 # ifndef DISABLE_TLS_RESUME
373 if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
374 { /* per RFC 7677 section 4 */
375 HDEBUG(D_auth) debug_printf(
376 "channel binding not usable on resumed TLS without extended-master-secret");
380 # ifdef CHANNELBIND_HACK
381 /* This is a gross hack to get around the library before 1.9.2
382 a) requiring that c-b was already set, at the _start() call, and
383 b) caching a b64'd version of the binding then which it never updates. */
385 gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
390 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
392 auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
393 gsasl_strerror_name(rc), gsasl_strerror(rc));
394 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
397 /* Hereafter: gsasl_finish(sctx) please */
399 cb_state.ablock = ablock;
400 cb_state.currently = CURRENTLY_SERVER;
401 gsasl_session_hook_set(sctx, &cb_state);
403 tmps = CS expand_string(ob->server_service);
404 gsasl_property_set(sctx, GSASL_SERVICE, tmps);
405 tmps = CS expand_string(ob->server_hostname);
406 gsasl_property_set(sctx, GSASL_HOSTNAME, tmps);
407 if (ob->server_realm)
409 tmps = CS expand_string(ob->server_realm);
411 gsasl_property_set(sctx, GSASL_REALM, tmps);
413 /* We don't support protection layers. */
414 gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
417 if (tls_in.channelbinding)
419 /* Some auth mechanisms can ensure that both sides are talking withing the
420 same security context; for TLS, this means that even if a bad certificate
421 has been accepted, they remain MitM-proof because both sides must be within
422 the same negotiated session; if someone is terminating one session and
423 proxying data on within a second, authentication will fail.
425 We might not have this available, depending upon TLS implementation,
426 ciphersuite, phase of moon ...
428 If we do, it results in extra SASL mechanisms being available; here,
429 Exim's one-mechanism-per-authenticator potentially causes problems.
430 It depends upon how GNU SASL will implement the PLUS variants of GS2
431 and whether it automatically mandates a switch to the bound PLUS
432 if the data is available. Since default-on, despite being more secure,
433 would then result in mechanism name changes on a library update, we
434 have little choice but to default it off and let the admin choose to
437 Earlier library versions need this set early, during the _start() call,
438 so we had to misuse gsasl_callback_hook_set/get() as a data transfer
439 mech for the callback done at that time to get the bind-data. More recently
440 the callback is done (if needed) during the first gsasl_stop(). We know
441 the bind-data here so can set it (and should not get a callback).
443 if (ob->server_channelbinding)
445 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
447 # ifndef CHANNELBIND_HACK
448 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding);
453 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
458 debug_printf("Auth %s: no channel-binding data available\n",
462 checked_server_condition = FALSE;
464 received = CS initial_data;
466 exim_error = exim_error_override = OK;
469 switch (rc = gsasl_step64(sctx, received, &to_send))
473 goto STOP_INTERACTION;
476 case GSASL_NEEDS_MORE:
479 case GSASL_AUTHENTICATION_ERROR:
480 case GSASL_INTEGRITY_ERROR:
481 case GSASL_NO_AUTHID:
482 case GSASL_NO_ANONYMOUS_TOKEN:
483 case GSASL_NO_AUTHZID:
484 case GSASL_NO_PASSWORD:
485 case GSASL_NO_PASSCODE:
487 case GSASL_BASE64_ERROR:
488 HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
489 gsasl_strerror_name(rc), gsasl_strerror(rc));
490 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
491 "GNU SASL permanent failure: %s (%s)",
492 ablock->name, ob->server_mech,
493 gsasl_strerror_name(rc), gsasl_strerror(rc));
494 if (rc == GSASL_BASE64_ERROR)
495 exim_error_override = BAD64;
496 goto STOP_INTERACTION;
499 auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
500 gsasl_strerror_name(rc), gsasl_strerror(rc));
501 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
502 exim_error_override = DEFER;
503 goto STOP_INTERACTION;
506 /*XXX having our caller send the final smtp "235" is unfortunate; wastes a roundtrip */
507 if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
508 exim_error = auth_get_no64_data(USS &received, US to_send);
517 break; /* handles * cancelled check */
519 } while (rc == GSASL_NEEDS_MORE);
527 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_ITER)))
528 debug_printf(" - itercnt: '%s'\n", s);
529 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALT)))
530 debug_printf(" - salt: '%s'\n", s);
531 #ifdef EXIM_GSASL_SCRAM_S_KEY
532 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SERVERKEY)))
533 debug_printf(" - ServerKey: '%s'\n", s);
534 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_STOREDKEY)))
535 debug_printf(" - StoredKey: '%s'\n", s);
541 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
543 if (exim_error != OK)
546 if (auth_result != GSASL_OK)
548 HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
549 gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
550 if (exim_error_override != OK)
551 return exim_error_override; /* might be DEFER */
552 if (sasl_error_should_defer) /* overriding auth failure SASL error */
557 /* Auth succeeded, check server_condition unless already done in callback */
558 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
562 /* returns the GSASL status of expanding the Exim string given */
564 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
566 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
569 case OK: return GSASL_OK;
570 case DEFER: sasl_error_should_defer = TRUE;
571 return GSASL_AUTHENTICATION_ERROR;
572 case FAIL: return GSASL_AUTHENTICATION_ERROR;
573 default: log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
574 "Unhandled return from checking %s: %d",
575 ablock->name, label, exim_rc);
579 return GSASL_AUTHENTICATION_ERROR;
584 set_exim_authvar_from_prop(Gsasl_session * sctx, Gsasl_property prop)
586 uschar * propval = US gsasl_property_fast(sctx, prop);
587 int i = expand_nmax, j = i + 1;
588 propval = propval ? string_copy(propval) : US"";
589 auth_vars[i] = expand_nstring[j] = propval;
590 expand_nlength[j] = Ustrlen(propval);
595 set_exim_authvars_from_a_az_r_props(Gsasl_session * sctx)
597 if (expand_nmax > 0 ) return;
599 /* Asking for GSASL_AUTHZID calls back into us if we use
600 gsasl_property_get(), thus the use of gsasl_property_fast().
601 Do we really want to hardcode limits per mechanism? What happens when
602 a new mechanism is added to the library. It *shouldn't* result in us
603 needing to add more glue, since avoiding that is a large part of the
606 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
607 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
608 set_exim_authvar_from_prop(sctx, GSASL_REALM);
613 prop_from_option(Gsasl_session * sctx, Gsasl_property prop,
614 const uschar * option)
616 HDEBUG(D_auth) debug_printf(" %s\n", gsasl_prop_code_to_name(prop));
619 set_exim_authvars_from_a_az_r_props(sctx);
620 option = expand_cstring(option);
621 HDEBUG(D_auth) debug_printf(" '%s'\n", option);
623 gsasl_property_set(sctx, prop, CCS option);
626 HDEBUG(D_auth) debug_printf(" option not set\n");
627 return GSASL_NO_CALLBACK;
631 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
632 auth_instance *ablock)
636 int cbrc = GSASL_NO_CALLBACK;
637 auth_gsasl_options_block *ob =
638 (auth_gsasl_options_block *)(ablock->options_block);
640 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as server\n",
641 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
643 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
648 case GSASL_VALIDATE_SIMPLE:
649 /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
650 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
651 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
652 set_exim_authvar_from_prop(sctx, GSASL_PASSWORD);
654 cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
655 checked_server_condition = TRUE;
658 case GSASL_VALIDATE_EXTERNAL:
659 if (!ablock->server_condition)
661 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
662 cbrc = GSASL_AUTHENTICATION_ERROR;
665 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
667 cbrc = condition_check(ablock,
668 US"server_condition (EXTERNAL)", ablock->server_condition);
669 checked_server_condition = TRUE;
672 case GSASL_VALIDATE_ANONYMOUS:
673 if (!ablock->server_condition)
675 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
676 cbrc = GSASL_AUTHENTICATION_ERROR;
679 set_exim_authvar_from_prop(sctx, GSASL_ANONYMOUS_TOKEN);
681 cbrc = condition_check(ablock,
682 US"server_condition (ANONYMOUS)", ablock->server_condition);
683 checked_server_condition = TRUE;
686 case GSASL_VALIDATE_GSSAPI:
687 /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
688 The display-name is authenticated as part of GSS, the authzid is claimed
689 by the SASL integration after authentication; protected against tampering
690 (if the SASL mechanism supports that, which Kerberos does) but is
691 unverified, same as normal for other mechanisms.
692 First coding, we had these values swapped, but for consistency and prior
693 to the first release of Exim with this authenticator, they've been
694 switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
696 set_exim_authvar_from_prop(sctx, GSASL_GSSAPI_DISPLAY_NAME);
697 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
699 /* In this one case, it perhaps makes sense to default back open?
700 But for consistency, let's just mandate server_condition here too. */
702 cbrc = condition_check(ablock,
703 US"server_condition (GSSAPI family)", ablock->server_condition);
704 checked_server_condition = TRUE;
707 case GSASL_SCRAM_ITER:
708 cbrc = prop_from_option(sctx, prop, ob->server_scram_iter);
711 case GSASL_SCRAM_SALT:
712 cbrc = prop_from_option(sctx, prop, ob->server_scram_salt);
715 #ifdef EXIM_GSASL_SCRAM_S_KEY
716 case GSASL_SCRAM_STOREDKEY:
717 cbrc = prop_from_option(sctx, prop, ob->server_s_key);
720 case GSASL_SCRAM_SERVERKEY:
721 cbrc = prop_from_option(sctx, prop, ob->server_key);
726 /* SCRAM-*: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
727 DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
728 CRAM-MD5: GSASL_AUTHID
729 PLAIN: GSASL_AUTHID and GSASL_AUTHZID
732 set_exim_authvars_from_a_az_r_props(sctx);
734 if (!(s = ob->server_password))
736 HDEBUG(D_auth) debug_printf("option not set\n");
739 if (!(tmps = CS expand_string(s)))
741 sasl_error_should_defer = !f.expand_string_forcedfail;
742 HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
743 "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
744 return GSASL_AUTHENTICATION_ERROR;
746 HDEBUG(D_auth) debug_printf(" set\n");
747 gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
749 /* This is inadequate; don't think Exim's store stacks are geared
750 for memory wiping, so expanding strings will leave stuff laying around.
751 But no need to compound the problem, so get rid of the one we can. */
753 memset(tmps, '\0', strlen(tmps));
758 HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
759 cbrc = GSASL_NO_CALLBACK;
762 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
763 gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
769 /******************************************************************************/
771 #define PROP_OPTIONAL BIT(0)
774 set_client_prop(Gsasl_session * sctx, Gsasl_property prop, uschar * val,
775 unsigned flags, uschar * buffer, int buffsize)
780 if (!val) return !!(flags & PROP_OPTIONAL);
781 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
783 string_format(buffer, buffsize, "%s", expand_string_message);
788 HDEBUG(D_auth) debug_printf("%s: set %s = '%s'\n", __FUNCTION__,
789 gsasl_prop_code_to_name(prop), s);
790 gsasl_property_set(sctx, prop, CS s);
796 /*************************************************
797 * Client entry point *
798 *************************************************/
800 /* For interface, see auths/README */
804 auth_instance *ablock, /* authenticator block */
805 void * sx, /* connection */
806 int timeout, /* command timeout */
807 uschar *buffer, /* buffer for reading response */
808 int buffsize) /* size of buffer */
810 auth_gsasl_options_block *ob =
811 (auth_gsasl_options_block *)(ablock->options_block);
812 Gsasl_session * sctx = NULL;
813 struct callback_exim_state cb_state;
816 int rc, yield = FAIL;
819 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
820 ablock->name, ob->server_mech);
825 if (tls_out.channelbinding && ob->client_channelbinding)
827 # ifndef DISABLE_TLS_RESUME
828 if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
829 { /* per RFC 7677 section 4 */
830 string_format(buffer, buffsize, "%s",
831 "channel binding not usable on resumed TLS without extended-master-secret");
835 # ifdef CHANNELBIND_HACK
836 /* This is a gross hack to get around the library before 1.9.2
837 a) requiring that c-b was already set, at the _start() call, and
838 b) caching a b64'd version of the binding then which it never updates. */
840 gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
845 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
847 string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
848 gsasl_strerror_name(rc), gsasl_strerror(rc));
849 HDEBUG(D_auth) debug_printf("%s\n", buffer);
853 cb_state.ablock = ablock;
854 cb_state.currently = CURRENTLY_CLIENT;
855 gsasl_session_hook_set(sctx, &cb_state);
859 if ( !set_client_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD, ob->client_spassword,
862 !set_client_prop(sctx, GSASL_PASSWORD, ob->client_password,
864 || !set_client_prop(sctx, GSASL_AUTHID, ob->client_username,
866 || !set_client_prop(sctx, GSASL_AUTHZID, ob->client_authz,
867 PROP_OPTIONAL, buffer, buffsize)
872 if (tls_out.channelbinding)
873 if (ob->client_channelbinding)
875 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
877 # ifndef CHANNELBIND_HACK
878 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
883 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
887 /* Run the SASL conversation with the server */
894 rc = gsasl_step64(sctx, CS s, CSS &outstr);
897 ? smtp_write_command(sx, SCMD_FLUSH,
898 outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
899 ablock->public_name, outstr) <= 0
901 ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
903 if (outstr && *outstr) free(outstr);
911 if (rc != GSASL_NEEDS_MORE)
915 string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
919 /* expecting a final 2xx from the server, accepting the AUTH */
921 if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
923 break; /* from SASL sequence loop */
926 /* 2xx or 3xx response is acceptable. If 2xx, no further input */
928 if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
929 if (errno == 0 && buffer[0] == '2')
942 const uschar * s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALTED_PASSWORD);
943 if (s) debug_printf(" - SaltedPassword: '%s'\n", s);
951 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
953 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n",
954 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
957 case GSASL_CB_TLS_UNIQUE: /*XXX should never get called for this */
959 debug_printf(" filling in\n");
960 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
964 debug_printf(" not providing one\n");
967 return GSASL_NO_CALLBACK;
970 /*************************************************
972 *************************************************/
975 auth_gsasl_version_report(FILE *f)
978 runtime = gsasl_check_version(NULL);
979 fprintf(f, "Library version: GNU SASL: Compile: %s\n"
981 GSASL_VERSION, runtime);
987 void auth_gsasl_macros(void) {}
989 #endif /*!MACRO_PREDEF*/
990 #endif /* AUTH_GSASL */
992 /* End of gsasl_exim.c */