Compiler quietening: C fn ptrs are officially not intercastable with object pointers
[exim.git] / src / src / auths / cyrus_sasl.c
CommitLineData
0756eb3c
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
f9ba5e22 5/* Copyright (c) University of Cambridge 1995 - 2018 */
0756eb3c
PH
6/* See the file NOTICE for conditions of use and distribution. */
7
c5ddb310 8/* This code was originally contributed by Matthew Byng-Maddick */
0756eb3c
PH
9
10/* Copyright (c) A L Digital 2004 */
11
12/* A generic (mechanism independent) Cyrus SASL authenticator. */
13
14
15#include "../exim.h"
16
17
18/* We can't just compile this code and allow the library mechanism to omit the
19functions if they are not wanted, because we need to have the Cyrus SASL header
20available for compiling. Therefore, compile these functions only if
21AUTH_CYRUS_SASL is defined. However, some compilers don't like compiling empty
22modules, so keep them happy with a dummy when skipping the rest. Make it
23reference itself to stop picky compilers complaining that it is unused, and put
24in a dummy argument to stop even pickier compilers complaining about infinite
25loops. */
26
27#ifndef AUTH_CYRUS_SASL
e1d15f5e
JH
28static void dummy(int x);
29static void dummy2(int x) { dummy(x-1); }
30static void dummy(int x) { dummy2(x-1); }
0756eb3c
PH
31#else
32
33
34#include <sasl/sasl.h>
35#include "cyrus_sasl.h"
36
37/* Options specific to the cyrus_sasl authentication mechanism. */
38
39optionlist auth_cyrus_sasl_options[] = {
40 { "server_hostname", opt_stringptr,
41 (void *)(offsetof(auth_cyrus_sasl_options_block, server_hostname)) },
42 { "server_mech", opt_stringptr,
43 (void *)(offsetof(auth_cyrus_sasl_options_block, server_mech)) },
44 { "server_realm", opt_stringptr,
45 (void *)(offsetof(auth_cyrus_sasl_options_block, server_realm)) },
46 { "server_service", opt_stringptr,
47 (void *)(offsetof(auth_cyrus_sasl_options_block, server_service)) }
48};
49
50/* Size of the options list. An extern variable has to be used so that its
51address can appear in the tables drtables.c. */
52
53int auth_cyrus_sasl_options_count =
54 sizeof(auth_cyrus_sasl_options)/sizeof(optionlist);
55
16ff981e 56/* Default private options block for the cyrus_sasl authentication method. */
0756eb3c
PH
57
58auth_cyrus_sasl_options_block auth_cyrus_sasl_option_defaults = {
59 US"smtp", /* server_service */
60 US"$primary_hostname", /* server_hostname */
61 NULL, /* server_realm */
62 NULL /* server_mech */
63};
64
65
d185889f
JH
66#ifdef MACRO_PREDEF
67
68/* Dummy values */
69void auth_cyrus_sasl_init(auth_instance *ablock) {}
70int auth_cyrus_sasl_server(auth_instance *ablock, uschar *data) {return 0;}
251b9eb4
JH
71int auth_cyrus_sasl_client(auth_instance *ablock, void * sx,
72 int timeout, uschar *buffer, int buffsize) {return 0;}
f9df71c0 73void auth_cyrus_sasl_version_report(FILE *f) {}
d185889f
JH
74
75#else /*!MACRO_PREDEF*/
76
77
78
79
0756eb3c
PH
80/*************************************************
81* Initialization entry point *
82*************************************************/
83
84/* Called for each instance, after its options have been read, to
85enable consistency checks to be done, or anything else that needs
86to be set up. */
87
c5ddb310
PH
88
89/* Auxiliary function, passed in data to sasl_server_init(). */
90
91static int
6a2c32cb
JH
92mysasl_config(void *context, const char *plugin_name, const char *option,
93 const char **result, unsigned int *len)
c5ddb310
PH
94{
95if (context && !strcmp(option, "mech_list"))
96 {
97 *result = context;
6a2c32cb 98 if (len) *len = strlen(*result);
c5ddb310
PH
99 return SASL_OK;
100 }
101return SASL_FAIL;
102}
103
104/* Here's the real function */
105
0756eb3c
PH
106void
107auth_cyrus_sasl_init(auth_instance *ablock)
108{
109auth_cyrus_sasl_options_block *ob =
110 (auth_cyrus_sasl_options_block *)(ablock->options_block);
93a6fce2 111const uschar *list, *listptr, *buffer;
0756eb3c
PH
112int rc, i;
113unsigned int len;
f3ebb786
JH
114rmark rs_point;
115uschar *expanded_hostname;
4c287009 116char *realm_expanded;
0756eb3c 117
c5ddb310 118sasl_conn_t *conn;
c0fb53b7 119sasl_callback_t cbs[] = {
c5ddb310
PH
120 {SASL_CB_GETOPT, NULL, NULL },
121 {SASL_CB_LIST_END, NULL, NULL}};
122
0756eb3c 123/* default the mechanism to our "public name" */
0756eb3c 124
6a2c32cb
JH
125if (!ob->server_mech) ob->server_mech = string_copy(ablock->public_name);
126
127if (!(expanded_hostname = expand_string(ob->server_hostname)))
edc33b5f
PP
128 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
129 "couldn't expand server_hostname [%s]: %s",
130 ablock->name, ob->server_hostname, expand_string_message);
131
c0fb53b7 132realm_expanded = NULL;
6a2c32cb
JH
133if ( ob->server_realm
134 && !(realm_expanded = CS expand_string(ob->server_realm)))
135 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
136 "couldn't expand server_realm [%s]: %s",
137 ablock->name, ob->server_realm, expand_string_message);
4c287009 138
0756eb3c 139/* we're going to initialise the library to check that there is an
6a2c32cb 140authenticator of type whatever mechanism we're using */
c5ddb310 141
edc33b5f 142cbs[0].proc = (int(*)(void)) &mysasl_config;
c5ddb310
PH
143cbs[0].context = ob->server_mech;
144
6a2c32cb 145if ((rc = sasl_server_init(cbs, "exim")) != SASL_OK)
0756eb3c
PH
146 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
147 "couldn't initialise Cyrus SASL library.", ablock->name);
148
c0fb53b7 149if ((rc = sasl_server_new(CS ob->server_service, CS expanded_hostname,
6a2c32cb 150 realm_expanded, NULL, NULL, NULL, 0, &conn)) != SASL_OK)
0756eb3c
PH
151 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
152 "couldn't initialise Cyrus SASL server connection.", ablock->name);
153
3d2e82c5 154if ((rc = sasl_listmech(conn, NULL, "", ":", "", CCSS &list, &len, &i)) != SASL_OK)
0756eb3c
PH
155 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
156 "couldn't get Cyrus SASL mechanism list.", ablock->name);
157
c0fb53b7
JH
158i = ':';
159listptr = list;
0756eb3c 160
c0fb53b7
JH
161HDEBUG(D_auth)
162 {
edc33b5f 163 debug_printf("Initialised Cyrus SASL service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
4c287009 164 ob->server_service, expanded_hostname, realm_expanded);
edc33b5f 165 debug_printf("Cyrus SASL knows mechanisms: %s\n", list);
c0fb53b7 166 }
0756eb3c
PH
167
168/* the store_get / store_reset mechanism is hierarchical
6a2c32cb
JH
169 the hierarchy is stored for us behind our back. This point
170 creates a hierarchy point for this function. */
171
f3ebb786 172rs_point = store_mark();
0756eb3c
PH
173
174/* loop until either we get to the end of the list, or we match the
6a2c32cb
JH
175public name of this authenticator */
176
177while ( (buffer = string_nextinlist(&listptr, &i, NULL, 0))
178 && strcmpic(buffer,ob->server_mech) );
0756eb3c 179
c0fb53b7 180if (!buffer)
0756eb3c
PH
181 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
182 "Cyrus SASL doesn't know about mechanism %s.", ablock->name, ob->server_mech);
183
184store_reset(rs_point);
185
186HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
187
188/* make sure that if we get here then we're allowed to advertise. */
189ablock->server = TRUE;
190
191sasl_dispose(&conn);
192sasl_done();
193}
194
195/*************************************************
196* Server entry point *
197*************************************************/
198
199/* For interface, see auths/README */
200
201/* note, we don't care too much about memory allocation in this, because this is entirely
6a2c32cb 202within a shortlived child */
0756eb3c
PH
203
204int
205auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
206{
207auth_cyrus_sasl_options_block *ob =
208 (auth_cyrus_sasl_options_block *)(ablock->options_block);
209uschar *output, *out2, *input, *clear, *hname;
210uschar *debug = NULL; /* Stops compiler complaining */
c0fb53b7 211sasl_callback_t cbs[] = {{SASL_CB_LIST_END, NULL, NULL}};
0756eb3c 212sasl_conn_t *conn;
c0fb53b7 213char * realm_expanded = NULL;
d7978c0f 214int rc, firsttime = 1, clen, *negotiated_ssf_ptr = NULL, negotiated_ssf;
0756eb3c
PH
215unsigned int inlen, outlen;
216
c0fb53b7
JH
217input = data;
218inlen = Ustrlen(data);
0756eb3c 219
c0fb53b7 220HDEBUG(D_auth) debug = string_copy(data);
0756eb3c 221
c0fb53b7 222hname = expand_string(ob->server_hostname);
4c287009 223if (hname && ob->server_realm)
c0fb53b7
JH
224 realm_expanded = CS expand_string(ob->server_realm);
225if (!hname || !realm_expanded && ob->server_realm)
0756eb3c
PH
226 {
227 auth_defer_msg = expand_string_message;
228 return DEFER;
229 }
230
c0fb53b7 231if (inlen)
0756eb3c 232 {
c0fb53b7 233 if ((clen = b64decode(input, &clear)) < 0)
0756eb3c 234 return BAD64;
c0fb53b7
JH
235 input = clear;
236 inlen = clen;
0756eb3c
PH
237 }
238
c0fb53b7 239if ((rc = sasl_server_init(cbs, "exim")) != SASL_OK)
0756eb3c
PH
240 {
241 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
242 return DEFER;
243 }
244
c0fb53b7 245rc = sasl_server_new(CS ob->server_service, CS hname, realm_expanded, NULL,
acb1b346
PH
246 NULL, NULL, 0, &conn);
247
edc33b5f
PP
248HDEBUG(D_auth)
249 debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
4c287009 250 ob->server_service, hname, realm_expanded);
edc33b5f 251
c0fb53b7 252if (rc != SASL_OK )
0756eb3c
PH
253 {
254 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
255 sasl_done();
256 return DEFER;
257 }
258
817d9f57 259if (tls_in.cipher)
edc33b5f 260 {
c0fb53b7 261 if ((rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_in.bits)) != SASL_OK)
edc33b5f
PP
262 {
263 HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n",
817d9f57 264 tls_in.bits, sasl_errstring(rc, NULL, NULL));
edc33b5f
PP
265 auth_defer_msg = US"couldn't set Cyrus SASL EXTERNAL SSF";
266 sasl_done();
267 return DEFER;
268 }
269 else
817d9f57 270 HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_in.bits);
6a2c32cb
JH
271
272 /*XXX Set channel-binding here with sasl_channel_binding_t / SASL_CHANNEL_BINDING
273 Unclear what the "name" element does though, ditto the "critical" flag. */
edc33b5f
PP
274 }
275else
276 HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");
277
66645890
PP
278/* So sasl_setprop() documents non-shorted IPv6 addresses which is incredibly
279annoying; looking at cyrus-imapd-2.3.x source, the IP address is constructed
280with their iptostring() function, which just wraps
281getnameinfo(..., NI_NUMERICHOST|NI_NUMERICSERV), which is equivalent to the
282inet_ntop which we wrap in our host_ntoa() function.
283
284So the docs are too strict and we shouldn't worry about :: contractions. */
285
286/* Set properties for remote and local host-ip;port */
d7978c0f 287for (int i = 0; i < 2; ++i)
66645890 288 {
6a2c32cb
JH
289 int propnum;
290 const uschar * label;
291 uschar * address_port;
66645890
PP
292 const char *s_err;
293 socklen_t sslen;
294
295 if (i)
296 {
66645890
PP
297 propnum = SASL_IPREMOTEPORT;
298 label = CUS"peer";
6a2c32cb
JH
299 address_port = string_sprintf("%s;%d",
300 sender_host_address, sender_host_port);
66645890
PP
301 }
302 else
303 {
66645890
PP
304 propnum = SASL_IPLOCALPORT;
305 label = CUS"local";
6a2c32cb 306 address_port = string_sprintf("%s;%d", interface_address, interface_port);
66645890
PP
307 }
308
c0fb53b7 309 if ((rc = sasl_setprop(conn, propnum, address_port)) != SASL_OK)
66645890 310 {
66645890 311 HDEBUG(D_auth)
6a2c32cb
JH
312 {
313 s_err = sasl_errdetail(conn);
66645890
PP
314 debug_printf("Failed to set %s SASL property: [%d] %s\n",
315 label, rc, s_err ? s_err : "<unknown reason>");
6a2c32cb 316 }
66645890
PP
317 break;
318 }
319 HDEBUG(D_auth) debug_printf("Cyrus SASL set %s hostport to: %s\n",
320 label, address_port);
321 }
322
c0fb53b7 323for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; )
0756eb3c 324 {
c0fb53b7 325 if (firsttime)
0756eb3c 326 {
c0fb53b7 327 firsttime = 0;
0756eb3c 328 HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
3d2e82c5
JH
329 rc = sasl_server_start(conn, CS ob->server_mech, inlen ? CS input : NULL, inlen,
330 CCSS &output, &outlen);
0756eb3c
PH
331 }
332 else
333 {
334 /* make sure that we have a null-terminated string */
c0fb53b7
JH
335 out2 = string_copyn(output, outlen);
336
337 if ((rc = auth_get_data(&input, out2, outlen)) != OK)
0756eb3c
PH
338 {
339 /* we couldn't get the data, so free up the library before
6a2c32cb 340 returning whatever error we get */
0756eb3c
PH
341 sasl_dispose(&conn);
342 sasl_done();
343 return rc;
344 }
c0fb53b7 345 inlen = Ustrlen(input);
0756eb3c 346
c0fb53b7
JH
347 HDEBUG(D_auth) debug = string_copy(input);
348 if (inlen)
0756eb3c 349 {
c0fb53b7 350 if ((clen = b64decode(input, &clear)) < 0)
0756eb3c 351 {
c0fb53b7
JH
352 sasl_dispose(&conn);
353 sasl_done();
0756eb3c
PH
354 return BAD64;
355 }
c0fb53b7
JH
356 input = clear;
357 inlen = clen;
0756eb3c
PH
358 }
359
360 HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
3d2e82c5 361 rc = sasl_server_step(conn, CS input, inlen, CCSS &output, &outlen);
0756eb3c 362 }
c0fb53b7
JH
363
364 if (rc == SASL_BADPROT)
0756eb3c
PH
365 {
366 sasl_dispose(&conn);
367 sasl_done();
368 return UNEXPECTED;
369 }
c0fb53b7
JH
370 if (rc == SASL_CONTINUE)
371 continue;
372
373 /* Get the username and copy it into $auth1 and $1. The former is now the
374 preferred variable; the latter is the original variable. */
375
376 if ((sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2))) != SASL_OK)
0756eb3c 377 {
0756eb3c 378 HDEBUG(D_auth)
c0fb53b7
JH
379 debug_printf("Cyrus SASL library will not tell us the username: %s\n",
380 sasl_errstring(rc, NULL, NULL));
0756eb3c 381 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
c0fb53b7 382 "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech,
0756eb3c
PH
383 sasl_errstring(rc, NULL, NULL));
384 sasl_dispose(&conn);
385 sasl_done();
386 return FAIL;
387 }
c0fb53b7
JH
388 auth_vars[0] = expand_nstring[1] = string_copy(out2);
389 expand_nlength[1] = Ustrlen(out2);
390 expand_nmax = 1;
391
392 switch (rc)
0756eb3c 393 {
c0fb53b7
JH
394 case SASL_FAIL: case SASL_BUFOVER: case SASL_BADMAC: case SASL_BADAUTH:
395 case SASL_NOAUTHZ: case SASL_ENCRYPT: case SASL_EXPIRED:
396 case SASL_DISABLED: case SASL_NOUSER:
397 /* these are considered permanent failure codes */
edc33b5f 398 HDEBUG(D_auth)
c0fb53b7 399 debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
edc33b5f 400 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
c0fb53b7
JH
401 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
402 sasl_errstring(rc, NULL, NULL));
edc33b5f
PP
403 sasl_dispose(&conn);
404 sasl_done();
405 return FAIL;
0756eb3c 406
c0fb53b7
JH
407 case SASL_NOMECH:
408 /* this is a temporary failure, because the mechanism is not
6a2c32cb
JH
409 available for this user. If it wasn't available at all, we
410 shouldn't have got here in the first place... */
411
edc33b5f 412 HDEBUG(D_auth)
c0fb53b7
JH
413 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
414 auth_defer_msg =
415 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
edc33b5f
PP
416 sasl_dispose(&conn);
417 sasl_done();
c0fb53b7
JH
418 return DEFER;
419
420 case SASL_OK:
edc33b5f 421 HDEBUG(D_auth)
c0fb53b7
JH
422 debug_printf("Cyrus SASL %s authentication succeeded for %s\n",
423 ob->server_mech, auth_vars[0]);
424
425 if ((rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf_ptr)))!= SASL_OK)
426 {
427 HDEBUG(D_auth)
428 debug_printf("Cyrus SASL library will not tell us the SSF: %s\n",
429 sasl_errstring(rc, NULL, NULL));
430 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
431 "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech,
432 sasl_errstring(rc, NULL, NULL));
433 sasl_dispose(&conn);
434 sasl_done();
435 return FAIL;
436 }
437 negotiated_ssf = *negotiated_ssf_ptr;
438 HDEBUG(D_auth)
439 debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf);
440 if (negotiated_ssf > 0)
441 {
442 HDEBUG(D_auth)
443 debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf);
444 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
445 "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf);
446 sasl_dispose(&conn);
447 sasl_done();
448 return FAIL;
449 }
450
451 /* close down the connection, freeing up library's memory */
edc33b5f
PP
452 sasl_dispose(&conn);
453 sasl_done();
edc33b5f 454
c0fb53b7
JH
455 /* Expand server_condition as an authorization check */
456 return auth_check_serv_cond(ablock);
16ff981e 457
c0fb53b7
JH
458 default:
459 /* Anything else is a temporary failure, and we'll let SASL print out
460 * the error string for us
461 */
462 HDEBUG(D_auth)
463 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
464 auth_defer_msg =
465 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
466 sasl_dispose(&conn);
467 sasl_done();
468 return DEFER;
0756eb3c
PH
469 }
470 }
471/* NOTREACHED */
472return 0; /* Stop compiler complaints */
473}
474
6545de78
PP
475/*************************************************
476* Diagnostic API *
477*************************************************/
478
479void
480auth_cyrus_sasl_version_report(FILE *f)
481{
c0fb53b7
JH
482const char *implementation, *version;
483sasl_version_info(&implementation, &version, NULL, NULL, NULL, NULL);
484fprintf(f, "Library version: Cyrus SASL: Compile: %d.%d.%d\n"
485 " Runtime: %s [%s]\n",
486 SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
487 version, implementation);
6545de78
PP
488}
489
0756eb3c
PH
490/*************************************************
491* Client entry point *
492*************************************************/
493
494/* For interface, see auths/README */
495
496int
497auth_cyrus_sasl_client(
498 auth_instance *ablock, /* authenticator block */
251b9eb4 499 void * sx, /* connexction */
0756eb3c 500 int timeout, /* command timeout */
d7978c0f 501 uschar *buffer, /* for reading response */
0756eb3c
PH
502 int buffsize) /* size of buffer */
503{
504/* We don't support clients (yet) in this implementation of cyrus_sasl */
505return FAIL;
506}
507
d185889f 508#endif /*!MACRO_PREDEF*/
0756eb3c
PH
509#endif /* AUTH_CYRUS_SASL */
510
511/* End of cyrus_sasl.c */