Use compressed form of ipv6 in $sender_host_address under -bh. Bug 3027
[exim.git] / src / src / auths / gsasl_exim.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) The Exim Maintainers 2019 - 2023 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
9
10 /* Copyright (c) Twitter Inc 2012
11    Author: Phil Pennock <pdp@exim.org> */
12 /* Copyright (c) Phil Pennock 2012 */
13
14 /* Interface to GNU SASL library for generic authentication. */
15
16 /* Trade-offs:
17
18 GNU SASL does not provide authentication data itself, so we have to expose
19 that decision to configuration.  For some mechanisms, we need to act much
20 like plaintext.  For others, we only need to be able to provide some
21 evaluated data on demand.  There's no abstracted way (ie, without hardcoding
22 knowledge of authenticators here) to know which need what properties; we
23 can't query a session or the library for "we will need these for mechanism X".
24
25 So: we always require server_condition, even if sometimes it will just be
26 set as "yes".  We do provide a number of other hooks, which might not make
27 sense in all contexts.  For some, we can do checks at init time.
28 */
29
30 #include "../exim.h"
31
32 #ifndef AUTH_GSASL
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); }
37 #else
38
39 #include <gsasl.h>
40 #include "gsasl_exim.h"
41
42
43 #if GSASL_VERSION_MAJOR == 2
44
45 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
46 # define EXIM_GSASL_SCRAM_S_KEY
47 # if GSASL_VERSION_MINOR >= 1
48 #  define EXIM_GSASL_HAVE_EXPORTER
49 # elif GSASL_VERSION_PATCH >= 1
50 #  define EXIM_GSASL_HAVE_EXPORTER
51 # endif
52
53 #elif GSASL_VERSION_MAJOR == 1
54 # if GSASL_VERSION_MINOR >= 10
55 #  define EXIM_GSASL_HAVE_SCRAM_SHA_256
56 #  define EXIM_GSASL_SCRAM_S_KEY
57
58 # elif GSASL_VERSION_MINOR == 9
59 #  define EXIM_GSASL_HAVE_SCRAM_SHA_256
60
61 #  if GSASL_VERSION_PATCH >= 1
62 #   define EXIM_GSASL_SCRAM_S_KEY
63 #  endif
64 #  if GSASL_VERSION_PATCH < 2
65 #   define CHANNELBIND_HACK
66 #  endif
67
68 # else
69 #  define CHANNELBIND_HACK
70 # endif
71 #endif
72
73 /* Convenience for testing strings */
74
75 #define STREQIC(Foo, Bar) (strcmpic((Foo), (Bar)) == 0)
76
77
78 /* Authenticator-specific options. */
79 /* I did have server_*_condition options for various mechanisms, but since
80 we only ever handle one mechanism at a time, I didn't see the point in keeping
81 that.  In case someone sees a point, I've left the condition_check() API
82 alone. */
83 #define LOFF(field) OPT_OFF(auth_gsasl_options_block, field)
84
85 optionlist auth_gsasl_options[] = {
86   { "client_authz",             opt_stringptr,  LOFF(client_authz) },
87   { "client_channelbinding",    opt_bool,       LOFF(client_channelbinding) },
88   { "client_password",          opt_stringptr,  LOFF(client_password) },
89   { "client_spassword",         opt_stringptr,  LOFF(client_spassword) },
90   { "client_username",          opt_stringptr,  LOFF(client_username) },
91
92   { "server_channelbinding",    opt_bool,       LOFF(server_channelbinding) },
93   { "server_hostname",          opt_stringptr,  LOFF(server_hostname) },
94 #ifdef EXIM_GSASL_SCRAM_S_KEY
95   { "server_key",               opt_stringptr,  LOFF(server_key) },
96 #endif
97   { "server_mech",              opt_stringptr,  LOFF(server_mech) },
98   { "server_password",          opt_stringptr,  LOFF(server_password) },
99   { "server_realm",             opt_stringptr,  LOFF(server_realm) },
100   { "server_scram_iter",        opt_stringptr,  LOFF(server_scram_iter) },
101   { "server_scram_salt",        opt_stringptr,  LOFF(server_scram_salt) },
102 #ifdef EXIM_GSASL_SCRAM_S_KEY
103   { "server_skey",              opt_stringptr,  LOFF(server_s_key) },
104 #endif
105   { "server_service",           opt_stringptr,  LOFF(server_service) }
106 };
107
108 int auth_gsasl_options_count =
109   sizeof(auth_gsasl_options)/sizeof(optionlist);
110
111 /* Defaults for the authenticator-specific options. */
112 auth_gsasl_options_block auth_gsasl_option_defaults = {
113   .server_service = US"smtp",
114   .server_hostname = US"$primary_hostname",
115   .server_scram_iter = US"4096",
116   /* all others zero/null */
117 };
118
119
120 #ifdef MACRO_PREDEF
121 # include "../macro_predef.h"
122
123 /* Dummy values */
124 void auth_gsasl_init(auth_instance *ablock) {}
125 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
126 int auth_gsasl_client(auth_instance *ablock, void * sx,
127   int timeout, uschar *buffer, int buffsize) {return 0;}
128 gstring * auth_gsasl_version_report(gstring * g) {return NULL;}
129
130 void
131 auth_gsasl_macros(void)
132 {
133 # ifdef EXIM_GSASL_HAVE_SCRAM_SHA_256
134   builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_SHA_256");
135 # endif
136 # ifdef EXIM_GSASL_SCRAM_S_KEY
137   builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_S_KEY");
138 # endif
139 }
140
141 #else   /*!MACRO_PREDEF*/
142
143
144
145 /* "Globals" for managing the gsasl interface. */
146
147 static Gsasl *gsasl_ctx = NULL;
148 static int
149   main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
150 static int
151   server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
152 static int
153   client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
154
155 static BOOL sasl_error_should_defer = FALSE;
156 static Gsasl_property callback_loop = 0;
157 static BOOL checked_server_condition = FALSE;
158
159 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
160
161 struct callback_exim_state {
162   auth_instance *ablock;
163   int currently;
164 };
165
166
167 /*************************************************
168 *          Initialization entry point            *
169 *************************************************/
170
171 /* Called for each instance, after its options have been read, to
172 enable consistency checks to be done, or anything else that needs
173 to be set up. */
174
175 void
176 auth_gsasl_init(auth_instance *ablock)
177 {
178 static char * once = NULL;
179 int rc;
180 auth_gsasl_options_block *ob =
181   (auth_gsasl_options_block *)(ablock->options_block);
182
183 /* As per existing Cyrus glue, use the authenticator's public name as
184 the default for the mechanism name; we don't handle multiple mechanisms
185 in one authenticator, but the same driver can be used multiple times. */
186
187 if (!ob->server_mech)
188   ob->server_mech = string_copy(ablock->public_name);
189
190 /* Can get multiple session contexts from one library context, so just
191 initialise the once. */
192
193 if (!gsasl_ctx)
194   {
195   if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
196     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
197               "couldn't initialise GNU SASL library: %s (%s)",
198               ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
199
200   gsasl_callback_set(gsasl_ctx, main_callback);
201   }
202
203 /* We don't need this except to log it for debugging. */
204
205 HDEBUG(D_auth) if (!once)
206   {
207   if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
208     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
209               "failed to retrieve list of mechanisms: %s (%s)",
210               ablock->name,  gsasl_strerror_name(rc), gsasl_strerror(rc));
211
212   debug_printf("GNU SASL supports: %s\n", once);
213   }
214
215 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
216   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
217             "GNU SASL does not support mechanism \"%s\"",
218             ablock->name, ob->server_mech);
219
220 if (ablock->server_condition)
221   ablock->server = TRUE;
222 else if(  ob->server_mech
223        && !STREQIC(ob->server_mech, US"EXTERNAL")
224        && !STREQIC(ob->server_mech, US"ANONYMOUS")
225        && !STREQIC(ob->server_mech, US"PLAIN")
226        && !STREQIC(ob->server_mech, US"LOGIN")
227        )
228   {
229   /* At present, for mechanisms we don't panic on absence of server_condition;
230   need to figure out the most generically correct approach to deciding when
231   it's critical and when it isn't.  Eg, for simple validation (PLAIN mechanism,
232   etc) it clearly is critical.
233   */
234
235   ablock->server = FALSE;
236   HDEBUG(D_auth) debug_printf("%s authenticator:  "
237             "Need server_condition for %s mechanism\n",
238             ablock->name, ob->server_mech);
239   }
240
241 /* This does *not* scale to new SASL mechanisms.  Need a better way to ask
242 which properties will be needed. */
243
244 if (  !ob->server_realm
245    && STREQIC(ob->server_mech, US"DIGEST-MD5"))
246   {
247   ablock->server = FALSE;
248   HDEBUG(D_auth) debug_printf("%s authenticator:  "
249             "Need server_realm for %s mechanism\n",
250             ablock->name, ob->server_mech);
251   }
252
253 ablock->client = ob->client_username && ob->client_password;
254 }
255
256
257 /* GNU SASL uses one top-level callback, registered at library level.
258 We dispatch to client and server functions instead. */
259
260 static int
261 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
262 {
263 int rc = 0;
264 struct callback_exim_state *cb_state =
265   (struct callback_exim_state *)gsasl_session_hook_get(sctx);
266
267 if (!cb_state)
268   {
269   HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
270 #ifdef CHANNELBIND_HACK
271   if (prop == GSASL_CB_TLS_UNIQUE)
272     {
273     uschar * s;
274     if ((s = gsasl_callback_hook_get(ctx)))     /* Gross hack for early lib vers */
275       {
276       HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
277       gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
278       }
279     else
280       {
281       HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE!  dummy for now\n");
282       gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
283       }
284     return GSASL_OK;
285     }
286 #endif
287   return GSASL_NO_CALLBACK;
288   }
289
290 HDEBUG(D_auth)
291   debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
292       prop, callback_loop);
293
294 if (callback_loop > 0)
295   {
296   /* Most likely is that we were asked for property FOO, and to
297   expand the string we asked for property BAR to put into an auth
298   variable, but property BAR is not supplied for this mechanism. */
299   HDEBUG(D_auth)
300     debug_printf("Loop, asked for property %d while handling property %d\n",
301         prop, callback_loop);
302   return GSASL_NO_CALLBACK;
303   }
304 callback_loop = prop;
305
306 if (cb_state->currently == CURRENTLY_CLIENT)
307   rc = client_callback(ctx, sctx, prop, cb_state->ablock);
308 else if (cb_state->currently == CURRENTLY_SERVER)
309   rc = server_callback(ctx, sctx, prop, cb_state->ablock);
310 else
311   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
312       "unhandled callback state, bug in Exim", cb_state->ablock->name);
313   /* NOTREACHED */
314
315 callback_loop = 0;
316 return rc;
317 }
318
319
320 /*************************************************
321 *             Debug service function             *
322 *************************************************/
323 static const uschar * 
324 gsasl_prop_code_to_name(Gsasl_property prop)
325 {
326 switch (prop)
327   {
328   case GSASL_AUTHID:                    return US"AUTHID";
329   case GSASL_AUTHZID:                   return US"AUTHZID";
330   case GSASL_PASSWORD:                  return US"PASSWORD";
331   case GSASL_ANONYMOUS_TOKEN:           return US"ANONYMOUS_TOKEN";
332   case GSASL_SERVICE:                   return US"SERVICE";
333   case GSASL_HOSTNAME:                  return US"HOSTNAME";
334   case GSASL_GSSAPI_DISPLAY_NAME:       return US"GSSAPI_DISPLAY_NAME";
335   case GSASL_PASSCODE:                  return US"PASSCODE";
336   case GSASL_SUGGESTED_PIN:             return US"SUGGESTED_PIN";
337   case GSASL_PIN:                       return US"PIN";
338   case GSASL_REALM:                     return US"REALM";
339   case GSASL_DIGEST_MD5_HASHED_PASSWORD:        return US"DIGEST_MD5_HASHED_PASSWORD";
340   case GSASL_QOPS:                      return US"QOPS";
341   case GSASL_QOP:                       return US"QOP";
342   case GSASL_SCRAM_ITER:                return US"SCRAM_ITER";
343   case GSASL_SCRAM_SALT:                return US"SCRAM_SALT";
344   case GSASL_SCRAM_SALTED_PASSWORD:     return US"SCRAM_SALTED_PASSWORD";
345 #ifdef EXIM_GSASL_SCRAM_S_KEY
346   case GSASL_SCRAM_STOREDKEY:           return US"SCRAM_STOREDKEY";
347   case GSASL_SCRAM_SERVERKEY:           return US"SCRAM_SERVERKEY";
348 #endif
349 #ifdef EXIM_GSASL_HAVE_EXPORTER         /* v. 2.1.0 */
350   case GSASL_CB_TLS_EXPORTER:           return US"CB_TLS_EXPORTER";
351 #endif
352   case GSASL_CB_TLS_UNIQUE:             return US"CB_TLS_UNIQUE";
353   case GSASL_SAML20_IDP_IDENTIFIER:     return US"SAML20_IDP_IDENTIFIER";
354   case GSASL_SAML20_REDIRECT_URL:       return US"SAML20_REDIRECT_URL";
355   case GSASL_OPENID20_REDIRECT_URL:     return US"OPENID20_REDIRECT_URL";
356   case GSASL_OPENID20_OUTCOME_DATA:     return US"OPENID20_OUTCOME_DATA";
357   case GSASL_SAML20_AUTHENTICATE_IN_BROWSER:    return US"SAML20_AUTHENTICATE_IN_BROWSER";
358   case GSASL_OPENID20_AUTHENTICATE_IN_BROWSER:  return US"OPENID20_AUTHENTICATE_IN_BROWSER";
359   case GSASL_VALIDATE_SIMPLE:           return US"VALIDATE_SIMPLE";
360   case GSASL_VALIDATE_EXTERNAL:         return US"VALIDATE_EXTERNAL";
361   case GSASL_VALIDATE_ANONYMOUS:        return US"VALIDATE_ANONYMOUS";
362   case GSASL_VALIDATE_GSSAPI:           return US"VALIDATE_GSSAPI";
363   case GSASL_VALIDATE_SECURID:          return US"VALIDATE_SECURID";
364   case GSASL_VALIDATE_SAML20:           return US"VALIDATE_SAML20";
365   case GSASL_VALIDATE_OPENID20:         return US"VALIDATE_OPENID20";
366   }
367 return CUS string_sprintf("(unknown prop: %d)", (int)prop);
368 }
369
370 static void
371 preload_prop(Gsasl_session * sctx, Gsasl_property propcode, const uschar * val)
372 {
373 DEBUG(D_auth) debug_printf("preloading prop %s val %s\n",
374   gsasl_prop_code_to_name(propcode), val);
375 gsasl_property_set(sctx, propcode, CCS val);
376 }
377
378 /*************************************************
379 *             Server entry point                 *
380 *************************************************/
381
382 /* For interface, see auths/README */
383
384 int
385 auth_gsasl_server(auth_instance * ablock, uschar * initial_data)
386 {
387 uschar * tmps;
388 char * to_send, * received;
389 Gsasl_session * sctx = NULL;
390 auth_gsasl_options_block * ob =
391   (auth_gsasl_options_block *)(ablock->options_block);
392 struct callback_exim_state cb_state;
393 int rc, auth_result, exim_error, exim_error_override;
394
395 HDEBUG(D_auth)
396   debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
397       ablock->name, ob->server_mech);
398
399 #ifndef DISABLE_TLS
400 if (tls_in.channelbinding && ob->server_channelbinding)
401   {
402 # ifndef DISABLE_TLS_RESUME
403   if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
404     {           /* per RFC 7677 section 4 */
405     HDEBUG(D_auth) debug_printf(
406       "channel binding not usable on resumed TLS without extended-master-secret");
407     return FAIL;
408     }
409 # endif
410 # ifdef CHANNELBIND_HACK
411 /* This is a gross hack to get around the library before 1.9.2
412 a) requiring that c-b was already set, at the _start() call, and
413 b) caching a b64'd version of the binding then which it never updates. */
414
415   gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
416 # endif
417   }
418 #endif
419
420 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
421   {
422   auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
423       gsasl_strerror_name(rc), gsasl_strerror(rc));
424   HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
425   return DEFER;
426   }
427 /* Hereafter: gsasl_finish(sctx) please */
428
429 cb_state.ablock = ablock;
430 cb_state.currently = CURRENTLY_SERVER;
431 gsasl_session_hook_set(sctx, &cb_state);
432
433 tmps = expand_string(ob->server_service);
434 preload_prop(sctx, GSASL_SERVICE, tmps);
435 tmps = expand_string(ob->server_hostname);
436 preload_prop(sctx, GSASL_HOSTNAME, tmps);
437 if (ob->server_realm)
438   {
439   tmps = expand_string(ob->server_realm);
440   if (tmps && *tmps)
441     preload_prop(sctx, GSASL_REALM, tmps);
442   }
443 /* We don't support protection layers. */
444 preload_prop(sctx, GSASL_QOPS, US "qop-auth");
445
446 #ifndef DISABLE_TLS
447 if (tls_in.channelbinding)
448   {
449   /* Some auth mechanisms can ensure that both sides are talking withing the
450   same security context; for TLS, this means that even if a bad certificate
451   has been accepted, they remain MitM-proof because both sides must be within
452   the same negotiated session; if someone is terminating one session and
453   proxying data on within a second, authentication will fail.
454
455   We might not have this available, depending upon TLS implementation,
456   ciphersuite, phase of moon ...
457
458   If we do, it results in extra SASL mechanisms being available; here,
459   Exim's one-mechanism-per-authenticator potentially causes problems.
460   It depends upon how GNU SASL will implement the PLUS variants of GS2
461   and whether it automatically mandates a switch to the bound PLUS
462   if the data is available.  Since default-on, despite being more secure,
463   would then result in mechanism name changes on a library update, we
464   have little choice but to default it off and let the admin choose to
465   enable it.  *sigh*
466
467   Earlier library versions need this set early, during the _start() call,
468   so we had to misuse gsasl_callback_hook_set/get() as a data transfer
469   mech for the callback done at that time to get the bind-data.  More recently
470   the callback is done (if needed) during the first gsasl_stop().  We know
471   the bind-data here so can set it (and should not get a callback).
472   */
473   if (ob->server_channelbinding)
474     {
475     HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
476         ablock->name);
477 # ifndef CHANNELBIND_HACK
478     preload_prop(sctx,
479 #  ifdef EXIM_GSASL_HAVE_EXPORTER
480       tls_in.channelbind_exporter ? GSASL_CB_TLS_EXPORTER :
481 #  endif
482                                     GSASL_CB_TLS_UNIQUE,
483       tls_in.channelbinding);
484 # endif
485     }
486   else
487     HDEBUG(D_auth)
488       debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
489           ablock->name);
490   }
491 else
492   HDEBUG(D_auth)
493     debug_printf("Auth %s: no channel-binding data available\n",
494         ablock->name);
495 #endif
496
497 checked_server_condition = FALSE;
498
499 received = CS initial_data;
500 to_send = NULL;
501 exim_error = exim_error_override = OK;
502
503 do {
504   switch (rc = gsasl_step64(sctx, received, &to_send))
505     {
506     case GSASL_OK:
507       if (!to_send)
508         goto STOP_INTERACTION;
509       break;
510
511     case GSASL_NEEDS_MORE:
512       break;
513
514     case GSASL_AUTHENTICATION_ERROR:
515     case GSASL_INTEGRITY_ERROR:
516     case GSASL_NO_AUTHID:
517     case GSASL_NO_ANONYMOUS_TOKEN:
518     case GSASL_NO_AUTHZID:
519     case GSASL_NO_PASSWORD:
520     case GSASL_NO_PASSCODE:
521     case GSASL_NO_PIN:
522     case GSASL_BASE64_ERROR:
523       HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
524           gsasl_strerror_name(rc), gsasl_strerror(rc));
525       log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
526           "GNU SASL permanent failure: %s (%s)",
527           ablock->name, ob->server_mech,
528           gsasl_strerror_name(rc), gsasl_strerror(rc));
529       if (rc == GSASL_BASE64_ERROR)
530         exim_error_override = BAD64;
531       goto STOP_INTERACTION;
532
533     default:
534       auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
535           gsasl_strerror_name(rc), gsasl_strerror(rc));
536       HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
537       exim_error_override = DEFER;
538       goto STOP_INTERACTION;
539     }
540
541   /*XXX having our caller send the final smtp "235" is unfortunate; wastes a roundtrip */
542   if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
543     exim_error = auth_get_no64_data(USS &received, US to_send);
544
545   if (to_send)
546     {
547     free(to_send);
548     to_send = NULL;
549     }
550
551   if (exim_error)
552     break; /* handles * cancelled check */
553
554   } while (rc == GSASL_NEEDS_MORE);
555
556 STOP_INTERACTION:
557 auth_result = rc;
558
559 HDEBUG(D_auth)
560   {
561   const uschar * s;
562   if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_ITER)))
563     debug_printf(" - itercnt:   '%s'\n", s);
564   if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALT)))
565     debug_printf(" - salt:      '%s'\n", s);
566 #ifdef EXIM_GSASL_SCRAM_S_KEY
567   if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SERVERKEY)))
568     debug_printf(" - ServerKey: '%s'\n", s);
569   if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_STOREDKEY)))
570     debug_printf(" - StoredKey: '%s'\n", s);
571 #endif
572   }
573
574 gsasl_finish(sctx);
575
576 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
577
578 if (exim_error != OK)
579   return exim_error;
580
581 if (auth_result != GSASL_OK)
582   {
583   HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
584       gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
585   if (exim_error_override != OK)
586     return exim_error_override; /* might be DEFER */
587   if (sasl_error_should_defer) /* overriding auth failure SASL error */
588     return DEFER;
589   return FAIL;
590   }
591
592 /* Auth succeeded, check server_condition unless already done in callback */
593 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
594 }
595
596
597 /* returns the GSASL status of expanding the Exim string given */
598 static int
599 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
600 {
601 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
602 switch (exim_rc)
603   {
604   case OK:      return GSASL_OK;
605   case DEFER:   sasl_error_should_defer = TRUE;
606                 return GSASL_AUTHENTICATION_ERROR;
607   case FAIL:    return GSASL_AUTHENTICATION_ERROR;
608   default:      log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
609                   "Unhandled return from checking %s: %d",
610                   ablock->name, label, exim_rc);
611   }
612
613 /* NOTREACHED */
614 return GSASL_AUTHENTICATION_ERROR;
615 }
616
617
618 /* Set the "next" $auth[n] and increment expand_nmax */
619
620 static void
621 set_exim_authvar_from_prop(Gsasl_session * sctx, Gsasl_property prop)
622 {
623 uschar * propval = US gsasl_property_fast(sctx, prop);
624 int i = expand_nmax, j = i + 1;
625 propval = propval ? string_copy(propval) : US"";
626 HDEBUG(D_auth) debug_printf("auth[%d] <=  %s'%s'\n",
627                             j, gsasl_prop_code_to_name(prop), propval);
628 expand_nstring[j] = propval;
629 expand_nlength[j] = Ustrlen(propval);
630 if (i < AUTH_VARS) auth_vars[i] = propval;
631 expand_nmax = j;
632 }
633
634 static void
635 set_exim_authvars_from_a_az_r_props(Gsasl_session * sctx)
636 {
637 if (expand_nmax > 0 ) return;
638
639 /* Asking for GSASL_AUTHZID calls back into us if we use
640 gsasl_property_get(), thus the use of gsasl_property_fast().
641 Do we really want to hardcode limits per mechanism?  What happens when
642 a new mechanism is added to the library.  It *shouldn't* result in us
643 needing to add more glue, since avoiding that is a large part of the
644 point of SASL. */
645
646 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
647 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
648 set_exim_authvar_from_prop(sctx, GSASL_REALM);
649 }
650
651
652 static int
653 prop_from_option(Gsasl_session * sctx, Gsasl_property prop,
654   const uschar * option)
655 {
656 HDEBUG(D_auth) debug_printf(" %s\n", gsasl_prop_code_to_name(prop));
657 if (option)
658   {
659   set_exim_authvars_from_a_az_r_props(sctx);
660   option = expand_cstring(option);
661   HDEBUG(D_auth) debug_printf("  '%s'\n", option);
662   if (*option)
663     gsasl_property_set(sctx, prop, CCS option);
664   return GSASL_OK;
665   }
666 HDEBUG(D_auth) debug_printf("  option not set\n");
667 return GSASL_NO_CALLBACK;
668 }
669
670 static int
671 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
672   auth_instance *ablock)
673 {
674 char * tmps;
675 uschar * s;
676 int cbrc = GSASL_NO_CALLBACK;
677 auth_gsasl_options_block * ob =
678   (auth_gsasl_options_block *)(ablock->options_block);
679
680 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as server\n",
681             gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
682
683 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
684 expand_nmax = 0;
685
686 switch (prop)
687   {
688   case GSASL_VALIDATE_SIMPLE:
689     /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
690     set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
691     set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
692     set_exim_authvar_from_prop(sctx, GSASL_PASSWORD);
693
694     cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
695     checked_server_condition = TRUE;
696     break;
697
698   case GSASL_VALIDATE_EXTERNAL:
699     if (!ablock->server_condition)
700       {
701       HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
702       cbrc = GSASL_AUTHENTICATION_ERROR;
703       break;
704       }
705     set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
706
707     cbrc = condition_check(ablock,
708         US"server_condition (EXTERNAL)", ablock->server_condition);
709     checked_server_condition = TRUE;
710     break;
711
712   case GSASL_VALIDATE_ANONYMOUS:
713     if (!ablock->server_condition)
714       {
715       HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
716       cbrc = GSASL_AUTHENTICATION_ERROR;
717       break;
718       }
719     set_exim_authvar_from_prop(sctx, GSASL_ANONYMOUS_TOKEN);
720
721     cbrc = condition_check(ablock,
722         US"server_condition (ANONYMOUS)", ablock->server_condition);
723     checked_server_condition = TRUE;
724     break;
725
726   case GSASL_VALIDATE_GSSAPI:
727     /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
728     The display-name is authenticated as part of GSS, the authzid is claimed
729     by the SASL integration after authentication; protected against tampering
730     (if the SASL mechanism supports that, which Kerberos does) but is
731     unverified, same as normal for other mechanisms.
732      First coding, we had these values swapped, but for consistency and prior
733     to the first release of Exim with this authenticator, they've been
734     switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
735
736     set_exim_authvar_from_prop(sctx, GSASL_GSSAPI_DISPLAY_NAME);
737     set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
738
739     /* In this one case, it perhaps makes sense to default back open?
740     But for consistency, let's just mandate server_condition here too. */
741
742     cbrc = condition_check(ablock,
743         US"server_condition (GSSAPI family)", ablock->server_condition);
744     checked_server_condition = TRUE;
745     break;
746
747   case GSASL_SCRAM_ITER:
748     cbrc = prop_from_option(sctx, prop, ob->server_scram_iter);
749     break;
750
751   case GSASL_SCRAM_SALT:
752     cbrc = prop_from_option(sctx, prop, ob->server_scram_salt);
753     break;
754
755 #ifdef EXIM_GSASL_SCRAM_S_KEY
756   case GSASL_SCRAM_STOREDKEY:
757     cbrc = prop_from_option(sctx, prop, ob->server_s_key);
758     break;
759
760   case GSASL_SCRAM_SERVERKEY:
761     cbrc = prop_from_option(sctx, prop, ob->server_key);
762     break;
763 #endif
764
765   case GSASL_PASSWORD:
766     /* SCRAM-*: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
767        DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
768        CRAM-MD5: GSASL_AUTHID
769        PLAIN: GSASL_AUTHID and GSASL_AUTHZID
770        LOGIN: GSASL_AUTHID
771      */
772     set_exim_authvars_from_a_az_r_props(sctx);
773
774     if (!(s = ob->server_password))
775       {
776       HDEBUG(D_auth) debug_printf("option not set\n");
777       break;
778       }
779     if (!(tmps = CS expand_string(s)))
780       {
781       sasl_error_should_defer = !f.expand_string_forcedfail;
782       HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
783           "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
784       return GSASL_AUTHENTICATION_ERROR;
785       }
786     HDEBUG(D_auth) debug_printf("  set\n");
787     gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
788
789     /* This is inadequate; don't think Exim's store stacks are geared
790     for memory wiping, so expanding strings will leave stuff laying around.
791     But no need to compound the problem, so get rid of the one we can. */
792
793     if (US tmps != s) memset(tmps, '\0', strlen(tmps));
794     cbrc = GSASL_OK;
795     break;
796
797   default:
798     HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
799     cbrc = GSASL_NO_CALLBACK;
800   }
801
802 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
803     gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
804
805 return cbrc;
806 }
807
808
809 /******************************************************************************/
810
811 #define PROP_OPTIONAL   BIT(0)
812
813 static BOOL
814 set_client_prop(Gsasl_session * sctx, Gsasl_property prop, uschar * val,
815   unsigned flags, uschar * buffer, int buffsize)
816 {
817 uschar * s;
818
819 if (!val) return !!(flags & PROP_OPTIONAL);
820 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
821   {
822   string_format(buffer, buffsize, "%s", expand_string_message);
823   return FALSE;
824   }
825 if (*s)
826   {
827   HDEBUG(D_auth) debug_printf("%s: set %s = '%s'\n", __FUNCTION__,
828     gsasl_prop_code_to_name(prop), s);
829   gsasl_property_set(sctx, prop, CS s);
830   }
831
832 return TRUE;
833 }
834
835 /*************************************************
836 *              Client entry point                *
837 *************************************************/
838
839 /* For interface, see auths/README */
840
841 int
842 auth_gsasl_client(
843   auth_instance * ablock,               /* authenticator block */
844   void * sx,                            /* connection */
845   int timeout,                          /* command timeout */
846   uschar * buffer,                      /* buffer for reading response */
847   int buffsize)                         /* size of buffer */
848 {
849 auth_gsasl_options_block * ob =
850   (auth_gsasl_options_block *)(ablock->options_block);
851 Gsasl_session * sctx = NULL;
852 struct callback_exim_state cb_state;
853 uschar * s;
854 BOOL initial = TRUE;
855 int rc, yield = FAIL;
856
857 HDEBUG(D_auth)
858   debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
859       ablock->name, ob->server_mech);
860
861 *buffer = 0;
862
863 #ifndef DISABLE_TLS
864 if (tls_out.channelbinding && ob->client_channelbinding)
865   {
866 # ifndef DISABLE_TLS_RESUME
867   if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
868     {   /* Per RFC 7677 section 4.  See also RFC 7627, "Triple Handshake"
869         vulnerability, and https://www.mitls.org/pages/attacks/3SHAKE */
870     string_format(buffer, buffsize, "%s",
871       "channel binding not usable on resumed TLS without extended-master-secret");
872     return FAIL;
873     }
874 # endif
875 # ifdef CHANNELBIND_HACK
876   /* This is a gross hack to get around the library before 1.9.2
877   a) requiring that c-b was already set, at the _start() call, and
878   b) caching a b64'd version of the binding then which it never updates. */
879
880   gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
881 # endif
882   }
883 #endif
884
885 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
886   {
887   string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
888       gsasl_strerror_name(rc), gsasl_strerror(rc));
889   HDEBUG(D_auth) debug_printf("%s\n", buffer);
890   return ERROR;
891   }
892
893 cb_state.ablock = ablock;
894 cb_state.currently = CURRENTLY_CLIENT;
895 gsasl_session_hook_set(sctx, &cb_state);
896
897 /* Set properties */
898
899 if (  !set_client_prop(sctx, GSASL_PASSWORD, ob->client_password,
900                   0, buffer, buffsize)
901    || !set_client_prop(sctx, GSASL_AUTHID, ob->client_username,
902                   0, buffer, buffsize)
903    || !set_client_prop(sctx, GSASL_AUTHZID, ob->client_authz,
904                   PROP_OPTIONAL, buffer, buffsize)
905    )
906   return ERROR;
907
908 #ifndef DISABLE_TLS
909 if (tls_out.channelbinding)
910   if (ob->client_channelbinding)
911     {
912     HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
913         ablock->name);
914 # ifndef CHANNELBIND_HACK
915     preload_prop(sctx,
916 #  ifdef EXIM_GSASL_HAVE_EXPORTER
917       tls_out.channelbind_exporter ? GSASL_CB_TLS_EXPORTER :
918 #  endif
919                                      GSASL_CB_TLS_UNIQUE,
920       tls_out.channelbinding);
921 # endif
922     }
923   else
924     HDEBUG(D_auth)
925       debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
926           ablock->name);
927 #endif
928
929 /* Run the SASL conversation with the server */
930
931 for(s = NULL; ;)
932   {
933   uschar * outstr;
934   BOOL fail = TRUE;
935
936   rc = gsasl_step64(sctx, CS s, CSS &outstr);
937
938   if (rc == GSASL_NEEDS_MORE || rc == GSASL_OK)
939     {
940     fail = initial
941       ? smtp_write_command(sx, SCMD_FLUSH,
942                           outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
943                           ablock->public_name, outstr) <= 0
944       : outstr
945       ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
946       : FALSE;
947     free(outstr);
948     if (fail)
949       {
950       yield = FAIL_SEND;
951       goto done;
952       }
953     initial = FALSE;
954     }
955
956   if (rc != GSASL_NEEDS_MORE)
957     {
958     if (rc != GSASL_OK)
959       {
960       string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
961       break;
962       }
963
964     /* expecting a final 2xx from the server, accepting the AUTH */
965
966     if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
967       yield = OK;
968     break;      /* from SASL sequence loop */
969     }
970
971   /* 2xx or 3xx response is acceptable.  If 2xx, no further input */
972
973   if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
974     if (errno == 0 && buffer[0] == '2')
975       buffer[4] = '\0';
976     else
977       {
978       yield = FAIL;
979       goto done;
980       }
981   s = buffer + 4;
982   }
983
984 done:
985 if (yield == OK)
986   {
987   expand_nmax = 0;
988   set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
989   set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
990   set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
991   set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD);
992   }
993
994 gsasl_finish(sctx);
995 return yield;
996 }
997
998 static int
999 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
1000 {
1001 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n",
1002             gsasl_prop_code_to_name(prop), ablock->name, ablock->public_name);
1003 switch (prop)
1004   {
1005 #ifdef EXIM_GSASL_HAVE_EXPORTER
1006   case GSASL_CB_TLS_EXPORTER:   /* Should never get called for this, as pre-set */
1007     if (!tls_out.channelbind_exporter) break;
1008     HDEBUG(D_auth) debug_printf(" filling in\n");
1009     gsasl_property_set(sctx, GSASL_CB_TLS_EXPORTER, CCS tls_out.channelbinding);
1010     return GSASL_OK;
1011 #endif
1012   case GSASL_CB_TLS_UNIQUE:     /* Should never get called for this, as pre-set */
1013 #ifdef EXIM_GSASL_HAVE_EXPORTER
1014     if (tls_out.channelbind_exporter) break;
1015 #endif
1016     HDEBUG(D_auth) debug_printf(" filling in\n");
1017     gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
1018     return GSASL_OK;
1019   case GSASL_SCRAM_SALTED_PASSWORD:
1020     {
1021     uschar * client_spassword =
1022       ((auth_gsasl_options_block *) ablock->options_block)->client_spassword;
1023     uschar dummy[4];
1024     HDEBUG(D_auth) if (!client_spassword)
1025       debug_printf(" client_spassword option unset\n");
1026     if (client_spassword)
1027       {
1028       expand_nmax = 0;
1029       set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
1030       set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
1031       set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
1032       set_client_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD, client_spassword,
1033                   0, dummy, sizeof(dummy));
1034       for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
1035       expand_nmax = 0;
1036       }
1037     break;
1038     }
1039   default:
1040     HDEBUG(D_auth)
1041       debug_printf(" not providing one\n");
1042     break;
1043   }
1044 return GSASL_NO_CALLBACK;
1045 }
1046
1047 /*************************************************
1048 *                Diagnostic API                  *
1049 *************************************************/
1050
1051 gstring *
1052 auth_gsasl_version_report(gstring * g)
1053 {
1054 return string_fmt_append(g, "Library version: GNU SASL: Compile: %s\n"
1055                             "                           Runtime: %s\n",
1056         GSASL_VERSION, gsasl_check_version(NULL));
1057 }
1058
1059
1060
1061 /* Dummy */
1062 void auth_gsasl_macros(void) {}
1063
1064 #endif   /*!MACRO_PREDEF*/
1065 #endif  /* AUTH_GSASL */
1066
1067 /* End of gsasl_exim.c */