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