Track tainted data and refuse to expand it
[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
92mysasl_config(void *context,
93 const char *plugin_name,
94 const char *option,
95 const char **result,
96 unsigned int *len)
97{
98if (context && !strcmp(option, "mech_list"))
99 {
100 *result = context;
101 if (len != NULL) *len = strlen(*result);
102 return SASL_OK;
103 }
104return SASL_FAIL;
105}
106
107/* Here's the real function */
108
0756eb3c
PH
109void
110auth_cyrus_sasl_init(auth_instance *ablock)
111{
112auth_cyrus_sasl_options_block *ob =
113 (auth_cyrus_sasl_options_block *)(ablock->options_block);
93a6fce2 114const uschar *list, *listptr, *buffer;
0756eb3c
PH
115int rc, i;
116unsigned int len;
f3ebb786
JH
117rmark rs_point;
118uschar *expanded_hostname;
4c287009 119char *realm_expanded;
0756eb3c 120
c5ddb310 121sasl_conn_t *conn;
c0fb53b7 122sasl_callback_t cbs[] = {
c5ddb310
PH
123 {SASL_CB_GETOPT, NULL, NULL },
124 {SASL_CB_LIST_END, NULL, NULL}};
125
0756eb3c 126/* default the mechanism to our "public name" */
c0fb53b7
JH
127if (ob->server_mech == NULL)
128 ob->server_mech = string_copy(ablock->public_name);
0756eb3c 129
edc33b5f
PP
130expanded_hostname = expand_string(ob->server_hostname);
131if (expanded_hostname == NULL)
132 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
133 "couldn't expand server_hostname [%s]: %s",
134 ablock->name, ob->server_hostname, expand_string_message);
135
c0fb53b7 136realm_expanded = NULL;
4c287009
PP
137if (ob->server_realm != NULL) {
138 realm_expanded = CS expand_string(ob->server_realm);
139 if (realm_expanded == NULL)
140 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
141 "couldn't expand server_realm [%s]: %s",
142 ablock->name, ob->server_realm, expand_string_message);
143}
144
0756eb3c
PH
145/* we're going to initialise the library to check that there is an
146 * authenticator of type whatever mechanism we're using
147 */
c5ddb310 148
edc33b5f 149cbs[0].proc = (int(*)(void)) &mysasl_config;
c5ddb310
PH
150cbs[0].context = ob->server_mech;
151
c0fb53b7 152if ((rc = sasl_server_init(cbs, "exim")) != SASL_OK )
0756eb3c
PH
153 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
154 "couldn't initialise Cyrus SASL library.", ablock->name);
155
c0fb53b7
JH
156if ((rc = sasl_server_new(CS ob->server_service, CS expanded_hostname,
157 realm_expanded, NULL, NULL, NULL, 0, &conn)) != SASL_OK )
0756eb3c
PH
158 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
159 "couldn't initialise Cyrus SASL server connection.", ablock->name);
160
c0fb53b7 161if ((rc = sasl_listmech(conn, NULL, "", ":", "", (const char **)&list, &len, &i)) != SASL_OK )
0756eb3c
PH
162 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
163 "couldn't get Cyrus SASL mechanism list.", ablock->name);
164
c0fb53b7
JH
165i = ':';
166listptr = list;
0756eb3c 167
c0fb53b7
JH
168HDEBUG(D_auth)
169 {
edc33b5f 170 debug_printf("Initialised Cyrus SASL service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
4c287009 171 ob->server_service, expanded_hostname, realm_expanded);
edc33b5f 172 debug_printf("Cyrus SASL knows mechanisms: %s\n", list);
c0fb53b7 173 }
0756eb3c
PH
174
175/* the store_get / store_reset mechanism is hierarchical
176 * the hierarchy is stored for us behind our back. This point
177 * creates a hierarchy point for this function.
178 */
f3ebb786 179rs_point = store_mark();
0756eb3c
PH
180
181/* loop until either we get to the end of the list, or we match the
182 * public name of this authenticator
183 */
c0fb53b7 184while ( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
0756eb3c
PH
185 strcmpic(buffer,ob->server_mech) );
186
c0fb53b7 187if (!buffer)
0756eb3c
PH
188 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
189 "Cyrus SASL doesn't know about mechanism %s.", ablock->name, ob->server_mech);
190
191store_reset(rs_point);
192
193HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
194
195/* make sure that if we get here then we're allowed to advertise. */
196ablock->server = TRUE;
197
198sasl_dispose(&conn);
199sasl_done();
200}
201
202/*************************************************
203* Server entry point *
204*************************************************/
205
206/* For interface, see auths/README */
207
208/* note, we don't care too much about memory allocation in this, because this is entirely
209 * within a shortlived child
210 */
211
212int
213auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
214{
215auth_cyrus_sasl_options_block *ob =
216 (auth_cyrus_sasl_options_block *)(ablock->options_block);
217uschar *output, *out2, *input, *clear, *hname;
218uschar *debug = NULL; /* Stops compiler complaining */
c0fb53b7 219sasl_callback_t cbs[] = {{SASL_CB_LIST_END, NULL, NULL}};
0756eb3c 220sasl_conn_t *conn;
c0fb53b7 221char * realm_expanded = NULL;
d7978c0f 222int rc, firsttime = 1, clen, *negotiated_ssf_ptr = NULL, negotiated_ssf;
0756eb3c
PH
223unsigned int inlen, outlen;
224
c0fb53b7
JH
225input = data;
226inlen = Ustrlen(data);
0756eb3c 227
c0fb53b7 228HDEBUG(D_auth) debug = string_copy(data);
0756eb3c 229
c0fb53b7 230hname = expand_string(ob->server_hostname);
4c287009 231if (hname && ob->server_realm)
c0fb53b7
JH
232 realm_expanded = CS expand_string(ob->server_realm);
233if (!hname || !realm_expanded && ob->server_realm)
0756eb3c
PH
234 {
235 auth_defer_msg = expand_string_message;
236 return DEFER;
237 }
238
c0fb53b7 239if (inlen)
0756eb3c 240 {
c0fb53b7 241 if ((clen = b64decode(input, &clear)) < 0)
0756eb3c 242 return BAD64;
c0fb53b7
JH
243 input = clear;
244 inlen = clen;
0756eb3c
PH
245 }
246
c0fb53b7 247if ((rc = sasl_server_init(cbs, "exim")) != SASL_OK)
0756eb3c
PH
248 {
249 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
250 return DEFER;
251 }
252
c0fb53b7 253rc = sasl_server_new(CS ob->server_service, CS hname, realm_expanded, NULL,
acb1b346
PH
254 NULL, NULL, 0, &conn);
255
edc33b5f
PP
256HDEBUG(D_auth)
257 debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
4c287009 258 ob->server_service, hname, realm_expanded);
edc33b5f 259
c0fb53b7 260if (rc != SASL_OK )
0756eb3c
PH
261 {
262 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
263 sasl_done();
264 return DEFER;
265 }
266
817d9f57 267if (tls_in.cipher)
edc33b5f 268 {
c0fb53b7 269 if ((rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_in.bits)) != SASL_OK)
edc33b5f
PP
270 {
271 HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n",
817d9f57 272 tls_in.bits, sasl_errstring(rc, NULL, NULL));
edc33b5f
PP
273 auth_defer_msg = US"couldn't set Cyrus SASL EXTERNAL SSF";
274 sasl_done();
275 return DEFER;
276 }
277 else
817d9f57 278 HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_in.bits);
edc33b5f
PP
279 }
280else
281 HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");
282
66645890
PP
283/* So sasl_setprop() documents non-shorted IPv6 addresses which is incredibly
284annoying; looking at cyrus-imapd-2.3.x source, the IP address is constructed
285with their iptostring() function, which just wraps
286getnameinfo(..., NI_NUMERICHOST|NI_NUMERICSERV), which is equivalent to the
287inet_ntop which we wrap in our host_ntoa() function.
288
289So the docs are too strict and we shouldn't worry about :: contractions. */
290
291/* Set properties for remote and local host-ip;port */
d7978c0f 292for (int i = 0; i < 2; ++i)
66645890
PP
293 {
294 struct sockaddr_storage ss;
295 int (*query)(int, struct sockaddr *, socklen_t *);
296 int propnum, port;
297 const uschar *label;
298 uschar *address, *address_port;
299 const char *s_err;
300 socklen_t sslen;
301
302 if (i)
303 {
304 query = &getpeername;
305 propnum = SASL_IPREMOTEPORT;
306 label = CUS"peer";
307 }
308 else
309 {
310 query = &getsockname;
311 propnum = SASL_IPLOCALPORT;
312 label = CUS"local";
313 }
314
315 sslen = sizeof(ss);
c0fb53b7 316 if ((rc = query(fileno(smtp_in), (struct sockaddr *) &ss, &sslen)) < 0)
66645890
PP
317 {
318 HDEBUG(D_auth)
319 debug_printf("Failed to get %s address information: %s\n",
320 label, strerror(errno));
321 break;
322 }
323
324 address = host_ntoa(-1, &ss, NULL, &port);
325 address_port = string_sprintf("%s;%d", address, port);
326
c0fb53b7 327 if ((rc = sasl_setprop(conn, propnum, address_port)) != SASL_OK)
66645890
PP
328 {
329 s_err = sasl_errdetail(conn);
330 HDEBUG(D_auth)
331 debug_printf("Failed to set %s SASL property: [%d] %s\n",
332 label, rc, s_err ? s_err : "<unknown reason>");
333 break;
334 }
335 HDEBUG(D_auth) debug_printf("Cyrus SASL set %s hostport to: %s\n",
336 label, address_port);
337 }
338
c0fb53b7 339for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; )
0756eb3c 340 {
c0fb53b7 341 if (firsttime)
0756eb3c 342 {
c0fb53b7 343 firsttime = 0;
0756eb3c 344 HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
c0fb53b7 345 rc = sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
0756eb3c
PH
346 (const char **)(&output), &outlen);
347 }
348 else
349 {
350 /* make sure that we have a null-terminated string */
c0fb53b7
JH
351 out2 = string_copyn(output, outlen);
352
353 if ((rc = auth_get_data(&input, out2, outlen)) != OK)
0756eb3c
PH
354 {
355 /* we couldn't get the data, so free up the library before
356 * returning whatever error we get */
357 sasl_dispose(&conn);
358 sasl_done();
359 return rc;
360 }
c0fb53b7 361 inlen = Ustrlen(input);
0756eb3c 362
c0fb53b7
JH
363 HDEBUG(D_auth) debug = string_copy(input);
364 if (inlen)
0756eb3c 365 {
c0fb53b7 366 if ((clen = b64decode(input, &clear)) < 0)
0756eb3c 367 {
c0fb53b7
JH
368 sasl_dispose(&conn);
369 sasl_done();
0756eb3c
PH
370 return BAD64;
371 }
c0fb53b7
JH
372 input = clear;
373 inlen = clen;
0756eb3c
PH
374 }
375
376 HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
c0fb53b7 377 rc = sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
0756eb3c 378 }
c0fb53b7
JH
379
380 if (rc == SASL_BADPROT)
0756eb3c
PH
381 {
382 sasl_dispose(&conn);
383 sasl_done();
384 return UNEXPECTED;
385 }
c0fb53b7
JH
386 if (rc == SASL_CONTINUE)
387 continue;
388
389 /* Get the username and copy it into $auth1 and $1. The former is now the
390 preferred variable; the latter is the original variable. */
391
392 if ((sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2))) != SASL_OK)
0756eb3c 393 {
0756eb3c 394 HDEBUG(D_auth)
c0fb53b7
JH
395 debug_printf("Cyrus SASL library will not tell us the username: %s\n",
396 sasl_errstring(rc, NULL, NULL));
0756eb3c 397 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
c0fb53b7 398 "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech,
0756eb3c
PH
399 sasl_errstring(rc, NULL, NULL));
400 sasl_dispose(&conn);
401 sasl_done();
402 return FAIL;
403 }
c0fb53b7
JH
404 auth_vars[0] = expand_nstring[1] = string_copy(out2);
405 expand_nlength[1] = Ustrlen(out2);
406 expand_nmax = 1;
407
408 switch (rc)
0756eb3c 409 {
c0fb53b7
JH
410 case SASL_FAIL: case SASL_BUFOVER: case SASL_BADMAC: case SASL_BADAUTH:
411 case SASL_NOAUTHZ: case SASL_ENCRYPT: case SASL_EXPIRED:
412 case SASL_DISABLED: case SASL_NOUSER:
413 /* these are considered permanent failure codes */
edc33b5f 414 HDEBUG(D_auth)
c0fb53b7 415 debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
edc33b5f 416 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
c0fb53b7
JH
417 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
418 sasl_errstring(rc, NULL, NULL));
edc33b5f
PP
419 sasl_dispose(&conn);
420 sasl_done();
421 return FAIL;
0756eb3c 422
c0fb53b7
JH
423 case SASL_NOMECH:
424 /* this is a temporary failure, because the mechanism is not
425 * available for this user. If it wasn't available at all, we
426 * shouldn't have got here in the first place...
427 */
edc33b5f 428 HDEBUG(D_auth)
c0fb53b7
JH
429 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
430 auth_defer_msg =
431 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
edc33b5f
PP
432 sasl_dispose(&conn);
433 sasl_done();
c0fb53b7
JH
434 return DEFER;
435
436 case SASL_OK:
edc33b5f 437 HDEBUG(D_auth)
c0fb53b7
JH
438 debug_printf("Cyrus SASL %s authentication succeeded for %s\n",
439 ob->server_mech, auth_vars[0]);
440
441 if ((rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf_ptr)))!= SASL_OK)
442 {
443 HDEBUG(D_auth)
444 debug_printf("Cyrus SASL library will not tell us the SSF: %s\n",
445 sasl_errstring(rc, NULL, NULL));
446 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
447 "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech,
448 sasl_errstring(rc, NULL, NULL));
449 sasl_dispose(&conn);
450 sasl_done();
451 return FAIL;
452 }
453 negotiated_ssf = *negotiated_ssf_ptr;
454 HDEBUG(D_auth)
455 debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf);
456 if (negotiated_ssf > 0)
457 {
458 HDEBUG(D_auth)
459 debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf);
460 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
461 "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf);
462 sasl_dispose(&conn);
463 sasl_done();
464 return FAIL;
465 }
466
467 /* close down the connection, freeing up library's memory */
edc33b5f
PP
468 sasl_dispose(&conn);
469 sasl_done();
edc33b5f 470
c0fb53b7
JH
471 /* Expand server_condition as an authorization check */
472 return auth_check_serv_cond(ablock);
16ff981e 473
c0fb53b7
JH
474 default:
475 /* Anything else is a temporary failure, and we'll let SASL print out
476 * the error string for us
477 */
478 HDEBUG(D_auth)
479 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
480 auth_defer_msg =
481 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
482 sasl_dispose(&conn);
483 sasl_done();
484 return DEFER;
0756eb3c
PH
485 }
486 }
487/* NOTREACHED */
488return 0; /* Stop compiler complaints */
489}
490
6545de78
PP
491/*************************************************
492* Diagnostic API *
493*************************************************/
494
495void
496auth_cyrus_sasl_version_report(FILE *f)
497{
c0fb53b7
JH
498const char *implementation, *version;
499sasl_version_info(&implementation, &version, NULL, NULL, NULL, NULL);
500fprintf(f, "Library version: Cyrus SASL: Compile: %d.%d.%d\n"
501 " Runtime: %s [%s]\n",
502 SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
503 version, implementation);
6545de78
PP
504}
505
0756eb3c
PH
506/*************************************************
507* Client entry point *
508*************************************************/
509
510/* For interface, see auths/README */
511
512int
513auth_cyrus_sasl_client(
514 auth_instance *ablock, /* authenticator block */
251b9eb4 515 void * sx, /* connexction */
0756eb3c 516 int timeout, /* command timeout */
d7978c0f 517 uschar *buffer, /* for reading response */
0756eb3c
PH
518 int buffsize) /* size of buffer */
519{
520/* We don't support clients (yet) in this implementation of cyrus_sasl */
521return FAIL;
522}
523
d185889f 524#endif /*!MACRO_PREDEF*/
0756eb3c
PH
525#endif /* AUTH_CYRUS_SASL */
526
527/* End of cyrus_sasl.c */