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