1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) The Exim Maintainers 2019 */
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 optionlist auth_gsasl_options[] = {
58 { "client_authz", opt_stringptr,
59 (void *)(offsetof(auth_gsasl_options_block, client_authz)) },
60 { "client_channelbinding", opt_bool,
61 (void *)(offsetof(auth_gsasl_options_block, client_channelbinding)) },
62 { "client_password", opt_stringptr,
63 (void *)(offsetof(auth_gsasl_options_block, client_password)) },
64 { "client_spassword", opt_stringptr,
65 (void *)(offsetof(auth_gsasl_options_block, client_spassword)) },
66 { "client_username", opt_stringptr,
67 (void *)(offsetof(auth_gsasl_options_block, client_username)) },
69 { "server_channelbinding", opt_bool,
70 (void *)(offsetof(auth_gsasl_options_block, server_channelbinding)) },
71 { "server_hostname", opt_stringptr,
72 (void *)(offsetof(auth_gsasl_options_block, server_hostname)) },
73 #ifdef EXIM_GSASL_SCRAM_S_KEY
74 { "server_key", opt_stringptr,
75 (void *)(offsetof(auth_gsasl_options_block, server_key)) },
77 { "server_mech", opt_stringptr,
78 (void *)(offsetof(auth_gsasl_options_block, server_mech)) },
79 { "server_password", opt_stringptr,
80 (void *)(offsetof(auth_gsasl_options_block, server_password)) },
81 { "server_realm", opt_stringptr,
82 (void *)(offsetof(auth_gsasl_options_block, server_realm)) },
83 { "server_scram_iter", opt_stringptr,
84 (void *)(offsetof(auth_gsasl_options_block, server_scram_iter)) },
85 { "server_scram_salt", opt_stringptr,
86 (void *)(offsetof(auth_gsasl_options_block, server_scram_salt)) },
87 #ifdef EXIM_GSASL_SCRAM_S_KEY
88 { "server_skey", opt_stringptr,
89 (void *)(offsetof(auth_gsasl_options_block, server_s_key)) },
91 { "server_service", opt_stringptr,
92 (void *)(offsetof(auth_gsasl_options_block, server_service)) }
95 int auth_gsasl_options_count =
96 sizeof(auth_gsasl_options)/sizeof(optionlist);
98 /* Defaults for the authenticator-specific options. */
99 auth_gsasl_options_block auth_gsasl_option_defaults = {
100 .server_service = US"smtp",
101 .server_hostname = US"$primary_hostname",
102 .server_scram_iter = US"4096",
103 /* all others zero/null */
108 # include "../macro_predef.h"
111 void auth_gsasl_init(auth_instance *ablock) {}
112 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
113 int auth_gsasl_client(auth_instance *ablock, void * sx,
114 int timeout, uschar *buffer, int buffsize) {return 0;}
115 void auth_gsasl_version_report(FILE *f) {}
118 auth_gsasl_macros(void)
120 # ifdef EXIM_GSASL_HAVE_SCRAM_SHA_256
121 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_SHA_256");
123 # ifdef EXIM_GSASL_SCRAM_S_KEY
124 builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_S_KEY");
128 #else /*!MACRO_PREDEF*/
132 /* "Globals" for managing the gsasl interface. */
134 static Gsasl *gsasl_ctx = NULL;
136 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
138 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
140 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
142 static BOOL sasl_error_should_defer = FALSE;
143 static Gsasl_property callback_loop = 0;
144 static BOOL checked_server_condition = FALSE;
146 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
148 struct callback_exim_state {
149 auth_instance *ablock;
154 /*************************************************
155 * Initialization entry point *
156 *************************************************/
158 /* Called for each instance, after its options have been read, to
159 enable consistency checks to be done, or anything else that needs
163 auth_gsasl_init(auth_instance *ablock)
165 static char * once = NULL;
167 auth_gsasl_options_block *ob =
168 (auth_gsasl_options_block *)(ablock->options_block);
170 /* As per existing Cyrus glue, use the authenticator's public name as
171 the default for the mechanism name; we don't handle multiple mechanisms
172 in one authenticator, but the same driver can be used multiple times. */
174 if (!ob->server_mech)
175 ob->server_mech = string_copy(ablock->public_name);
177 /* Can get multiple session contexts from one library context, so just
178 initialise the once. */
182 if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
183 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
184 "couldn't initialise GNU SASL library: %s (%s)",
185 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
187 gsasl_callback_set(gsasl_ctx, main_callback);
190 /* We don't need this except to log it for debugging. */
192 HDEBUG(D_auth) if (!once)
194 if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
195 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
196 "failed to retrieve list of mechanisms: %s (%s)",
197 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
199 debug_printf("GNU SASL supports: %s\n", once);
202 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
203 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
204 "GNU SASL does not support mechanism \"%s\"",
205 ablock->name, ob->server_mech);
207 ablock->server = TRUE;
209 if ( !ablock->server_condition
210 && ( streqic(ob->server_mech, US"EXTERNAL")
211 || streqic(ob->server_mech, US"ANONYMOUS")
212 || streqic(ob->server_mech, US"PLAIN")
213 || streqic(ob->server_mech, US"LOGIN")
216 ablock->server = FALSE;
217 HDEBUG(D_auth) debug_printf("%s authenticator: "
218 "Need server_condition for %s mechanism\n",
219 ablock->name, ob->server_mech);
222 /* This does *not* scale to new SASL mechanisms. Need a better way to ask
223 which properties will be needed. */
225 if ( !ob->server_realm
226 && streqic(ob->server_mech, US"DIGEST-MD5"))
228 ablock->server = FALSE;
229 HDEBUG(D_auth) debug_printf("%s authenticator: "
230 "Need server_realm for %s mechanism\n",
231 ablock->name, ob->server_mech);
234 /* At present, for mechanisms we don't panic on absence of server_condition;
235 need to figure out the most generically correct approach to deciding when
236 it's critical and when it isn't. Eg, for simple validation (PLAIN mechanism,
237 etc) it clearly is critical.
240 ablock->client = ob->client_username && ob->client_password;
244 /* GNU SASL uses one top-level callback, registered at library level.
245 We dispatch to client and server functions instead. */
248 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
251 struct callback_exim_state *cb_state =
252 (struct callback_exim_state *)gsasl_session_hook_get(sctx);
256 HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
257 #ifdef CHANNELBIND_HACK
258 if (prop == GSASL_CB_TLS_UNIQUE)
261 if ((s = gsasl_callback_hook_get(ctx)))
263 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
264 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
268 HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE! dummy for now\n");
269 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
274 return GSASL_NO_CALLBACK;
278 debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
279 prop, callback_loop);
281 if (callback_loop > 0)
283 /* Most likely is that we were asked for property FOO, and to
284 expand the string we asked for property BAR to put into an auth
285 variable, but property BAR is not supplied for this mechanism. */
287 debug_printf("Loop, asked for property %d while handling property %d\n",
288 prop, callback_loop);
289 return GSASL_NO_CALLBACK;
291 callback_loop = prop;
293 if (cb_state->currently == CURRENTLY_CLIENT)
294 rc = client_callback(ctx, sctx, prop, cb_state->ablock);
295 else if (cb_state->currently == CURRENTLY_SERVER)
296 rc = server_callback(ctx, sctx, prop, cb_state->ablock);
298 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
299 "unhandled callback state, bug in Exim", cb_state->ablock->name);
307 /*************************************************
308 * Debug service function *
309 *************************************************/
310 static const uschar *
311 gsasl_prop_code_to_name(Gsasl_property prop)
315 case GSASL_AUTHID: return US"AUTHID";
316 case GSASL_AUTHZID: return US"AUTHZID";
317 case GSASL_PASSWORD: return US"PASSWORD";
318 case GSASL_ANONYMOUS_TOKEN: return US"ANONYMOUS_TOKEN";
319 case GSASL_SERVICE: return US"SERVICE";
320 case GSASL_HOSTNAME: return US"HOSTNAME";
321 case GSASL_GSSAPI_DISPLAY_NAME: return US"GSSAPI_DISPLAY_NAME";
322 case GSASL_PASSCODE: return US"PASSCODE";
323 case GSASL_SUGGESTED_PIN: return US"SUGGESTED_PIN";
324 case GSASL_PIN: return US"PIN";
325 case GSASL_REALM: return US"REALM";
326 case GSASL_DIGEST_MD5_HASHED_PASSWORD: return US"DIGEST_MD5_HASHED_PASSWORD";
327 case GSASL_QOPS: return US"QOPS";
328 case GSASL_QOP: return US"QOP";
329 case GSASL_SCRAM_ITER: return US"SCRAM_ITER";
330 case GSASL_SCRAM_SALT: return US"SCRAM_SALT";
331 case GSASL_SCRAM_SALTED_PASSWORD: return US"SCRAM_SALTED_PASSWORD";
332 #ifdef EXIM_GSASL_SCRAM_S_KEY
333 case GSASL_SCRAM_STOREDKEY: return US"SCRAM_STOREDKEY";
334 case GSASL_SCRAM_SERVERKEY: return US"SCRAM_SERVERKEY";
336 case GSASL_CB_TLS_UNIQUE: return US"CB_TLS_UNIQUE";
337 case GSASL_SAML20_IDP_IDENTIFIER: return US"SAML20_IDP_IDENTIFIER";
338 case GSASL_SAML20_REDIRECT_URL: return US"SAML20_REDIRECT_URL";
339 case GSASL_OPENID20_REDIRECT_URL: return US"OPENID20_REDIRECT_URL";
340 case GSASL_OPENID20_OUTCOME_DATA: return US"OPENID20_OUTCOME_DATA";
341 case GSASL_SAML20_AUTHENTICATE_IN_BROWSER: return US"SAML20_AUTHENTICATE_IN_BROWSER";
342 case GSASL_OPENID20_AUTHENTICATE_IN_BROWSER: return US"OPENID20_AUTHENTICATE_IN_BROWSER";
343 #ifdef EXIM_GSASL_SCRAM_S_KEY
344 case GSASL_SCRAM_CLIENTKEY: return US"SCRAM_CLIENTKEY";
346 case GSASL_VALIDATE_SIMPLE: return US"VALIDATE_SIMPLE";
347 case GSASL_VALIDATE_EXTERNAL: return US"VALIDATE_EXTERNAL";
348 case GSASL_VALIDATE_ANONYMOUS: return US"VALIDATE_ANONYMOUS";
349 case GSASL_VALIDATE_GSSAPI: return US"VALIDATE_GSSAPI";
350 case GSASL_VALIDATE_SECURID: return US"VALIDATE_SECURID";
351 case GSASL_VALIDATE_SAML20: return US"VALIDATE_SAML20";
352 case GSASL_VALIDATE_OPENID20: return US"VALIDATE_OPENID20";
354 return CUS string_sprintf("(unknown prop: %d)", (int)prop);
357 /*************************************************
358 * Server entry point *
359 *************************************************/
361 /* For interface, see auths/README */
364 auth_gsasl_server(auth_instance *ablock, uschar *initial_data)
367 char *to_send, *received;
368 Gsasl_session *sctx = NULL;
369 auth_gsasl_options_block *ob =
370 (auth_gsasl_options_block *)(ablock->options_block);
371 struct callback_exim_state cb_state;
372 int rc, auth_result, exim_error, exim_error_override;
375 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
376 ablock->name, ob->server_mech);
379 if (tls_in.channelbinding && ob->server_channelbinding)
381 # ifdef EXPERIMENTAL_TLS_RESUME
382 if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
383 { /* per RFC 7677 section 4 */
384 HDEBUG(D_auth) debug_printf(
385 "channel binding not usable on resumed TLS without extended-master-secret");
389 # ifdef CHANNELBIND_HACK
390 /* This is a gross hack to get around the library a) requiring that
391 c-b was already set, at the _start() call, and b) caching a b64'd
392 version of the binding then which it never updates. */
394 gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
399 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
401 auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
402 gsasl_strerror_name(rc), gsasl_strerror(rc));
403 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
406 /* Hereafter: gsasl_finish(sctx) please */
408 cb_state.ablock = ablock;
409 cb_state.currently = CURRENTLY_SERVER;
410 gsasl_session_hook_set(sctx, &cb_state);
412 tmps = CS expand_string(ob->server_service);
413 gsasl_property_set(sctx, GSASL_SERVICE, tmps);
414 tmps = CS expand_string(ob->server_hostname);
415 gsasl_property_set(sctx, GSASL_HOSTNAME, tmps);
416 if (ob->server_realm)
418 tmps = CS expand_string(ob->server_realm);
420 gsasl_property_set(sctx, GSASL_REALM, tmps);
422 /* We don't support protection layers. */
423 gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
426 if (tls_in.channelbinding)
428 /* Some auth mechanisms can ensure that both sides are talking withing the
429 same security context; for TLS, this means that even if a bad certificate
430 has been accepted, they remain MitM-proof because both sides must be within
431 the same negotiated session; if someone is terminating one session and
432 proxying data on within a second, authentication will fail.
434 We might not have this available, depending upon TLS implementation,
435 ciphersuite, phase of moon ...
437 If we do, it results in extra SASL mechanisms being available; here,
438 Exim's one-mechanism-per-authenticator potentially causes problems.
439 It depends upon how GNU SASL will implement the PLUS variants of GS2
440 and whether it automatically mandates a switch to the bound PLUS
441 if the data is available. Since default-on, despite being more secure,
442 would then result in mechanism name changes on a library update, we
443 have little choice but to default it off and let the admin choose to
446 if (ob->server_channelbinding)
448 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
450 # ifndef CHANNELBIND_HACK
451 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding);
456 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
461 debug_printf("Auth %s: no channel-binding data available\n",
465 checked_server_condition = FALSE;
467 received = CS initial_data;
469 exim_error = exim_error_override = OK;
472 switch (rc = gsasl_step64(sctx, received, &to_send))
476 goto STOP_INTERACTION;
479 case GSASL_NEEDS_MORE:
482 case GSASL_AUTHENTICATION_ERROR:
483 case GSASL_INTEGRITY_ERROR:
484 case GSASL_NO_AUTHID:
485 case GSASL_NO_ANONYMOUS_TOKEN:
486 case GSASL_NO_AUTHZID:
487 case GSASL_NO_PASSWORD:
488 case GSASL_NO_PASSCODE:
490 case GSASL_BASE64_ERROR:
491 HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
492 gsasl_strerror_name(rc), gsasl_strerror(rc));
493 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
494 "GNU SASL permanent failure: %s (%s)",
495 ablock->name, ob->server_mech,
496 gsasl_strerror_name(rc), gsasl_strerror(rc));
497 if (rc == GSASL_BASE64_ERROR)
498 exim_error_override = BAD64;
499 goto STOP_INTERACTION;
502 auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
503 gsasl_strerror_name(rc), gsasl_strerror(rc));
504 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
505 exim_error_override = DEFER;
506 goto STOP_INTERACTION;
509 /*XXX having our caller send the final smtp "235" is unfortunate; wastes a roundtrip */
510 if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
511 exim_error = auth_get_no64_data(USS &received, US to_send);
520 break; /* handles * cancelled check */
522 } while (rc == GSASL_NEEDS_MORE);
530 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_ITER)))
531 debug_printf(" - itercnt: '%s'\n", s);
532 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALT)))
533 debug_printf(" - salt: '%s'\n", s);
534 #ifdef EXIM_GSASL_SCRAM_S_KEY
535 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SERVERKEY)))
536 debug_printf(" - ServerKey: '%s'\n", s);
537 if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_STOREDKEY)))
538 debug_printf(" - StoredKey: '%s'\n", s);
544 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
546 if (exim_error != OK)
549 if (auth_result != GSASL_OK)
551 HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
552 gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
553 if (exim_error_override != OK)
554 return exim_error_override; /* might be DEFER */
555 if (sasl_error_should_defer) /* overriding auth failure SASL error */
560 /* Auth succeeded, check server_condition unless already done in callback */
561 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
565 /* returns the GSASL status of expanding the Exim string given */
567 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
569 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
572 case OK: return GSASL_OK;
573 case DEFER: sasl_error_should_defer = TRUE;
574 return GSASL_AUTHENTICATION_ERROR;
575 case FAIL: return GSASL_AUTHENTICATION_ERROR;
576 default: log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
577 "Unhandled return from checking %s: %d",
578 ablock->name, label, exim_rc);
582 return GSASL_AUTHENTICATION_ERROR;
587 set_exim_authvar_from_prop(Gsasl_session * sctx, Gsasl_property prop)
589 uschar * propval = US gsasl_property_fast(sctx, prop);
590 int i = expand_nmax, j = i + 1;
591 propval = propval ? string_copy(propval) : US"";
592 auth_vars[i] = expand_nstring[j] = propval;
593 expand_nlength[j] = Ustrlen(propval);
598 set_exim_authvars_from_a_az_r_props(Gsasl_session * sctx)
600 if (expand_nmax > 0 ) return;
602 /* Asking for GSASL_AUTHZID calls back into us if we use
603 gsasl_property_get(), thus the use of gsasl_property_fast().
604 Do we really want to hardcode limits per mechanism? What happens when
605 a new mechanism is added to the library. It *shouldn't* result in us
606 needing to add more glue, since avoiding that is a large part of the
609 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
610 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
611 set_exim_authvar_from_prop(sctx, GSASL_REALM);
616 prop_from_option(Gsasl_session * sctx, Gsasl_property prop,
617 const uschar * option)
619 HDEBUG(D_auth) debug_printf(" %s\n", gsasl_prop_code_to_name(prop));
622 set_exim_authvars_from_a_az_r_props(sctx);
623 option = expand_cstring(option);
624 HDEBUG(D_auth) debug_printf(" '%s'\n", option);
626 gsasl_property_set(sctx, prop, CCS option);
629 HDEBUG(D_auth) debug_printf(" option not set\n");
630 return GSASL_NO_CALLBACK;
634 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
635 auth_instance *ablock)
639 int cbrc = GSASL_NO_CALLBACK;
640 auth_gsasl_options_block *ob =
641 (auth_gsasl_options_block *)(ablock->options_block);
643 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as server\n",
644 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
646 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
651 case GSASL_VALIDATE_SIMPLE:
652 /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
653 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
654 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
655 set_exim_authvar_from_prop(sctx, GSASL_PASSWORD);
657 cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
658 checked_server_condition = TRUE;
661 case GSASL_VALIDATE_EXTERNAL:
662 if (!ablock->server_condition)
664 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
665 cbrc = GSASL_AUTHENTICATION_ERROR;
668 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
670 cbrc = condition_check(ablock,
671 US"server_condition (EXTERNAL)", ablock->server_condition);
672 checked_server_condition = TRUE;
675 case GSASL_VALIDATE_ANONYMOUS:
676 if (!ablock->server_condition)
678 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
679 cbrc = GSASL_AUTHENTICATION_ERROR;
682 set_exim_authvar_from_prop(sctx, GSASL_ANONYMOUS_TOKEN);
684 cbrc = condition_check(ablock,
685 US"server_condition (ANONYMOUS)", ablock->server_condition);
686 checked_server_condition = TRUE;
689 case GSASL_VALIDATE_GSSAPI:
690 /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
691 The display-name is authenticated as part of GSS, the authzid is claimed
692 by the SASL integration after authentication; protected against tampering
693 (if the SASL mechanism supports that, which Kerberos does) but is
694 unverified, same as normal for other mechanisms.
695 First coding, we had these values swapped, but for consistency and prior
696 to the first release of Exim with this authenticator, they've been
697 switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
699 set_exim_authvar_from_prop(sctx, GSASL_GSSAPI_DISPLAY_NAME);
700 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
702 /* In this one case, it perhaps makes sense to default back open?
703 But for consistency, let's just mandate server_condition here too. */
705 cbrc = condition_check(ablock,
706 US"server_condition (GSSAPI family)", ablock->server_condition);
707 checked_server_condition = TRUE;
710 case GSASL_SCRAM_ITER:
711 cbrc = prop_from_option(sctx, prop, ob->server_scram_iter);
714 case GSASL_SCRAM_SALT:
715 cbrc = prop_from_option(sctx, prop, ob->server_scram_salt);
718 #ifdef EXIM_GSASL_SCRAM_S_KEY
719 case GSASL_SCRAM_STOREDKEY:
720 cbrc = prop_from_option(sctx, prop, ob->server_s_key);
723 case GSASL_SCRAM_SERVERKEY:
724 cbrc = prop_from_option(sctx, prop, ob->server_key);
729 /* SCRAM-*: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
730 DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
731 CRAM-MD5: GSASL_AUTHID
732 PLAIN: GSASL_AUTHID and GSASL_AUTHZID
735 set_exim_authvars_from_a_az_r_props(sctx);
737 if (!(s = ob->server_password))
739 HDEBUG(D_auth) debug_printf("option not set\n");
742 if (!(tmps = CS expand_string(s)))
744 sasl_error_should_defer = !f.expand_string_forcedfail;
745 HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
746 "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
747 return GSASL_AUTHENTICATION_ERROR;
749 HDEBUG(D_auth) debug_printf(" set\n");
750 gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
752 /* This is inadequate; don't think Exim's store stacks are geared
753 for memory wiping, so expanding strings will leave stuff laying around.
754 But no need to compound the problem, so get rid of the one we can. */
756 memset(tmps, '\0', strlen(tmps));
761 HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
762 cbrc = GSASL_NO_CALLBACK;
765 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
766 gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
772 /******************************************************************************/
774 #define PROP_OPTIONAL BIT(0)
777 set_client_prop(Gsasl_session * sctx, Gsasl_property prop, uschar * val,
778 unsigned flags, uschar * buffer, int buffsize)
783 if (!val) return !!(flags & PROP_OPTIONAL);
784 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
786 string_format(buffer, buffsize, "%s", expand_string_message);
791 HDEBUG(D_auth) debug_printf("%s: set %s = '%s'\n", __FUNCTION__,
792 gsasl_prop_code_to_name(prop), s);
793 gsasl_property_set(sctx, prop, CS s);
799 /*************************************************
800 * Client entry point *
801 *************************************************/
803 /* For interface, see auths/README */
807 auth_instance *ablock, /* authenticator block */
808 void * sx, /* connection */
809 int timeout, /* command timeout */
810 uschar *buffer, /* buffer for reading response */
811 int buffsize) /* size of buffer */
813 auth_gsasl_options_block *ob =
814 (auth_gsasl_options_block *)(ablock->options_block);
815 Gsasl_session * sctx = NULL;
816 struct callback_exim_state cb_state;
819 int rc, yield = FAIL;
822 debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
823 ablock->name, ob->server_mech);
828 if (tls_out.channelbinding && ob->client_channelbinding)
830 # ifdef EXPERIMENTAL_TLS_RESUME
831 if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
832 { /* per RFC 7677 section 4 */
833 string_format(buffer, buffsize, "%s",
834 "channel binding not usable on resumed TLS without extended-master-secret");
838 # ifdef CHANNELBIND_HACK
839 /* This is a gross hack to get around the library a) requiring that
840 c-b was already set, at the _start() call, and b) caching a b64'd
841 version of the binding then which it never updates. */
843 gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
848 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
850 string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
851 gsasl_strerror_name(rc), gsasl_strerror(rc));
852 HDEBUG(D_auth) debug_printf("%s\n", buffer);
856 cb_state.ablock = ablock;
857 cb_state.currently = CURRENTLY_CLIENT;
858 gsasl_session_hook_set(sctx, &cb_state);
862 if ( !set_client_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD, ob->client_spassword,
865 !set_client_prop(sctx, GSASL_PASSWORD, ob->client_password,
867 || !set_client_prop(sctx, GSASL_AUTHID, ob->client_username,
869 || !set_client_prop(sctx, GSASL_AUTHZID, ob->client_authz,
870 PROP_OPTIONAL, buffer, buffsize)
875 if (tls_out.channelbinding)
876 if (ob->client_channelbinding)
878 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
880 # ifndef CHANNELBIND_HACK
881 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
886 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
890 /* Run the SASL conversation with the server */
897 rc = gsasl_step64(sctx, CS s, CSS &outstr);
900 ? smtp_write_command(sx, SCMD_FLUSH,
901 outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
902 ablock->public_name, outstr) <= 0
904 ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
906 if (outstr && *outstr) free(outstr);
914 if (rc != GSASL_NEEDS_MORE)
918 string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
922 /* expecting a final 2xx from the server, accepting the AUTH */
924 if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
926 break; /* from SASL sequence loop */
929 /* 2xx or 3xx response is acceptable. If 2xx, no further input */
931 if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
932 if (errno == 0 && buffer[0] == '2')
945 const uschar * s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALTED_PASSWORD);
946 if (s) debug_printf(" - SaltedPassword: '%s'\n", s);
954 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
956 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n",
957 gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
960 case GSASL_CB_TLS_UNIQUE:
962 debug_printf(" filling in\n");
963 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
967 debug_printf(" not providing one\n");
970 return GSASL_NO_CALLBACK;
973 /*************************************************
975 *************************************************/
978 auth_gsasl_version_report(FILE *f)
981 runtime = gsasl_check_version(NULL);
982 fprintf(f, "Library version: GNU SASL: Compile: %s\n"
984 GSASL_VERSION, runtime);
990 void auth_gsasl_macros(void) {}
992 #endif /*!MACRO_PREDEF*/
993 #endif /* AUTH_GSASL */
995 /* End of gsasl_exim.c */