55ac15b4bafd8a7f7fe8c5f859f6b02ba8cbdbed
[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(driver_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(driver_instance * a)
177 {
178 auth_instance * ablock = (auth_instance *)a;
179 auth_gsasl_options_block * ob = a->options_block;
180 static char * once = NULL;
181 int rc;
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               a->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               a->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             a->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             a->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             a->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->drinst.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 auth_gsasl_options_block * ob = ablock->drinst.options_block;
388 const uschar * auname = ablock->drinst.name;
389 uschar * tmps;
390 char * to_send, * received;
391 Gsasl_session * sctx = NULL;
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       auname, 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         auname);
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           auname);
490   }
491 else
492   HDEBUG(D_auth)
493     debug_printf("Auth %s: no channel-binding data available\n",
494         auname);
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           auname, 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->drinst.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 auth_gsasl_options_block * ob = ablock->drinst.options_block;
675 char * tmps;
676 uschar * s;
677 int cbrc = GSASL_NO_CALLBACK;
678
679 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as server\n",
680       gsasl_prop_code_to_name(prop), ablock->drinst.name, ablock->public_name);
681
682 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
683 expand_nmax = 0;
684
685 switch (prop)
686   {
687   case GSASL_VALIDATE_SIMPLE:
688     /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
689     set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
690     set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
691     set_exim_authvar_from_prop(sctx, GSASL_PASSWORD);
692
693     cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
694     checked_server_condition = TRUE;
695     break;
696
697   case GSASL_VALIDATE_EXTERNAL:
698     if (!ablock->server_condition)
699       {
700       HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
701       cbrc = GSASL_AUTHENTICATION_ERROR;
702       break;
703       }
704     set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
705
706     cbrc = condition_check(ablock,
707         US"server_condition (EXTERNAL)", ablock->server_condition);
708     checked_server_condition = TRUE;
709     break;
710
711   case GSASL_VALIDATE_ANONYMOUS:
712     if (!ablock->server_condition)
713       {
714       HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
715       cbrc = GSASL_AUTHENTICATION_ERROR;
716       break;
717       }
718     set_exim_authvar_from_prop(sctx, GSASL_ANONYMOUS_TOKEN);
719
720     cbrc = condition_check(ablock,
721         US"server_condition (ANONYMOUS)", ablock->server_condition);
722     checked_server_condition = TRUE;
723     break;
724
725   case GSASL_VALIDATE_GSSAPI:
726     /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
727     The display-name is authenticated as part of GSS, the authzid is claimed
728     by the SASL integration after authentication; protected against tampering
729     (if the SASL mechanism supports that, which Kerberos does) but is
730     unverified, same as normal for other mechanisms.
731      First coding, we had these values swapped, but for consistency and prior
732     to the first release of Exim with this authenticator, they've been
733     switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
734
735     set_exim_authvar_from_prop(sctx, GSASL_GSSAPI_DISPLAY_NAME);
736     set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
737
738     /* In this one case, it perhaps makes sense to default back open?
739     But for consistency, let's just mandate server_condition here too. */
740
741     cbrc = condition_check(ablock,
742         US"server_condition (GSSAPI family)", ablock->server_condition);
743     checked_server_condition = TRUE;
744     break;
745
746   case GSASL_SCRAM_ITER:
747     cbrc = prop_from_option(sctx, prop, ob->server_scram_iter);
748     break;
749
750   case GSASL_SCRAM_SALT:
751     cbrc = prop_from_option(sctx, prop, ob->server_scram_salt);
752     break;
753
754 #ifdef EXIM_GSASL_SCRAM_S_KEY
755   case GSASL_SCRAM_STOREDKEY:
756     cbrc = prop_from_option(sctx, prop, ob->server_s_key);
757     break;
758
759   case GSASL_SCRAM_SERVERKEY:
760     cbrc = prop_from_option(sctx, prop, ob->server_key);
761     break;
762 #endif
763
764   case GSASL_PASSWORD:
765     /* SCRAM-*: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
766        DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
767        CRAM-MD5: GSASL_AUTHID
768        PLAIN: GSASL_AUTHID and GSASL_AUTHZID
769        LOGIN: GSASL_AUTHID
770      */
771     set_exim_authvars_from_a_az_r_props(sctx);
772
773     if (!(s = ob->server_password))
774       {
775       HDEBUG(D_auth) debug_printf("option not set\n");
776       break;
777       }
778     if (!(tmps = CS expand_string(s)))
779       {
780       sasl_error_should_defer = !f.expand_string_forcedfail;
781       HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
782           "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
783       return GSASL_AUTHENTICATION_ERROR;
784       }
785     HDEBUG(D_auth) debug_printf("  set\n");
786     gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
787
788     /* This is inadequate; don't think Exim's store stacks are geared
789     for memory wiping, so expanding strings will leave stuff laying around.
790     But no need to compound the problem, so get rid of the one we can. */
791
792     if (US tmps != s) memset(tmps, '\0', strlen(tmps));
793     cbrc = GSASL_OK;
794     break;
795
796   default:
797     HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
798     cbrc = GSASL_NO_CALLBACK;
799   }
800
801 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
802     gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
803
804 return cbrc;
805 }
806
807
808 /******************************************************************************/
809
810 #define PROP_OPTIONAL   BIT(0)
811
812 static BOOL
813 set_client_prop(Gsasl_session * sctx, Gsasl_property prop, uschar * val,
814   unsigned flags, uschar * buffer, int buffsize)
815 {
816 uschar * s;
817
818 if (!val) return !!(flags & PROP_OPTIONAL);
819 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
820   {
821   string_format(buffer, buffsize, "%s", expand_string_message);
822   return FALSE;
823   }
824 if (*s)
825   {
826   HDEBUG(D_auth) debug_printf("%s: set %s = '%s'\n", __FUNCTION__,
827     gsasl_prop_code_to_name(prop), s);
828   gsasl_property_set(sctx, prop, CS s);
829   }
830
831 return TRUE;
832 }
833
834 /*************************************************
835 *              Client entry point                *
836 *************************************************/
837
838 /* For interface, see auths/README */
839
840 int
841 auth_gsasl_client(
842   auth_instance * ablock,               /* authenticator block */
843   void * sx,                            /* connection */
844   int timeout,                          /* command timeout */
845   uschar * buffer,                      /* buffer for reading response */
846   int buffsize)                         /* size of buffer */
847 {
848 auth_gsasl_options_block * ob = ablock->drinst.options_block;
849 const uschar * auname = ablock->drinst.name;
850 Gsasl_session * sctx = NULL;
851 struct callback_exim_state cb_state;
852 uschar * s;
853 BOOL initial = TRUE;
854 int rc, yield = FAIL;
855
856 HDEBUG(D_auth)
857   debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
858       auname, ob->server_mech);
859
860 *buffer = 0;
861
862 #ifndef DISABLE_TLS
863 if (tls_out.channelbinding && ob->client_channelbinding)
864   {
865 # ifndef DISABLE_TLS_RESUME
866   if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED)
867     {   /* Per RFC 7677 section 4.  See also RFC 7627, "Triple Handshake"
868         vulnerability, and https://www.mitls.org/pages/attacks/3SHAKE */
869     string_format(buffer, buffsize, "%s",
870       "channel binding not usable on resumed TLS without extended-master-secret");
871     return FAIL;
872     }
873 # endif
874 # ifdef CHANNELBIND_HACK
875   /* This is a gross hack to get around the library before 1.9.2
876   a) requiring that c-b was already set, at the _start() call, and
877   b) caching a b64'd version of the binding then which it never updates. */
878
879   gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
880 # endif
881   }
882 #endif
883
884 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
885   {
886   string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
887       gsasl_strerror_name(rc), gsasl_strerror(rc));
888   HDEBUG(D_auth) debug_printf("%s\n", buffer);
889   return ERROR;
890   }
891
892 cb_state.ablock = ablock;
893 cb_state.currently = CURRENTLY_CLIENT;
894 gsasl_session_hook_set(sctx, &cb_state);
895
896 /* Set properties */
897
898 if (  !set_client_prop(sctx, GSASL_PASSWORD, ob->client_password,
899                   0, buffer, buffsize)
900    || !set_client_prop(sctx, GSASL_AUTHID, ob->client_username,
901                   0, buffer, buffsize)
902    || !set_client_prop(sctx, GSASL_AUTHZID, ob->client_authz,
903                   PROP_OPTIONAL, buffer, buffsize)
904    )
905   return ERROR;
906
907 #ifndef DISABLE_TLS
908 if (tls_out.channelbinding)
909   if (ob->client_channelbinding)
910     {
911     HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
912         auname);
913 # ifndef CHANNELBIND_HACK
914     preload_prop(sctx,
915 #  ifdef EXIM_GSASL_HAVE_EXPORTER
916       tls_out.channelbind_exporter ? GSASL_CB_TLS_EXPORTER :
917 #  endif
918                                      GSASL_CB_TLS_UNIQUE,
919       tls_out.channelbinding);
920 # endif
921     }
922   else
923     HDEBUG(D_auth)
924       debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
925           auname);
926 #endif
927
928 /* Run the SASL conversation with the server */
929
930 for(s = NULL; ;)
931   {
932   uschar * outstr;
933   BOOL fail = TRUE;
934
935   rc = gsasl_step64(sctx, CS s, CSS &outstr);
936
937   if (rc == GSASL_NEEDS_MORE || rc == GSASL_OK)
938     {
939     fail = initial
940       ? smtp_write_command(sx, SCMD_FLUSH,
941                           outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
942                           ablock->public_name, outstr) <= 0
943       : outstr
944       ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
945       : FALSE;
946     free(outstr);
947     if (fail)
948       {
949       yield = FAIL_SEND;
950       goto done;
951       }
952     initial = FALSE;
953     }
954
955   if (rc != GSASL_NEEDS_MORE)
956     {
957     if (rc != GSASL_OK)
958       {
959       string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
960       break;
961       }
962
963     /* expecting a final 2xx from the server, accepting the AUTH */
964
965     if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
966       yield = OK;
967     break;      /* from SASL sequence loop */
968     }
969
970   /* 2xx or 3xx response is acceptable.  If 2xx, no further input */
971
972   if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
973     if (errno == 0 && buffer[0] == '2')
974       buffer[4] = '\0';
975     else
976       {
977       yield = FAIL;
978       goto done;
979       }
980   s = buffer + 4;
981   }
982
983 done:
984 if (yield == OK)
985   {
986   expand_nmax = 0;
987   set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
988   set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
989   set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
990   set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD);
991   }
992
993 gsasl_finish(sctx);
994 return yield;
995 }
996
997 static int
998 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
999 {
1000 HDEBUG(D_auth) debug_printf("GNU SASL callback %s for %s/%s as client\n",
1001       gsasl_prop_code_to_name(prop), ablock->drinst.name, ablock->public_name);
1002 switch (prop)
1003   {
1004 #ifdef EXIM_GSASL_HAVE_EXPORTER
1005   case GSASL_CB_TLS_EXPORTER:   /* Should never get called for this, as pre-set */
1006     if (!tls_out.channelbind_exporter) break;
1007     HDEBUG(D_auth) debug_printf(" filling in\n");
1008     gsasl_property_set(sctx, GSASL_CB_TLS_EXPORTER, CCS tls_out.channelbinding);
1009     return GSASL_OK;
1010 #endif
1011   case GSASL_CB_TLS_UNIQUE:     /* Should never get called for this, as pre-set */
1012 #ifdef EXIM_GSASL_HAVE_EXPORTER
1013     if (tls_out.channelbind_exporter) break;
1014 #endif
1015     HDEBUG(D_auth) debug_printf(" filling in\n");
1016     gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
1017     return GSASL_OK;
1018   case GSASL_SCRAM_SALTED_PASSWORD:
1019     {
1020     uschar * client_spassword =
1021       ((auth_gsasl_options_block *) ablock->drinst.options_block)->client_spassword;
1022     uschar dummy[4];
1023     HDEBUG(D_auth) if (!client_spassword)
1024       debug_printf(" client_spassword option unset\n");
1025     if (client_spassword)
1026       {
1027       expand_nmax = 0;
1028       set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
1029       set_exim_authvar_from_prop(sctx, GSASL_SCRAM_ITER);
1030       set_exim_authvar_from_prop(sctx, GSASL_SCRAM_SALT);
1031       set_client_prop(sctx, GSASL_SCRAM_SALTED_PASSWORD, client_spassword,
1032                   0, dummy, sizeof(dummy));
1033       for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
1034       expand_nmax = 0;
1035       }
1036     break;
1037     }
1038   default:
1039     HDEBUG(D_auth)
1040       debug_printf(" not providing one\n");
1041     break;
1042   }
1043 return GSASL_NO_CALLBACK;
1044 }
1045
1046 /*************************************************
1047 *                Diagnostic API                  *
1048 *************************************************/
1049
1050 gstring *
1051 auth_gsasl_version_report(gstring * g)
1052 {
1053 return string_fmt_append(g, "Library version: GNU SASL: Compile: %s\n"
1054                             "                           Runtime: %s\n",
1055         GSASL_VERSION, gsasl_check_version(NULL));
1056 }
1057
1058
1059
1060 /* Dummy */
1061 void auth_gsasl_macros(void) {}
1062
1063 #endif   /*!MACRO_PREDEF*/
1064 #endif  /* AUTH_GSASL */
1065
1066 /* End of gsasl_exim.c */