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