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