db14a40e0ad2fe6b8117a6d23034d760f58909d9
[users/jgh/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, smtp_inblock * 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 # ifdef CHANNELBIND_HACK
305 /* This is a gross hack to get around the library a) requiring that
306 c-b was already set, at the _start() call, and b) caching a b64'd
307 version of the binding then which it never updates. */
308
309 if (tls_in.channelbinding && ob->server_channelbinding)
310   gsasl_callback_hook_set(gsasl_ctx, tls_in.channelbinding);
311 # endif
312 #endif
313
314 if ((rc = gsasl_server_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
315   {
316   auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
317       gsasl_strerror_name(rc), gsasl_strerror(rc));
318   HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
319   return DEFER;
320   }
321 /* Hereafter: gsasl_finish(sctx) please */
322
323 cb_state.ablock = ablock;
324 cb_state.currently = CURRENTLY_SERVER;
325 gsasl_session_hook_set(sctx, &cb_state);
326
327 tmps = CS expand_string(ob->server_service);
328 gsasl_property_set(sctx, GSASL_SERVICE, tmps);
329 tmps = CS expand_string(ob->server_hostname);
330 gsasl_property_set(sctx, GSASL_HOSTNAME, tmps);
331 if (ob->server_realm)
332   {
333   tmps = CS expand_string(ob->server_realm);
334   if (tmps && *tmps)
335     gsasl_property_set(sctx, GSASL_REALM, tmps);
336   }
337 /* We don't support protection layers. */
338 gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
339
340 #ifndef DISABLE_TLS
341 if (tls_in.channelbinding)
342   {
343   /* Some auth mechanisms can ensure that both sides are talking withing the
344   same security context; for TLS, this means that even if a bad certificate
345   has been accepted, they remain MitM-proof because both sides must be within
346   the same negotiated session; if someone is terminating one session and
347   proxying data on within a second, authentication will fail.
348
349   We might not have this available, depending upon TLS implementation,
350   ciphersuite, phase of moon ...
351
352   If we do, it results in extra SASL mechanisms being available; here,
353   Exim's one-mechanism-per-authenticator potentially causes problems.
354   It depends upon how GNU SASL will implement the PLUS variants of GS2
355   and whether it automatically mandates a switch to the bound PLUS
356   if the data is available.  Since default-on, despite being more secure,
357   would then result in mechanism name changes on a library update, we
358   have little choice but to default it off and let the admin choose to
359   enable it.  *sigh*
360   */
361   if (ob->server_channelbinding)
362     {
363     HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
364         ablock->name);
365 # ifdef CHANNELBIND_HACK
366     gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_in.channelbinding);
367 # endif
368     }
369   else
370     HDEBUG(D_auth)
371       debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
372           ablock->name);
373   }
374 else
375   HDEBUG(D_auth)
376     debug_printf("Auth %s: no channel-binding data available\n",
377         ablock->name);
378 #endif
379
380 checked_server_condition = FALSE;
381
382 received = CS initial_data;
383 to_send = NULL;
384 exim_error = exim_error_override = OK;
385
386 do {
387   switch (rc = gsasl_step64(sctx, received, &to_send))
388     {
389     case GSASL_OK:
390       if (!to_send)
391         goto STOP_INTERACTION;
392       break;
393
394     case GSASL_NEEDS_MORE:
395       break;
396
397     case GSASL_AUTHENTICATION_ERROR:
398     case GSASL_INTEGRITY_ERROR:
399     case GSASL_NO_AUTHID:
400     case GSASL_NO_ANONYMOUS_TOKEN:
401     case GSASL_NO_AUTHZID:
402     case GSASL_NO_PASSWORD:
403     case GSASL_NO_PASSCODE:
404     case GSASL_NO_PIN:
405     case GSASL_BASE64_ERROR:
406       HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
407           gsasl_strerror_name(rc), gsasl_strerror(rc));
408       log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
409           "GNU SASL permanent failure: %s (%s)",
410           ablock->name, ob->server_mech,
411           gsasl_strerror_name(rc), gsasl_strerror(rc));
412       if (rc == GSASL_BASE64_ERROR)
413         exim_error_override = BAD64;
414       goto STOP_INTERACTION;
415
416     default:
417       auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
418           gsasl_strerror_name(rc), gsasl_strerror(rc));
419       HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
420       exim_error_override = DEFER;
421       goto STOP_INTERACTION;
422     }
423
424   if ((rc == GSASL_NEEDS_MORE) || (to_send && *to_send))
425     exim_error = auth_get_no64_data(USS &received, US to_send);
426
427   if (to_send)
428     {
429     free(to_send);
430     to_send = NULL;
431     }
432
433   if (exim_error)
434     break; /* handles * cancelled check */
435
436   } while (rc == GSASL_NEEDS_MORE);
437
438 STOP_INTERACTION:
439 auth_result = rc;
440
441 gsasl_finish(sctx);
442
443 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
444
445 if (exim_error != OK)
446   return exim_error;
447
448 if (auth_result != GSASL_OK)
449   {
450   HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
451       gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
452   if (exim_error_override != OK)
453     return exim_error_override; /* might be DEFER */
454   if (sasl_error_should_defer) /* overriding auth failure SASL error */
455     return DEFER;
456   return FAIL;
457   }
458
459 /* Auth succeeded, check server_condition unless already done in callback */
460 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
461 }
462
463
464 /* returns the GSASL status of expanding the Exim string given */
465 static int
466 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
467 {
468 int exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
469 switch (exim_rc)
470   {
471   case OK:      return GSASL_OK;
472   case DEFER:   sasl_error_should_defer = TRUE;
473                 return GSASL_AUTHENTICATION_ERROR;
474   case FAIL:    return GSASL_AUTHENTICATION_ERROR;
475   default:      log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
476                   "Unhandled return from checking %s: %d",
477                   ablock->name, label, exim_rc);
478   }
479
480 /* NOTREACHED */
481 return GSASL_AUTHENTICATION_ERROR;
482 }
483
484 static int
485 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop,
486   auth_instance *ablock)
487 {
488 char *tmps;
489 uschar *propval;
490 int cbrc = GSASL_NO_CALLBACK;
491 auth_gsasl_options_block *ob =
492   (auth_gsasl_options_block *)(ablock->options_block);
493
494 HDEBUG(D_auth)
495   debug_printf("GNU SASL callback %d for %s/%s as server\n",
496       prop, ablock->name, ablock->public_name);
497
498 for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
499 expand_nmax = 0;
500
501 switch (prop)
502   {
503   case GSASL_VALIDATE_SIMPLE:
504     HDEBUG(D_auth) debug_printf(" VALIDATE_SIMPLE\n");
505     /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
506     propval = US gsasl_property_fast(sctx, GSASL_AUTHID);
507     auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
508     propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
509     auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
510     propval = US gsasl_property_fast(sctx, GSASL_PASSWORD);
511     auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US"";
512     expand_nmax = 3;
513     for (int i = 1; i <= 3; ++i)
514       expand_nlength[i] = Ustrlen(expand_nstring[i]);
515
516     cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
517     checked_server_condition = TRUE;
518     break;
519
520   case GSASL_VALIDATE_EXTERNAL:
521     HDEBUG(D_auth) debug_printf(" VALIDATE_EXTERNAL\n");
522     if (!ablock->server_condition)
523       {
524       HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL\n");
525       cbrc = GSASL_AUTHENTICATION_ERROR;
526       break;
527       }
528     propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
529
530     /* We always set $auth1, even if only to empty string. */
531     auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
532     expand_nlength[1] = Ustrlen(expand_nstring[1]);
533     expand_nmax = 1;
534
535     cbrc = condition_check(ablock,
536         US"server_condition (EXTERNAL)", ablock->server_condition);
537     checked_server_condition = TRUE;
538     break;
539
540   case GSASL_VALIDATE_ANONYMOUS:
541     HDEBUG(D_auth) debug_printf(" VALIDATE_ANONYMOUS\n");
542     if (!ablock->server_condition)
543       {
544       HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS\n");
545       cbrc = GSASL_AUTHENTICATION_ERROR;
546       break;
547       }
548     propval = US gsasl_property_fast(sctx, GSASL_ANONYMOUS_TOKEN);
549
550     /* We always set $auth1, even if only to empty string. */
551
552     auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
553     expand_nlength[1] = Ustrlen(expand_nstring[1]);
554     expand_nmax = 1;
555
556     cbrc = condition_check(ablock,
557         US"server_condition (ANONYMOUS)", ablock->server_condition);
558     checked_server_condition = TRUE;
559     break;
560
561   case GSASL_VALIDATE_GSSAPI:
562     HDEBUG(D_auth) debug_printf(" VALIDATE_GSSAPI\n");
563     /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
564     The display-name is authenticated as part of GSS, the authzid is claimed
565     by the SASL integration after authentication; protected against tampering
566     (if the SASL mechanism supports that, which Kerberos does) but is
567     unverified, same as normal for other mechanisms.
568      First coding, we had these values swapped, but for consistency and prior
569     to the first release of Exim with this authenticator, they've been
570     switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
571
572     propval = US gsasl_property_fast(sctx, GSASL_GSSAPI_DISPLAY_NAME);
573     auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
574     propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
575     auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
576     expand_nmax = 2;
577     for (int i = 1; i <= 2; ++i)
578       expand_nlength[i] = Ustrlen(expand_nstring[i]);
579
580     /* In this one case, it perhaps makes sense to default back open?
581     But for consistency, let's just mandate server_condition here too. */
582
583     cbrc = condition_check(ablock,
584         US"server_condition (GSSAPI family)", ablock->server_condition);
585     checked_server_condition = TRUE;
586     break;
587
588   case GSASL_SCRAM_ITER:
589     HDEBUG(D_auth) debug_printf(" SCRAM_ITER\n");
590     if (ob->server_scram_iter)
591       {
592       tmps = CS expand_string(ob->server_scram_iter);
593       gsasl_property_set(sctx, GSASL_SCRAM_ITER, tmps);
594       cbrc = GSASL_OK;
595       }
596     break;
597
598   case GSASL_SCRAM_SALT:
599     HDEBUG(D_auth) debug_printf(" SCRAM_SALT\n");
600     if (ob->server_scram_iter)
601       {
602       tmps = CS expand_string(ob->server_scram_salt);
603       gsasl_property_set(sctx, GSASL_SCRAM_SALT, tmps);
604       cbrc = GSASL_OK;
605       }
606     break;
607
608   case GSASL_PASSWORD:
609     HDEBUG(D_auth) debug_printf(" PASSWORD\n");
610     /* DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
611        CRAM-MD5: GSASL_AUTHID
612        PLAIN: GSASL_AUTHID and GSASL_AUTHZID
613        LOGIN: GSASL_AUTHID
614      */
615     if (ob->server_scram_iter)
616       {
617       tmps = CS expand_string(ob->server_scram_iter);
618       gsasl_property_set(sctx, GSASL_SCRAM_ITER, tmps);
619       }
620     if (ob->server_scram_salt)
621       {
622       tmps = CS expand_string(ob->server_scram_salt);
623       gsasl_property_set(sctx, GSASL_SCRAM_SALT, tmps);
624       }
625
626     /* Asking for GSASL_AUTHZID calls back into us if we use
627     gsasl_property_get(), thus the use of gsasl_property_fast().
628     Do we really want to hardcode limits per mechanism?  What happens when
629     a new mechanism is added to the library.  It *shouldn't* result in us
630     needing to add more glue, since avoiding that is a large part of the
631     point of SASL. */
632
633     propval = US gsasl_property_fast(sctx, GSASL_AUTHID);
634     auth_vars[0] = expand_nstring[1] = propval ? string_copy(propval) : US"";
635     propval = US gsasl_property_fast(sctx, GSASL_AUTHZID);
636     auth_vars[1] = expand_nstring[2] = propval ? string_copy(propval) : US"";
637     propval = US gsasl_property_fast(sctx, GSASL_REALM);
638     auth_vars[2] = expand_nstring[3] = propval ? string_copy(propval) : US"";
639     expand_nmax = 3;
640     for (int i = 1; i <= 3; ++i)
641       expand_nlength[i] = Ustrlen(expand_nstring[i]);
642
643     if (!(tmps = CS expand_string(ob->server_password)))
644       {
645       sasl_error_should_defer = f.expand_string_forcedfail ? FALSE : TRUE;
646       HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
647           "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
648       return GSASL_AUTHENTICATION_ERROR;
649       }
650     gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
651
652     /* This is inadequate; don't think Exim's store stacks are geared
653     for memory wiping, so expanding strings will leave stuff laying around.
654     But no need to compound the problem, so get rid of the one we can. */
655
656     memset(tmps, '\0', strlen(tmps));
657     cbrc = GSASL_OK;
658     break;
659
660   default:
661     HDEBUG(D_auth) debug_printf(" Unrecognised callback: %d\n", prop);
662     cbrc = GSASL_NO_CALLBACK;
663   }
664
665 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
666     gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
667
668 return cbrc;
669 }
670
671
672 /******************************************************************************/
673
674 #define PROP_OPTIONAL   BIT(0)
675 #define PROP_STRINGPREP BIT(1)
676
677
678 static BOOL
679 client_prop(Gsasl_session * sctx, Gsasl_property propnum, uschar * val,
680   const uschar * why, unsigned flags, uschar * buffer, int buffsize)
681 {
682 uschar * s, * t;
683 int rc;
684
685 if (flags & PROP_OPTIONAL && !val) return TRUE;
686 if (!(s = expand_string(val)) || !(flags & PROP_OPTIONAL) && !*s)
687   {
688   string_format(buffer, buffsize, "%s", expand_string_message);
689   return FALSE;
690   }
691 if (!*s) return TRUE;
692
693 #ifdef SUPPORT_I18N
694 if (flags & PROP_STRINGPREP)
695   {
696   if (gsasl_saslprep(CCS s, 0, CSS &t, &rc) != GSASL_OK)
697     {
698     string_format(buffer, buffsize, "Bad result from saslprep(%s): %s\n",
699                   why, stringprep_strerror(rc));
700     HDEBUG(D_auth) debug_printf("%s\n", buffer);
701     return FALSE;
702     }
703   gsasl_property_set(sctx, propnum, CS t);
704
705   free(t);
706   }
707 else
708 #endif
709   gsasl_property_set(sctx, propnum, CS s);
710
711 return TRUE;
712 }
713
714 /*************************************************
715 *              Client entry point                *
716 *************************************************/
717
718 /* For interface, see auths/README */
719
720 int
721 auth_gsasl_client(
722   auth_instance *ablock,                /* authenticator block */
723   smtp_inblock * sx,                    /* connection */
724   int timeout,                          /* command timeout */
725   uschar *buffer,                       /* buffer for reading response */
726   int buffsize)                         /* size of buffer */
727 {
728 auth_gsasl_options_block *ob =
729   (auth_gsasl_options_block *)(ablock->options_block);
730 Gsasl_session * sctx = NULL;
731 struct callback_exim_state cb_state;
732 uschar * s;
733 BOOL initial = TRUE, do_stringprep;
734 int rc, yield = FAIL, flags;
735
736 HDEBUG(D_auth)
737   debug_printf("GNU SASL: initialising session for %s, mechanism %s\n",
738       ablock->name, ob->server_mech);
739
740 *buffer = 0;
741
742 #ifndef DISABLE_TLS
743 /* This is a gross hack to get around the library a) requiring that
744 c-b was already set, at the _start() call, and b) caching a b64'd
745 version of the binding then which it never updates. */
746
747 if (tls_out.channelbinding)
748   if (ob->client_channelbinding)
749     gsasl_callback_hook_set(gsasl_ctx, tls_out.channelbinding);
750 #endif
751
752 if ((rc = gsasl_client_start(gsasl_ctx, CCS ob->server_mech, &sctx)) != GSASL_OK)
753   {
754   string_format(buffer, buffsize, "GNU SASL: session start failure: %s (%s)",
755       gsasl_strerror_name(rc), gsasl_strerror(rc));
756   HDEBUG(D_auth) debug_printf("%s\n", buffer);
757   return ERROR;
758   }
759
760 cb_state.ablock = ablock;
761 cb_state.currently = CURRENTLY_CLIENT;
762 gsasl_session_hook_set(sctx, &cb_state);
763
764 /* Set properties */
765
766 flags = Ustrncmp(ob->server_mech, "SCRAM-", 5) == 0 ? PROP_STRINGPREP : 0;
767
768 if (  !client_prop(sctx, GSASL_PASSWORD, ob->client_password, US"password",
769                   flags, buffer, buffsize)
770    || !client_prop(sctx, GSASL_AUTHID, ob->client_username, US"username",
771                   flags, buffer, buffsize)
772    || !client_prop(sctx, GSASL_AUTHZID, ob->client_authz, US"authz",
773                   flags | PROP_OPTIONAL, buffer, buffsize)
774    )
775   return ERROR;
776
777 #ifndef DISABLE_TLS
778 if (tls_out.channelbinding)
779   if (ob->client_channelbinding)
780     {
781     HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
782         ablock->name);
783 # ifdef CHANNELBIND_HACK
784     gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
785 # endif
786     }
787   else
788     HDEBUG(D_auth)
789       debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
790           ablock->name);
791 #endif
792
793 /* Run the SASL conversation with the server */
794
795 for(s = NULL; ;)
796   {
797   uschar * outstr;
798   BOOL fail;
799
800   rc = gsasl_step64(sctx, CS s, CSS &outstr);
801
802   fail = initial
803     ? smtp_write_command(sx, SCMD_FLUSH,
804                         outstr ? "AUTH %s %s\r\n" : "AUTH %s\r\n",
805                         ablock->public_name, outstr) <= 0
806     : outstr
807     ? smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", outstr) <= 0
808     : FALSE;
809   if (outstr && *outstr) free(outstr);
810   if (fail)
811     {
812     yield = FAIL_SEND;
813     goto done;
814     }
815   initial = FALSE;
816
817   if (rc != GSASL_NEEDS_MORE)
818     {
819     if (rc != GSASL_OK)
820       {
821       string_format(buffer, buffsize, "gsasl: %s", gsasl_strerror(rc));
822       break;
823       }
824
825     /* expecting a final 2xx from the server, accepting the AUTH */
826
827     if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
828       yield = OK;
829     break;      /* from SASL sequence loop */
830     }
831
832   /* 2xx or 3xx response is acceptable.  If 2xx, no further input */
833
834   if (!smtp_read_response(sx, buffer, buffsize, '3', timeout))
835     if (errno == 0 && buffer[0] == '2')
836       buffer[4] = '\0';
837     else
838       {
839       yield = FAIL;
840       goto done;
841       }
842   s = buffer + 4;
843   }
844
845 done:
846 gsasl_finish(sctx);
847 return yield;
848 }
849
850 static int
851 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
852 {
853 HDEBUG(D_auth) debug_printf("GNU SASL callback %d for %s/%s as client\n",
854                 prop, ablock->name, ablock->public_name);
855 switch (prop)
856   {
857   case GSASL_AUTHZID:
858     HDEBUG(D_auth) debug_printf(" inquired for AUTHZID; not providing one\n");
859     break;
860   case GSASL_SCRAM_SALTED_PASSWORD:
861     HDEBUG(D_auth)
862       debug_printf(" inquired for SCRAM_SALTED_PASSWORD; not providing one\n");
863     break;
864   case GSASL_CB_TLS_UNIQUE:
865     HDEBUG(D_auth)
866       debug_printf(" inquired for CB_TLS_UNIQUE, filling in\n");
867     gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE, CCS tls_out.channelbinding);
868     break;
869   }
870 return GSASL_NO_CALLBACK;
871 }
872
873 /*************************************************
874 *                Diagnostic API                  *
875 *************************************************/
876
877 void
878 auth_gsasl_version_report(FILE *f)
879 {
880 const char *runtime;
881 runtime = gsasl_check_version(NULL);
882 fprintf(f, "Library version: GNU SASL: Compile: %s\n"
883            "                           Runtime: %s\n",
884         GSASL_VERSION, runtime);
885 }
886
887 #endif   /*!MACRO_PREDEF*/
888 #endif  /* AUTH_GSASL */
889
890 /* End of gsasl_exim.c */