87be9b5e1d8c4422c628eff4b77af3b88be9e830
[exim.git] / src / 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 */