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