GSASL: use tls-exporter for SCRAM*PLUS methods under TLSv1.3
[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 - 2022 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8
9 /* Copyright (c) Twitter Inc 2012
10    Author: Phil Pennock <pdp@exim.org> */
11 /* Copyright (c) Phil Pennock 2012 */
12
13 /* Interface to GNU SASL library for generic authentication. */
14
15 /* Trade-offs:
16
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".
23
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.
27 */
28
29 #include "../exim.h"
30
31 #ifndef AUTH_GSASL
32 /* dummy function to satisfy compilers when we link in an "empty" file. */
33 static void dummy(int x);
34 static void dummy2(int x) { dummy(x-1); }
35 static void dummy(int x) { dummy2(x-1); }
36 #else
37
38 #include <gsasl.h>
39 #include "gsasl_exim.h"
40
41
42 #if GSASL_VERSION_MAJOR == 2
43
44 # define EXIM_GSASL_HAVE_SCRAM_SHA_256
45 # define EXIM_GSASL_SCRAM_S_KEY
46 # if GSASL_VERSION_MINOR >= 1
47 #  define EXIM_GSASL_HAVE_EXPORTER
48 # elif GSASL_VERSION_PATCH >= 1
49 #  define EXIM_GSASL_HAVE_EXPORTER
50 # endif
51
52 #elif GSASL_VERSION_MAJOR == 1
53 # if GSASL_VERSION_MINOR >= 10
54 #  define EXIM_GSASL_HAVE_SCRAM_SHA_256
55 #  define EXIM_GSASL_SCRAM_S_KEY
56
57 # elif GSASL_VERSION_MINOR == 9
58 #  define EXIM_GSASL_HAVE_SCRAM_SHA_256
59
60 #  if GSASL_VERSION_PATCH >= 1
61 #   define EXIM_GSASL_SCRAM_S_KEY
62 #  endif
63 #  if GSASL_VERSION_PATCH < 2
64 #   define CHANNELBIND_HACK
65 #  endif
66
67 # else
68 #  define CHANNELBIND_HACK
69 # endif
70 #endif
71
72 /* Convenience for testing strings */
73
74 #define STREQIC(Foo, Bar) (strcmpic((Foo), (Bar)) == 0)
75
76
77 /* Authenticator-specific options. */
78 /* I did have server_*_condition options for various mechanisms, but since
79 we only ever handle one mechanism at a time, I didn't see the point in keeping
80 that.  In case someone sees a point, I've left the condition_check() API
81 alone. */
82 #define LOFF(field) OPT_OFF(auth_gsasl_options_block, field)
83
84 optionlist auth_gsasl_options[] = {
85   { "client_authz",             opt_stringptr,  LOFF(client_authz) },
86   { "client_channelbinding",    opt_bool,       LOFF(client_channelbinding) },
87   { "client_password",          opt_stringptr,  LOFF(client_password) },
88   { "client_spassword",         opt_stringptr,  LOFF(client_spassword) },
89   { "client_username",          opt_stringptr,  LOFF(client_username) },
90
91   { "server_channelbinding",    opt_bool,       LOFF(server_channelbinding) },
92   { "server_hostname",          opt_stringptr,  LOFF(server_hostname) },
93 #ifdef EXIM_GSASL_SCRAM_S_KEY
94   { "server_key",               opt_stringptr,  LOFF(server_key) },
95 #endif
96   { "server_mech",              opt_stringptr,  LOFF(server_mech) },
97   { "server_password",          opt_stringptr,  LOFF(server_password) },
98   { "server_realm",             opt_stringptr,  LOFF(server_realm) },
99   { "server_scram_iter",        opt_stringptr,  LOFF(server_scram_iter) },
100   { "server_scram_salt",        opt_stringptr,  LOFF(server_scram_salt) },
101 #ifdef EXIM_GSASL_SCRAM_S_KEY
102   { "server_skey",              opt_stringptr,  LOFF(server_s_key) },
103 #endif
104   { "server_service",           opt_stringptr,  LOFF(server_service) }
105 };
106
107 int auth_gsasl_options_count =
108   sizeof(auth_gsasl_options)/sizeof(optionlist);
109
110 /* Defaults for the authenticator-specific options. */
111 auth_gsasl_options_block auth_gsasl_option_defaults = {
112   .server_service = US"smtp",
113   .server_hostname = US"$primary_hostname",
114   .server_scram_iter = US"4096",
115   /* all others zero/null */
116 };
117
118
119 #ifdef MACRO_PREDEF
120 # include "../macro_predef.h"
121
122 /* Dummy values */
123 void auth_gsasl_init(auth_instance *ablock) {}
124 int auth_gsasl_server(auth_instance *ablock, uschar *data) {return 0;}
125 int auth_gsasl_client(auth_instance *ablock, void * sx,
126   int timeout, uschar *buffer, int buffsize) {return 0;}
127 gstring * auth_gsasl_version_report(gstring * g) {return NULL;}
128
129 void
130 auth_gsasl_macros(void)
131 {
132 # ifdef EXIM_GSASL_HAVE_SCRAM_SHA_256
133   builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_SHA_256");
134 # endif
135 # ifdef EXIM_GSASL_SCRAM_S_KEY
136   builtin_macro_create(US"_HAVE_AUTH_GSASL_SCRAM_S_KEY");
137 # endif
138 }
139
140 #else   /*!MACRO_PREDEF*/
141
142
143
144 /* "Globals" for managing the gsasl interface. */
145
146 static Gsasl *gsasl_ctx = NULL;
147 static int
148   main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
149 static int
150   server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
151 static int
152   client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
153
154 static BOOL sasl_error_should_defer = FALSE;
155 static Gsasl_property callback_loop = 0;
156 static BOOL checked_server_condition = FALSE;
157
158 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
159
160 struct callback_exim_state {
161   auth_instance *ablock;
162   int currently;
163 };
164
165
166 /*************************************************
167 *          Initialization entry point            *
168 *************************************************/
169
170 /* Called for each instance, after its options have been read, to
171 enable consistency checks to be done, or anything else that needs
172 to be set up. */
173
174 void
175 auth_gsasl_init(auth_instance *ablock)
176 {
177 static char * once = NULL;
178 int rc;
179 auth_gsasl_options_block *ob =
180   (auth_gsasl_options_block *)(ablock->options_block);
181
182 /* As per existing Cyrus glue, use the authenticator's public name as
183 the default for the mechanism name; we don't handle multiple mechanisms
184 in one authenticator, but the same driver can be used multiple times. */
185
186 if (!ob->server_mech)
187   ob->server_mech = string_copy(ablock->public_name);
188
189 /* Can get multiple session contexts from one library context, so just
190 initialise the once. */
191
192 if (!gsasl_ctx)
193   {
194   if ((rc = gsasl_init(&gsasl_ctx)) != GSASL_OK)
195     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
196               "couldn't initialise GNU SASL library: %s (%s)",
197               ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
198
199   gsasl_callback_set(gsasl_ctx, main_callback);
200   }
201
202 /* We don't need this except to log it for debugging. */
203
204 HDEBUG(D_auth) if (!once)
205   {
206   if ((rc = gsasl_server_mechlist(gsasl_ctx, &once)) != GSASL_OK)
207     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
208               "failed to retrieve list of mechanisms: %s (%s)",
209               ablock->name,  gsasl_strerror_name(rc), gsasl_strerror(rc));
210
211   debug_printf("GNU SASL supports: %s\n", once);
212   }
213
214 if (!gsasl_client_support_p(gsasl_ctx, CCS ob->server_mech))
215   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
216             "GNU SASL does not support mechanism \"%s\"",
217             ablock->name, ob->server_mech);
218
219 if (ablock->server_condition)
220   ablock->server = TRUE;
221 else if(  ob->server_mech
222        && !STREQIC(ob->server_mech, US"EXTERNAL")
223        && !STREQIC(ob->server_mech, US"ANONYMOUS")
224        && !STREQIC(ob->server_mech, US"PLAIN")
225        && !STREQIC(ob->server_mech, US"LOGIN")
226        )
227   {
228   /* At present, for mechanisms we don't panic on absence of server_condition;
229   need to figure out the most generically correct approach to deciding when
230   it's critical and when it isn't.  Eg, for simple validation (PLAIN mechanism,
231   etc) it clearly is critical.
232   */
233
234   ablock->server = FALSE;
235   HDEBUG(D_auth) debug_printf("%s authenticator:  "
236             "Need server_condition for %s mechanism\n",
237             ablock->name, ob->server_mech);
238   }
239
240 /* This does *not* scale to new SASL mechanisms.  Need a better way to ask
241 which properties will be needed. */
242
243 if (  !ob->server_realm
244    && STREQIC(ob->server_mech, US"DIGEST-MD5"))
245   {
246   ablock->server = FALSE;
247   HDEBUG(D_auth) debug_printf("%s authenticator:  "
248             "Need server_realm for %s mechanism\n",
249             ablock->name, ob->server_mech);
250   }
251
252 ablock->client = ob->client_username && ob->client_password;
253 }
254
255
256 /* GNU SASL uses one top-level callback, registered at library level.
257 We dispatch to client and server functions instead. */
258
259 static int
260 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
261 {
262 int rc = 0;
263 struct callback_exim_state *cb_state =
264   (struct callback_exim_state *)gsasl_session_hook_get(sctx);
265
266 if (!cb_state)
267   {
268   HDEBUG(D_auth) debug_printf("gsasl callback (%d) not from our server/client processing\n", prop);
269 #ifdef CHANNELBIND_HACK
270   if (prop == GSASL_CB_TLS_UNIQUE)
271     {
272     uschar * s;
273     if ((s = gsasl_callback_hook_get(ctx)))     /* Gross hack for early lib vers */
274       {
275       HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE from ctx hook\n");
276       gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CS s);
277       }
278     else
279       {
280       HDEBUG(D_auth) debug_printf("GSASL_CB_TLS_UNIQUE!  dummy for now\n");
281       gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, "");
282       }
283     return GSASL_OK;
284     }
285 #endif
286   return GSASL_NO_CALLBACK;
287   }
288
289 HDEBUG(D_auth)
290   debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
291       prop, callback_loop);
292
293 if (callback_loop > 0)
294   {
295   /* Most likely is that we were asked for property FOO, and to
296   expand the string we asked for property BAR to put into an auth
297   variable, but property BAR is not supplied for this mechanism. */
298   HDEBUG(D_auth)
299     debug_printf("Loop, asked for property %d while handling property %d\n",
300         prop, callback_loop);
301   return GSASL_NO_CALLBACK;
302   }
303 callback_loop = prop;
304
305 if (cb_state->currently == CURRENTLY_CLIENT)
306   rc = client_callback(ctx, sctx, prop, cb_state->ablock);
307 else if (cb_state->currently == CURRENTLY_SERVER)
308   rc = server_callback(ctx, sctx, prop, cb_state->ablock);
309 else
310   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
311       "unhandled callback state, bug in Exim", cb_state->ablock->name);
312   /* NOTREACHED */
313
314 callback_loop = 0;
315 return rc;
316 }
317
318
319 /*************************************************
320 *             Debug service function             *
321 *************************************************/
322 static const uschar * 
323 gsasl_prop_code_to_name(Gsasl_property prop)
324 {
325 switch (prop)
326   {
327   case GSASL_AUTHID:                    return US"AUTHID";
328   case GSASL_AUTHZID:                   return US"AUTHZID";
329   case GSASL_PASSWORD:                  return US"PASSWORD";
330   case GSASL_ANONYMOUS_TOKEN:           return US"ANONYMOUS_TOKEN";
331   case GSASL_SERVICE:                   return US"SERVICE";
332   case GSASL_HOSTNAME:                  return US"HOSTNAME";
333   case GSASL_GSSAPI_DISPLAY_NAME:       return US"GSSAPI_DISPLAY_NAME";
334   case GSASL_PASSCODE:                  return US"PASSCODE";
335   case GSASL_SUGGESTED_PIN:             return US"SUGGESTED_PIN";
336   case GSASL_PIN:                       return US"PIN";
337   case GSASL_REALM:                     return US"REALM";
338   case GSASL_DIGEST_MD5_HASHED_PASSWORD:        return US"DIGEST_MD5_HASHED_PASSWORD";
339   case GSASL_QOPS:                      return US"QOPS";
340   case GSASL_QOP:                       return US"QOP";
341   case GSASL_SCRAM_ITER:                return US"SCRAM_ITER";
342   case GSASL_SCRAM_SALT:                return US"SCRAM_SALT";
343   case GSASL_SCRAM_SALTED_PASSWORD:     return US"SCRAM_SALTED_PASSWORD";
344 #ifdef EXIM_GSASL_SCRAM_S_KEY
345   case GSASL_SCRAM_STOREDKEY:           return US"SCRAM_STOREDKEY";
346   case GSASL_SCRAM_SERVERKEY:           return US"SCRAM_SERVERKEY";
347 #endif
348 #ifdef EXIM_GSASL_HAVE_EXPORTER         /* v. 2.1.0 */
349   case GSASL_CB_TLS_EXPORTER:           return US"CB_TLS_EXPORTER";
350 #endif
351   case GSASL_CB_TLS_UNIQUE:             return US"CB_TLS_UNIQUE";
352   case GSASL_SAML20_IDP_IDENTIFIER:     return US"SAML20_IDP_IDENTIFIER";
353   case GSASL_SAML20_REDIRECT_URL:       return US"SAML20_REDIRECT_URL";
354   case GSASL_OPENID20_REDIRECT_URL:     return US"OPENID20_REDIRECT_URL";
355   case GSASL_OPENID20_OUTCOME_DATA:     return US"OPENID20_OUTCOME_DATA";
356   case GSASL_SAML20_AUTHENTICATE_IN_BROWSER:    return US"SAML20_AUTHENTICATE_IN_BROWSER";
357   case GSASL_OPENID20_AUTHENTICATE_IN_BROWSER:  return US"OPENID20_AUTHENTICATE_IN_BROWSER";
358   case GSASL_VALIDATE_SIMPLE:           return US"VALIDATE_SIMPLE";
359   case GSASL_VALIDATE_EXTERNAL:         return US"VALIDATE_EXTERNAL";
360   case GSASL_VALIDATE_ANONYMOUS:        return US"VALIDATE_ANONYMOUS";
361   case GSASL_VALIDATE_GSSAPI:           return US"VALIDATE_GSSAPI";
362   case GSASL_VALIDATE_SECURID:          return US"VALIDATE_SECURID";
363   case GSASL_VALIDATE_SAML20:           return US"VALIDATE_SAML20";
364   case GSASL_VALIDATE_OPENID20:         return US"VALIDATE_OPENID20";
365   }
366 return CUS string_sprintf("(unknown prop: %d)", (int)prop);
367 }
368
369 static void
370 preload_prop(Gsasl_session * sctx, Gsasl_property propcode, const uschar * val)
371 {
372 DEBUG(D_auth) debug_printf("preloading prop %s val %s\n",
373   gsasl_prop_code_to_name(propcode), val);
374 gsasl_property_set(sctx, propcode, CCS val);
375 }
376
377 /*************************************************
378 *             Server entry point                 *
379 *************************************************/
380
381 /* For interface, see auths/README */
382
383 int
384 auth_gsasl_server(auth_instance * ablock, uschar * initial_data)
385 {
386 uschar * tmps;
387 char * to_send, * received;
388 Gsasl_session * sctx = NULL;
389 auth_gsasl_options_block * ob =
390   (auth_gsasl_options_block *)(ablock->options_block);
391 struct callback_exim_state cb_state;
392 int rc, auth_result, exim_error, exim_error_override;
393
394 HDEBUG(D_auth)
395   debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
396       ablock->name, ob->server_mech);
397
398 #ifndef DISABLE_TLS
399 if (tls_in.channelbinding && ob->server_channelbinding)
400   {
401 # ifndef DISABLE_TLS_RESUME
402   if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED)
403     {           /* per RFC 7677 section 4 */
404     HDEBUG(D_auth) debug_printf(
405       "channel binding not usable on resumed TLS without extended-master-secret");
406     return FAIL;
407     }
408 # endif
409 # ifdef CHANNELBIND_HACK
410 /* This is a gross hack to get around the library before 1.9.2
411 a) requiring that c-b was already set, at the _start() call, and
412 b) caching a b64'd version of the binding then which it never updates. */
413
414   gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
415 # endif
416   }
417 #endif
418
419 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
420   {
421   auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
422       gsasl_strerror_name(rc), gsasl_strerror(rc));
423   HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
424   return DEFER;
425   }
426 /* Hereafter: gsasl_finish(sctx) please */
427
428 cb_state.ablock = ablock;
429 cb_state.currently = CURRENTLY_SERVER;
430 gsasl_session_hook_set(sctx, &cb_state);
431
432 tmps = expand_string(ob->server_service);
433 preload_prop(sctx, GSASL_SERVICE, tmps);
434 tmps = expand_string(ob->server_hostname);
435 preload_prop(sctx, GSASL_HOSTNAME, tmps);
436 if (ob->server_realm)
437   {
438   tmps = expand_string(ob->server_realm);
439   if (tmps && *tmps)
440     preload_prop(sctx, GSASL_REALM, tmps);
441   }
442 /* We don't support protection layers. */
443 preload_prop(sctx, GSASL_QOPS, US "qop-auth");
444
445 #ifndef DISABLE_TLS
446 if (tls_in.channelbinding)
447   {
448   /* Some auth mechanisms can ensure that both sides are talking withing the
449   same security context; for TLS, this means that even if a bad certificate
450   has been accepted, they remain MitM-proof because both sides must be within
451   the same negotiated session; if someone is terminating one session and
452   proxying data on within a second, authentication will fail.
453
454   We might not have this available, depending upon TLS implementation,
455   ciphersuite, phase of moon ...
456
457   If we do, it results in extra SASL mechanisms being available; here,
458   Exim's one-mechanism-per-authenticator potentially causes problems.
459   It depends upon how GNU SASL will implement the PLUS variants of GS2
460   and whether it automatically mandates a switch to the bound PLUS
461   if the data is available.  Since default-on, despite being more secure,
462   would then result in mechanism name changes on a library update, we
463   have little choice but to default it off and let the admin choose to
464   enable it.  *sigh*
465
466   Earlier library versions need this set early, during the _start() call,
467   so we had to misuse gsasl_callback_hook_set/get() as a data transfer
468   mech for the callback done at that time to get the bind-data.  More recently
469   the callback is done (if needed) during the first gsasl_stop().  We know
470   the bind-data here so can set it (and should not get a callback).
471   */
472   if (ob->server_channelbinding)
473     {
474     HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
475         ablock->name);
476 # ifndef CHANNELBIND_HACK
477     preload_prop(sctx,
478 #  ifdef EXIM_GSASL_HAVE_EXPORTER
479       tls_in.channelbind_exporter ? GSASL_CB_TLS_EXPORTER :
480 #  endif
481                                     GSASL_CB_TLS_UNIQUE,
482       tls_in.channelbinding);
483 # endif
484     }
485   else
486     HDEBUG(D_auth)
487       debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
488           ablock->name);
489   }
490 else
491   HDEBUG(D_auth)
492     debug_printf("Auth %s: no channel-binding data available\n",
493         ablock->name);
494 #endif
495
496 checked_server_condition = FALSE;
497
498 received = CS initial_data;
499 to_send = NULL;
500 exim_error = exim_error_override = OK;
501
502 do {
503   switch (rc = gsasl_step64(sctx, received, &to_send))
504     {
505     case GSASL_OK:
506       if (!to_send)
507         goto STOP_INTERACTION;
508       break;
509
510     case GSASL_NEEDS_MORE:
511       break;
512
513     case GSASL_AUTHENTICATION_ERROR:
514     case GSASL_INTEGRITY_ERROR:
515     case GSASL_NO_AUTHID:
516     case GSASL_NO_ANONYMOUS_TOKEN:
517     case GSASL_NO_AUTHZID:
518     case GSASL_NO_PASSWORD:
519     case GSASL_NO_PASSCODE:
520     case GSASL_NO_PIN:
521     case GSASL_BASE64_ERROR:
522       HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
523           gsasl_strerror_name(rc), gsasl_strerror(rc));
524       log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
525           "GNU SASL permanent failure: %s (%s)",
526           ablock->name, ob->server_mech,
527           gsasl_strerror_name(rc), gsasl_strerror(rc));
528       if (rc == GSASL_BASE64_ERROR)
529         exim_error_override = BAD64;
530       goto STOP_INTERACTION;
531
532     default:
533       auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
534           gsasl_strerror_name(rc), gsasl_strerror(rc));
535       HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
536       exim_error_override = DEFER;
537       goto STOP_INTERACTION;
538     }
539
540   /*XXX having our caller send the final smtp "235" is unfortunate; wastes a roundtrip */
541   if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
542     exim_error = auth_get_no64_data(USS &received, US to_send);
543
544   if (to_send)
545     {
546     free(to_send);
547     to_send = NULL;
548     }
549
550   if (exim_error)
551     break; /* handles * cancelled check */
552
553   } while (rc == GSASL_NEEDS_MORE);
554
555 STOP_INTERACTION:
556 auth_result = rc;
557
558 HDEBUG(D_auth)
559   {
560   const uschar * s;
561   if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_ITER)))
562     debug_printf(" - itercnt:   '%s'\n", s);
563   if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SALT)))
564     debug_printf(" - salt:      '%s'\n", s);
565 #ifdef EXIM_GSASL_SCRAM_S_KEY
566   if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_SERVERKEY)))
567     debug_printf(" - ServerKey: '%s'\n", s);
568   if ((s = CUS gsasl_property_fast(sctx, GSASL_SCRAM_STOREDKEY)))
569     debug_printf(" - StoredKey: '%s'\n", s);
570 #endif
571   }
572
573 gsasl_finish(sctx);
574
575 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
576
577 if (exim_error != OK)
578   return exim_error;
579
580 if (auth_result != GSASL_OK)
581   {
582   HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
583       gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
584   if (exim_error_override != OK)
585     return exim_error_override; /* might be DEFER */
586   if (sasl_error_should_defer) /* overriding auth failure SASL error */
587     return DEFER;
588   return FAIL;
589   }
590
591 /* Auth succeeded, check server_condition unless already done in callback */
592 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
593 }
594
595
596 /* returns the GSASL status of expanding the Exim string given */
597 static int
598 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
599 {
600 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
601 switch (exim_rc)
602   {
603   case OK:      return GSASL_OK;
604   case DEFER:   sasl_error_should_defer = TRUE;
605                 return GSASL_AUTHENTICATION_ERROR;
606   case FAIL:    return GSASL_AUTHENTICATION_ERROR;
607   default:      log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
608                   "Unhandled return from checking %s: %d",
609                   ablock->name, label, exim_rc);
610   }
611
612 /* NOTREACHED */
613 return GSASL_AUTHENTICATION_ERROR;
614 }
615
616
617 /* Set the "next" $auth[n] and increment expand_nmax */
618
619 static void
620 set_exim_authvar_from_prop(Gsasl_session * sctx, Gsasl_property prop)
621 {
622 uschar * propval = US gsasl_property_fast(sctx, prop);
623 int i = expand_nmax, j = i + 1;
624 propval = propval ? string_copy(propval) : US"";
625 HDEBUG(D_auth) debug_printf("auth[%d] <=  %s'%s'\n",
626                             j, gsasl_prop_code_to_name(prop), propval);
627 expand_nstring[j] = propval;
628 expand_nlength[j] = Ustrlen(propval);
629 if (i < AUTH_VARS) auth_vars[i] = propval;
630 expand_nmax = j;
631 }
632
633 static void
634 set_exim_authvars_from_a_az_r_props(Gsasl_session * sctx)
635 {
636 if (expand_nmax > 0 ) return;
637
638 /* Asking for GSASL_AUTHZID calls back into us if we use
639 gsasl_property_get(), thus the use of gsasl_property_fast().
640 Do we really want to hardcode limits per mechanism?  What happens when
641 a new mechanism is added to the library.  It *shouldn't* result in us
642 needing to add more glue, since avoiding that is a large part of the
643 point of SASL. */
644
645 set_exim_authvar_from_prop(sctx, GSASL_AUTHID);
646 set_exim_authvar_from_prop(sctx, GSASL_AUTHZID);
647 set_exim_authvar_from_prop(sctx, GSASL_REALM);
648 }
649
650
651 static int
652 prop_from_option(Gsasl_session * sctx, Gsasl_property prop,
653   const uschar * option)
654 {
655 HDEBUG(D_auth) debug_printf(" %s\n", gsasl_prop_code_to_name(prop));
656 if (option)
657   {
658   set_exim_authvars_from_a_az_r_props(sctx);
659   option = expand_cstring(option);
660   HDEBUG(D_auth) debug_printf("  '%s'\n", option);
661   if (*option)
662     gsasl_property_set(sctx, prop, CCS option);
663   return GSASL_OK;
664   }
665 HDEBUG(D_auth) debug_printf("  option not set\n");
666 return GSASL_NO_CALLBACK;
667 }
668
669 static int
670 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
671   auth_instance *ablock)
672 {
673 char * tmps;
674 uschar * s;
675 int cbrc = GSASL_NO_CALLBACK;
676 auth_gsasl_options_block * ob =
677   (auth_gsasl_options_block *)(ablock->options_block);
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->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 =
849   (auth_gsasl_options_block *)(ablock->options_block);
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       ablock->name, 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         ablock->name);
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           ablock->name);
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->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->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 */