Imported Upstream version 4.84
[hcoop/debian/exim4.git] / src / auths / gsasl_exim.c
1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2012 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 /* Copyright (c) Twitter Inc 2012
9 Author: Phil Pennock <pdp@exim.org> */
10 /* Copyright (c) Phil Pennock 2012 */
11
12 /* Interface to GNU SASL library for generic authentication. */
13
14 /* Trade-offs:
15
16 GNU SASL does not provide authentication data itself, so we have to expose
17 that decision to configuration. For some mechanisms, we need to act much
18 like plaintext. For others, we only need to be able to provide some
19 evaluated data on demand. There's no abstracted way (ie, without hardcoding
20 knowledge of authenticators here) to know which need what properties; we
21 can't query a session or the library for "we will need these for mechanism X".
22
23 So: we always require server_condition, even if sometimes it will just be
24 set as "yes". We do provide a number of other hooks, which might not make
25 sense in all contexts. For some, we can do checks at init time.
26 */
27
28 #include "../exim.h"
29
30 #ifndef AUTH_GSASL
31 /* dummy function to satisfy compilers when we link in an "empty" file. */
32 static void dummy(int x);
33 static void dummy2(int x) { dummy(x-1); }
34 static void dummy(int x) { dummy2(x-1); }
35 #else
36
37 #include <gsasl.h>
38 #include "gsasl_exim.h"
39
40 /* Authenticator-specific options. */
41 /* I did have server_*_condition options for various mechanisms, but since
42 we only ever handle one mechanism at a time, I didn't see the point in keeping
43 that. In case someone sees a point, I've left the condition_check() API
44 alone. */
45 optionlist auth_gsasl_options[] = {
46 { "server_channelbinding", opt_bool,
47 (void *)(offsetof(auth_gsasl_options_block, server_channelbinding)) },
48 { "server_hostname", opt_stringptr,
49 (void *)(offsetof(auth_gsasl_options_block, server_hostname)) },
50 { "server_mech", opt_stringptr,
51 (void *)(offsetof(auth_gsasl_options_block, server_mech)) },
52 { "server_password", opt_stringptr,
53 (void *)(offsetof(auth_gsasl_options_block, server_password)) },
54 { "server_realm", opt_stringptr,
55 (void *)(offsetof(auth_gsasl_options_block, server_realm)) },
56 { "server_scram_iter", opt_stringptr,
57 (void *)(offsetof(auth_gsasl_options_block, server_scram_iter)) },
58 { "server_scram_salt", opt_stringptr,
59 (void *)(offsetof(auth_gsasl_options_block, server_scram_salt)) },
60 { "server_service", opt_stringptr,
61 (void *)(offsetof(auth_gsasl_options_block, server_service)) }
62 };
63 /* GSASL_SCRAM_SALTED_PASSWORD documented only for client, so not implementing
64 hooks to avoid cleartext passwords in the Exim server. */
65
66 int auth_gsasl_options_count =
67 sizeof(auth_gsasl_options)/sizeof(optionlist);
68
69 /* Defaults for the authenticator-specific options. */
70 auth_gsasl_options_block auth_gsasl_option_defaults = {
71 US"smtp", /* server_service */
72 US"$primary_hostname", /* server_hostname */
73 NULL, /* server_realm */
74 NULL, /* server_mech */
75 NULL, /* server_password */
76 NULL, /* server_scram_iter */
77 NULL, /* server_scram_salt */
78 FALSE /* server_channelbinding */
79 };
80
81 /* "Globals" for managing the gsasl interface. */
82
83 static Gsasl *gsasl_ctx = NULL;
84 static int
85 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop);
86 static int
87 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
88 static int
89 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock);
90
91 static BOOL sasl_error_should_defer = FALSE;
92 static Gsasl_property callback_loop = 0;
93 static BOOL checked_server_condition = FALSE;
94
95 enum { CURRENTLY_SERVER = 1, CURRENTLY_CLIENT = 2 };
96
97 struct callback_exim_state {
98 auth_instance *ablock;
99 int currently;
100 };
101
102
103 /*************************************************
104 * Initialization entry point *
105 *************************************************/
106
107 /* Called for each instance, after its options have been read, to
108 enable consistency checks to be done, or anything else that needs
109 to be set up. */
110
111 void
112 auth_gsasl_init(auth_instance *ablock)
113 {
114 char *p;
115 int rc, supported;
116 auth_gsasl_options_block *ob =
117 (auth_gsasl_options_block *)(ablock->options_block);
118
119 /* As per existing Cyrus glue, use the authenticator's public name as
120 the default for the mechanism name; we don't handle multiple mechanisms
121 in one authenticator, but the same driver can be used multiple times. */
122
123 if (ob->server_mech == NULL)
124 ob->server_mech = string_copy(ablock->public_name);
125
126 /* Can get multiple session contexts from one library context, so just
127 initialise the once. */
128 if (gsasl_ctx == NULL) {
129 rc = gsasl_init(&gsasl_ctx);
130 if (rc != GSASL_OK) {
131 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
132 "couldn't initialise GNU SASL library: %s (%s)",
133 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
134 }
135 gsasl_callback_set(gsasl_ctx, main_callback);
136 }
137
138 /* We don't need this except to log it for debugging. */
139 rc = gsasl_server_mechlist(gsasl_ctx, &p);
140 if (rc != GSASL_OK)
141 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
142 "failed to retrieve list of mechanisms: %s (%s)",
143 ablock->name, gsasl_strerror_name(rc), gsasl_strerror(rc));
144 HDEBUG(D_auth) debug_printf("GNU SASL supports: %s\n", p);
145
146 supported = gsasl_client_support_p(gsasl_ctx, (const char *)ob->server_mech);
147 if (!supported)
148 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
149 "GNU SASL does not support mechanism \"%s\"",
150 ablock->name, ob->server_mech);
151
152 if ((ablock->server_condition == NULL) &&
153 (streqic(ob->server_mech, US"EXTERNAL") ||
154 streqic(ob->server_mech, US"ANONYMOUS") ||
155 streqic(ob->server_mech, US"PLAIN") ||
156 streqic(ob->server_mech, US"LOGIN")))
157 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
158 "Need server_condition for %s mechanism",
159 ablock->name, ob->server_mech);
160
161 /* This does *not* scale to new SASL mechanisms. Need a better way to ask
162 which properties will be needed. */
163 if ((ob->server_realm == NULL) &&
164 streqic(ob->server_mech, US"DIGEST-MD5"))
165 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
166 "Need server_realm for %s mechanism",
167 ablock->name, ob->server_mech);
168
169 /* At present, for mechanisms we don't panic on absence of server_condition;
170 need to figure out the most generically correct approach to deciding when
171 it's critical and when it isn't. Eg, for simple validation (PLAIN mechanism,
172 etc) it clearly is critical.
173
174 So don't activate without server_condition, this might be relaxed in the future.
175 */
176 if (ablock->server_condition != NULL) ablock->server = TRUE;
177 ablock->client = FALSE;
178 }
179
180
181 /* GNU SASL uses one top-level callback, registered at library level.
182 We dispatch to client and server functions instead. */
183
184 static int
185 main_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop)
186 {
187 int rc = 0;
188 struct callback_exim_state *cb_state =
189 (struct callback_exim_state *)gsasl_session_hook_get(sctx);
190
191 HDEBUG(D_auth)
192 debug_printf("GNU SASL Callback entered, prop=%d (loop prop=%d)\n",
193 prop, callback_loop);
194
195 if (cb_state == NULL) {
196 HDEBUG(D_auth) debug_printf(" not from our server/client processing.\n");
197 return GSASL_NO_CALLBACK;
198 }
199
200 if (callback_loop > 0) {
201 /* Most likely is that we were asked for property foo, and to
202 expand the string we asked for property bar to put into an auth
203 variable, but property bar is not supplied for this mechanism. */
204 HDEBUG(D_auth)
205 debug_printf("Loop, asked for property %d while handling property %d\n",
206 prop, callback_loop);
207 return GSASL_NO_CALLBACK;
208 }
209 callback_loop = prop;
210
211 if (cb_state->currently == CURRENTLY_CLIENT)
212 rc = client_callback(ctx, sctx, prop, cb_state->ablock);
213 else if (cb_state->currently == CURRENTLY_SERVER)
214 rc = server_callback(ctx, sctx, prop, cb_state->ablock);
215 else {
216 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
217 "unhandled callback state, bug in Exim", cb_state->ablock->name);
218 /* NOTREACHED */
219 }
220
221 callback_loop = 0;
222 return rc;
223 }
224
225
226 /*************************************************
227 * Server entry point *
228 *************************************************/
229
230 /* For interface, see auths/README */
231
232 int
233 auth_gsasl_server(auth_instance *ablock, uschar *initial_data)
234 {
235 char *tmps;
236 char *to_send, *received;
237 Gsasl_session *sctx = NULL;
238 auth_gsasl_options_block *ob =
239 (auth_gsasl_options_block *)(ablock->options_block);
240 struct callback_exim_state cb_state;
241 int rc, auth_result, exim_error, exim_error_override;
242
243 HDEBUG(D_auth)
244 debug_printf("GNU SASL: initialising session for %s, mechanism %s.\n",
245 ablock->name, ob->server_mech);
246
247 rc = gsasl_server_start(gsasl_ctx, (const char *)ob->server_mech, &sctx);
248 if (rc != GSASL_OK) {
249 auth_defer_msg = string_sprintf("GNU SASL: session start failure: %s (%s)",
250 gsasl_strerror_name(rc), gsasl_strerror(rc));
251 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
252 return DEFER;
253 }
254 /* Hereafter: gsasl_finish(sctx) please */
255
256 gsasl_session_hook_set(sctx, (void *)ablock);
257 cb_state.ablock = ablock;
258 cb_state.currently = CURRENTLY_SERVER;
259 gsasl_session_hook_set(sctx, (void *)&cb_state);
260
261 tmps = CS expand_string(ob->server_service);
262 gsasl_property_set(sctx, GSASL_SERVICE, tmps);
263 tmps = CS expand_string(ob->server_hostname);
264 gsasl_property_set(sctx, GSASL_HOSTNAME, tmps);
265 if (ob->server_realm) {
266 tmps = CS expand_string(ob->server_realm);
267 if (tmps && *tmps) {
268 gsasl_property_set(sctx, GSASL_REALM, tmps);
269 }
270 }
271 /* We don't support protection layers. */
272 gsasl_property_set(sctx, GSASL_QOPS, "qop-auth");
273 #ifdef SUPPORT_TLS
274 if (tls_channelbinding_b64) {
275 /* Some auth mechanisms can ensure that both sides are talking withing the
276 same security context; for TLS, this means that even if a bad certificate
277 has been accepted, they remain MitM-proof because both sides must be within
278 the same negotiated session; if someone is terminating one sesson and
279 proxying data on within a second, authentication will fail.
280
281 We might not have this available, depending upon TLS implementation,
282 ciphersuite, phase of moon ...
283
284 If we do, it results in extra SASL mechanisms being available; here,
285 Exim's one-mechanism-per-authenticator potentially causes problems.
286 It depends upon how GNU SASL will implement the PLUS variants of GS2
287 and whether it automatically mandates a switch to the bound PLUS
288 if the data is available. Since default-on, despite being more secure,
289 would then result in mechanism name changes on a library update, we
290 have little choice but to default it off and let the admin choose to
291 enable it. *sigh*
292 */
293 if (ob->server_channelbinding) {
294 HDEBUG(D_auth) debug_printf("Auth %s: Enabling channel-binding\n",
295 ablock->name);
296 gsasl_property_set(sctx, GSASL_CB_TLS_UNIQUE,
297 (const char *) tls_channelbinding_b64);
298 } else {
299 HDEBUG(D_auth)
300 debug_printf("Auth %s: Not enabling channel-binding (data available)\n",
301 ablock->name);
302 }
303 } else {
304 HDEBUG(D_auth)
305 debug_printf("Auth %s: no channel-binding data available\n",
306 ablock->name);
307 }
308 #endif
309
310 checked_server_condition = FALSE;
311
312 received = CS initial_data;
313 to_send = NULL;
314 exim_error = exim_error_override = OK;
315
316 do {
317 rc = gsasl_step64(sctx, received, &to_send);
318
319 switch (rc) {
320 case GSASL_OK:
321 if (!to_send)
322 goto STOP_INTERACTION;
323 break;
324
325 case GSASL_NEEDS_MORE:
326 break;
327
328 case GSASL_AUTHENTICATION_ERROR:
329 case GSASL_INTEGRITY_ERROR:
330 case GSASL_NO_AUTHID:
331 case GSASL_NO_ANONYMOUS_TOKEN:
332 case GSASL_NO_AUTHZID:
333 case GSASL_NO_PASSWORD:
334 case GSASL_NO_PASSCODE:
335 case GSASL_NO_PIN:
336 case GSASL_BASE64_ERROR:
337 HDEBUG(D_auth) debug_printf("GNU SASL permanent error: %s (%s)\n",
338 gsasl_strerror_name(rc), gsasl_strerror(rc));
339 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
340 "GNU SASL permanent failure: %s (%s)",
341 ablock->name, ob->server_mech,
342 gsasl_strerror_name(rc), gsasl_strerror(rc));
343 if (rc == GSASL_BASE64_ERROR)
344 exim_error_override = BAD64;
345 goto STOP_INTERACTION;
346
347 default:
348 auth_defer_msg = string_sprintf("GNU SASL temporary error: %s (%s)",
349 gsasl_strerror_name(rc), gsasl_strerror(rc));
350 HDEBUG(D_auth) debug_printf("%s\n", auth_defer_msg);
351 exim_error_override = DEFER;
352 goto STOP_INTERACTION;
353 }
354
355 if ((rc == GSASL_NEEDS_MORE) ||
356 (to_send && *to_send))
357 exim_error =
358 auth_get_no64_data((uschar **)&received, (uschar *)to_send);
359
360 if (to_send) {
361 free(to_send);
362 to_send = NULL;
363 }
364
365 if (exim_error)
366 break; /* handles * cancelled check */
367
368 } while (rc == GSASL_NEEDS_MORE);
369
370 STOP_INTERACTION:
371 auth_result = rc;
372
373 gsasl_finish(sctx);
374
375 /* Can return: OK DEFER FAIL CANCELLED BAD64 UNEXPECTED */
376
377 if (exim_error != OK)
378 return exim_error;
379
380 if (auth_result != GSASL_OK) {
381 HDEBUG(D_auth) debug_printf("authentication returned %s (%s)\n",
382 gsasl_strerror_name(auth_result), gsasl_strerror(auth_result));
383 if (exim_error_override != OK)
384 return exim_error_override; /* might be DEFER */
385 if (sasl_error_should_defer) /* overriding auth failure SASL error */
386 return DEFER;
387 return FAIL;
388 }
389
390 /* Auth succeeded, check server_condition unless already done in callback */
391 return checked_server_condition ? OK : auth_check_serv_cond(ablock);
392 }
393
394 /* returns the GSASL status of expanding the Exim string given */
395 static int
396 condition_check(auth_instance *ablock, uschar *label, uschar *condition_string)
397 {
398 int exim_rc;
399
400 exim_rc = auth_check_some_cond(ablock, label, condition_string, FAIL);
401
402 if (exim_rc == OK) {
403 return GSASL_OK;
404 } else if (exim_rc == DEFER) {
405 sasl_error_should_defer = TRUE;
406 return GSASL_AUTHENTICATION_ERROR;
407 } else if (exim_rc == FAIL) {
408 return GSASL_AUTHENTICATION_ERROR;
409 }
410
411 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
412 "Unhandled return from checking %s: %d",
413 ablock->name, label, exim_rc);
414 /* NOTREACHED */
415 return GSASL_AUTHENTICATION_ERROR;
416 }
417
418 static int
419 server_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
420 {
421 char *tmps;
422 uschar *propval;
423 int cbrc = GSASL_NO_CALLBACK;
424 int i;
425 auth_gsasl_options_block *ob =
426 (auth_gsasl_options_block *)(ablock->options_block);
427
428 HDEBUG(D_auth)
429 debug_printf("GNU SASL callback %d for %s/%s as server\n",
430 prop, ablock->name, ablock->public_name);
431
432 for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
433 expand_nmax = 0;
434
435 switch (prop) {
436 case GSASL_VALIDATE_SIMPLE:
437 /* GSASL_AUTHID, GSASL_AUTHZID, and GSASL_PASSWORD */
438 propval = (uschar *) gsasl_property_fast(sctx, GSASL_AUTHID);
439 auth_vars[0] = expand_nstring[1] = propval ? propval : US"";
440 propval = (uschar *) gsasl_property_fast(sctx, GSASL_AUTHZID);
441 auth_vars[1] = expand_nstring[2] = propval ? propval : US"";
442 propval = (uschar *) gsasl_property_fast(sctx, GSASL_PASSWORD);
443 auth_vars[2] = expand_nstring[3] = propval ? propval : US"";
444 expand_nmax = 3;
445 for (i = 1; i <= 3; ++i)
446 expand_nlength[i] = Ustrlen(expand_nstring[i]);
447
448 cbrc = condition_check(ablock, US"server_condition", ablock->server_condition);
449 checked_server_condition = TRUE;
450 break;
451
452 case GSASL_VALIDATE_EXTERNAL:
453 if (ablock->server_condition == NULL) {
454 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate EXTERNAL.\n");
455 cbrc = GSASL_AUTHENTICATION_ERROR;
456 break;
457 }
458 propval = (uschar *) gsasl_property_fast(sctx, GSASL_AUTHZID);
459 /* We always set $auth1, even if only to empty string. */
460 auth_vars[0] = expand_nstring[1] = propval ? propval : US"";
461 expand_nlength[1] = Ustrlen(expand_nstring[1]);
462 expand_nmax = 1;
463
464 cbrc = condition_check(ablock,
465 US"server_condition (EXTERNAL)", ablock->server_condition);
466 checked_server_condition = TRUE;
467 break;
468
469 case GSASL_VALIDATE_ANONYMOUS:
470 if (ablock->server_condition == NULL) {
471 HDEBUG(D_auth) debug_printf("No server_condition supplied, to validate ANONYMOUS.\n");
472 cbrc = GSASL_AUTHENTICATION_ERROR;
473 break;
474 }
475 propval = (uschar *) gsasl_property_fast(sctx, GSASL_ANONYMOUS_TOKEN);
476 /* We always set $auth1, even if only to empty string. */
477 auth_vars[0] = expand_nstring[1] = propval ? propval : US"";
478 expand_nlength[1] = Ustrlen(expand_nstring[1]);
479 expand_nmax = 1;
480
481 cbrc = condition_check(ablock,
482 US"server_condition (ANONYMOUS)", ablock->server_condition);
483 checked_server_condition = TRUE;
484 break;
485
486 case GSASL_VALIDATE_GSSAPI:
487 /* GSASL_AUTHZID and GSASL_GSSAPI_DISPLAY_NAME
488 The display-name is authenticated as part of GSS, the authzid is claimed
489 by the SASL integration after authentication; protected against tampering
490 (if the SASL mechanism supports that, which Kerberos does) but is
491 unverified, same as normal for other mechanisms.
492
493 First coding, we had these values swapped, but for consistency and prior
494 to the first release of Exim with this authenticator, they've been
495 switched to match the ordering of GSASL_VALIDATE_SIMPLE. */
496 propval = (uschar *) gsasl_property_fast(sctx, GSASL_GSSAPI_DISPLAY_NAME);
497 auth_vars[0] = expand_nstring[1] = propval ? propval : US"";
498 propval = (uschar *) gsasl_property_fast(sctx, GSASL_AUTHZID);
499 auth_vars[1] = expand_nstring[2] = propval ? propval : US"";
500 expand_nmax = 2;
501 for (i = 1; i <= 2; ++i)
502 expand_nlength[i] = Ustrlen(expand_nstring[i]);
503
504 /* In this one case, it perhaps makes sense to default back open?
505 But for consistency, let's just mandate server_condition here too. */
506 cbrc = condition_check(ablock,
507 US"server_condition (GSSAPI family)", ablock->server_condition);
508 checked_server_condition = TRUE;
509 break;
510
511 case GSASL_PASSWORD:
512 /* DIGEST-MD5: GSASL_AUTHID, GSASL_AUTHZID and GSASL_REALM
513 CRAM-MD5: GSASL_AUTHID
514 PLAIN: GSASL_AUTHID and GSASL_AUTHZID
515 LOGIN: GSASL_AUTHID
516 */
517 if (ob->server_scram_iter) {
518 tmps = CS expand_string(ob->server_scram_iter);
519 gsasl_property_set(sctx, GSASL_SCRAM_ITER, tmps);
520 }
521 if (ob->server_scram_salt) {
522 tmps = CS expand_string(ob->server_scram_salt);
523 gsasl_property_set(sctx, GSASL_SCRAM_SALT, tmps);
524 }
525 /* Asking for GSASL_AUTHZID calls back into us if we use
526 gsasl_property_get(), thus the use of gsasl_property_fast().
527 Do we really want to hardcode limits per mechanism? What happens when
528 a new mechanism is added to the library. It *shouldn't* result in us
529 needing to add more glue, since avoiding that is a large part of the
530 point of SASL. */
531 propval = (uschar *) gsasl_property_fast(sctx, GSASL_AUTHID);
532 auth_vars[0] = expand_nstring[1] = propval ? propval : US"";
533 propval = (uschar *) gsasl_property_fast(sctx, GSASL_AUTHZID);
534 auth_vars[1] = expand_nstring[2] = propval ? propval : US"";
535 propval = (uschar *) gsasl_property_fast(sctx, GSASL_REALM);
536 auth_vars[2] = expand_nstring[3] = propval ? propval : US"";
537 expand_nmax = 3;
538 for (i = 1; i <= 3; ++i)
539 expand_nlength[i] = Ustrlen(expand_nstring[i]);
540
541 tmps = CS expand_string(ob->server_password);
542 if (tmps == NULL) {
543 sasl_error_should_defer = expand_string_forcedfail ? FALSE : TRUE;
544 HDEBUG(D_auth) debug_printf("server_password expansion failed, so "
545 "can't tell GNU SASL library the password for %s\n", auth_vars[0]);
546 return GSASL_AUTHENTICATION_ERROR;
547 }
548 gsasl_property_set(sctx, GSASL_PASSWORD, tmps);
549 /* This is inadequate; don't think Exim's store stacks are geared
550 for memory wiping, so expanding strings will leave stuff laying around.
551 But no need to compound the problem, so get rid of the one we can. */
552 memset(tmps, '\0', strlen(tmps));
553 cbrc = GSASL_OK;
554 break;
555
556 default:
557 HDEBUG(D_auth) debug_printf("Unrecognised callback: %d\n", prop);
558 cbrc = GSASL_NO_CALLBACK;
559 }
560
561 HDEBUG(D_auth) debug_printf("Returning %s (%s)\n",
562 gsasl_strerror_name(cbrc), gsasl_strerror(cbrc));
563
564 return cbrc;
565 }
566
567
568 /*************************************************
569 * Client entry point *
570 *************************************************/
571
572 /* For interface, see auths/README */
573
574 int
575 auth_gsasl_client(
576 auth_instance *ablock, /* authenticator block */
577 smtp_inblock *inblock, /* connection inblock */
578 smtp_outblock *outblock, /* connection outblock */
579 int timeout, /* command timeout */
580 uschar *buffer, /* buffer for reading response */
581 int buffsize) /* size of buffer */
582 {
583 HDEBUG(D_auth)
584 debug_printf("Client side NOT IMPLEMENTED: you should not see this!\n");
585 /* NOT IMPLEMENTED */
586 return FAIL;
587 }
588
589 static int
590 client_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop, auth_instance *ablock)
591 {
592 int cbrc = GSASL_NO_CALLBACK;
593 HDEBUG(D_auth)
594 debug_printf("GNU SASL callback %d for %s/%s as client\n",
595 prop, ablock->name, ablock->public_name);
596
597 HDEBUG(D_auth)
598 debug_printf("Client side NOT IMPLEMENTED: you should not see this!\n");
599
600 return cbrc;
601 }
602
603 /*************************************************
604 * Diagnostic API *
605 *************************************************/
606
607 void
608 auth_gsasl_version_report(FILE *f)
609 {
610 const char *runtime;
611 runtime = gsasl_check_version(NULL);
612 fprintf(f, "Library version: GNU SASL: Compile: %s\n"
613 " Runtime: %s\n",
614 GSASL_VERSION, runtime);
615 }
616
617 #endif /* AUTH_GSASL */
618
619 /* End of gsasl_exim.c */