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