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