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