Track tainted data and refuse to expand it
[exim.git] / src / src / auths / cyrus_sasl.c
1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 /* This code was originally contributed by Matthew Byng-Maddick */
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
19 functions if they are not wanted, because we need to have the Cyrus SASL header
20 available for compiling. Therefore, compile these functions only if
21 AUTH_CYRUS_SASL is defined. However, some compilers don't like compiling empty
22 modules, so keep them happy with a dummy when skipping the rest. Make it
23 reference itself to stop picky compilers complaining that it is unused, and put
24 in a dummy argument to stop even pickier compilers complaining about infinite
25 loops. */
26
27 #ifndef AUTH_CYRUS_SASL
28 static void dummy(int x);
29 static void dummy2(int x) { dummy(x-1); }
30 static void dummy(int x) { dummy2(x-1); }
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
39 optionlist 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
51 address can appear in the tables drtables.c. */
52
53 int auth_cyrus_sasl_options_count =
54 sizeof(auth_cyrus_sasl_options)/sizeof(optionlist);
55
56 /* Default private options block for the cyrus_sasl authentication method. */
57
58 auth_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
66 #ifdef MACRO_PREDEF
67
68 /* Dummy values */
69 void auth_cyrus_sasl_init(auth_instance *ablock) {}
70 int auth_cyrus_sasl_server(auth_instance *ablock, uschar *data) {return 0;}
71 int auth_cyrus_sasl_client(auth_instance *ablock, void * sx,
72 int timeout, uschar *buffer, int buffsize) {return 0;}
73 void auth_cyrus_sasl_version_report(FILE *f) {}
74
75 #else /*!MACRO_PREDEF*/
76
77
78
79
80 /*************************************************
81 * Initialization entry point *
82 *************************************************/
83
84 /* Called for each instance, after its options have been read, to
85 enable consistency checks to be done, or anything else that needs
86 to be set up. */
87
88
89 /* Auxiliary function, passed in data to sasl_server_init(). */
90
91 static int
92 mysasl_config(void *context,
93 const char *plugin_name,
94 const char *option,
95 const char **result,
96 unsigned int *len)
97 {
98 if (context && !strcmp(option, "mech_list"))
99 {
100 *result = context;
101 if (len != NULL) *len = strlen(*result);
102 return SASL_OK;
103 }
104 return SASL_FAIL;
105 }
106
107 /* Here's the real function */
108
109 void
110 auth_cyrus_sasl_init(auth_instance *ablock)
111 {
112 auth_cyrus_sasl_options_block *ob =
113 (auth_cyrus_sasl_options_block *)(ablock->options_block);
114 const uschar *list, *listptr, *buffer;
115 int rc, i;
116 unsigned int len;
117 rmark rs_point;
118 uschar *expanded_hostname;
119 char *realm_expanded;
120
121 sasl_conn_t *conn;
122 sasl_callback_t cbs[] = {
123 {SASL_CB_GETOPT, NULL, NULL },
124 {SASL_CB_LIST_END, NULL, NULL}};
125
126 /* default the mechanism to our "public name" */
127 if (ob->server_mech == NULL)
128 ob->server_mech = string_copy(ablock->public_name);
129
130 expanded_hostname = expand_string(ob->server_hostname);
131 if (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
136 realm_expanded = NULL;
137 if (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
145 /* we're going to initialise the library to check that there is an
146 * authenticator of type whatever mechanism we're using
147 */
148
149 cbs[0].proc = (int(*)(void)) &mysasl_config;
150 cbs[0].context = ob->server_mech;
151
152 if ((rc = sasl_server_init(cbs, "exim")) != SASL_OK )
153 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
154 "couldn't initialise Cyrus SASL library.", ablock->name);
155
156 if ((rc = sasl_server_new(CS ob->server_service, CS expanded_hostname,
157 realm_expanded, NULL, NULL, NULL, 0, &conn)) != SASL_OK )
158 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
159 "couldn't initialise Cyrus SASL server connection.", ablock->name);
160
161 if ((rc = sasl_listmech(conn, NULL, "", ":", "", (const char **)&list, &len, &i)) != SASL_OK )
162 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
163 "couldn't get Cyrus SASL mechanism list.", ablock->name);
164
165 i = ':';
166 listptr = list;
167
168 HDEBUG(D_auth)
169 {
170 debug_printf("Initialised Cyrus SASL service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
171 ob->server_service, expanded_hostname, realm_expanded);
172 debug_printf("Cyrus SASL knows mechanisms: %s\n", list);
173 }
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 */
179 rs_point = store_mark();
180
181 /* loop until either we get to the end of the list, or we match the
182 * public name of this authenticator
183 */
184 while ( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
185 strcmpic(buffer,ob->server_mech) );
186
187 if (!buffer)
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
191 store_reset(rs_point);
192
193 HDEBUG(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. */
196 ablock->server = TRUE;
197
198 sasl_dispose(&conn);
199 sasl_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
212 int
213 auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
214 {
215 auth_cyrus_sasl_options_block *ob =
216 (auth_cyrus_sasl_options_block *)(ablock->options_block);
217 uschar *output, *out2, *input, *clear, *hname;
218 uschar *debug = NULL; /* Stops compiler complaining */
219 sasl_callback_t cbs[] = {{SASL_CB_LIST_END, NULL, NULL}};
220 sasl_conn_t *conn;
221 char * realm_expanded = NULL;
222 int rc, firsttime = 1, clen, *negotiated_ssf_ptr = NULL, negotiated_ssf;
223 unsigned int inlen, outlen;
224
225 input = data;
226 inlen = Ustrlen(data);
227
228 HDEBUG(D_auth) debug = string_copy(data);
229
230 hname = expand_string(ob->server_hostname);
231 if (hname && ob->server_realm)
232 realm_expanded = CS expand_string(ob->server_realm);
233 if (!hname || !realm_expanded && ob->server_realm)
234 {
235 auth_defer_msg = expand_string_message;
236 return DEFER;
237 }
238
239 if (inlen)
240 {
241 if ((clen = b64decode(input, &clear)) < 0)
242 return BAD64;
243 input = clear;
244 inlen = clen;
245 }
246
247 if ((rc = sasl_server_init(cbs, "exim")) != SASL_OK)
248 {
249 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
250 return DEFER;
251 }
252
253 rc = sasl_server_new(CS ob->server_service, CS hname, realm_expanded, NULL,
254 NULL, NULL, 0, &conn);
255
256 HDEBUG(D_auth)
257 debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
258 ob->server_service, hname, realm_expanded);
259
260 if (rc != SASL_OK )
261 {
262 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
263 sasl_done();
264 return DEFER;
265 }
266
267 if (tls_in.cipher)
268 {
269 if ((rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_in.bits)) != SASL_OK)
270 {
271 HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n",
272 tls_in.bits, sasl_errstring(rc, NULL, NULL));
273 auth_defer_msg = US"couldn't set Cyrus SASL EXTERNAL SSF";
274 sasl_done();
275 return DEFER;
276 }
277 else
278 HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_in.bits);
279 }
280 else
281 HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");
282
283 /* So sasl_setprop() documents non-shorted IPv6 addresses which is incredibly
284 annoying; looking at cyrus-imapd-2.3.x source, the IP address is constructed
285 with their iptostring() function, which just wraps
286 getnameinfo(..., NI_NUMERICHOST|NI_NUMERICSERV), which is equivalent to the
287 inet_ntop which we wrap in our host_ntoa() function.
288
289 So the docs are too strict and we shouldn't worry about :: contractions. */
290
291 /* Set properties for remote and local host-ip;port */
292 for (int i = 0; i < 2; ++i)
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);
316 if ((rc = query(fileno(smtp_in), (struct sockaddr *) &ss, &sslen)) < 0)
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
327 if ((rc = sasl_setprop(conn, propnum, address_port)) != SASL_OK)
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
339 for (rc = SASL_CONTINUE; rc == SASL_CONTINUE; )
340 {
341 if (firsttime)
342 {
343 firsttime = 0;
344 HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
345 rc = sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
346 (const char **)(&output), &outlen);
347 }
348 else
349 {
350 /* make sure that we have a null-terminated string */
351 out2 = string_copyn(output, outlen);
352
353 if ((rc = auth_get_data(&input, out2, outlen)) != OK)
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 }
361 inlen = Ustrlen(input);
362
363 HDEBUG(D_auth) debug = string_copy(input);
364 if (inlen)
365 {
366 if ((clen = b64decode(input, &clear)) < 0)
367 {
368 sasl_dispose(&conn);
369 sasl_done();
370 return BAD64;
371 }
372 input = clear;
373 inlen = clen;
374 }
375
376 HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
377 rc = sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
378 }
379
380 if (rc == SASL_BADPROT)
381 {
382 sasl_dispose(&conn);
383 sasl_done();
384 return UNEXPECTED;
385 }
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)
393 {
394 HDEBUG(D_auth)
395 debug_printf("Cyrus SASL library will not tell us the username: %s\n",
396 sasl_errstring(rc, NULL, NULL));
397 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
398 "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech,
399 sasl_errstring(rc, NULL, NULL));
400 sasl_dispose(&conn);
401 sasl_done();
402 return FAIL;
403 }
404 auth_vars[0] = expand_nstring[1] = string_copy(out2);
405 expand_nlength[1] = Ustrlen(out2);
406 expand_nmax = 1;
407
408 switch (rc)
409 {
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 */
414 HDEBUG(D_auth)
415 debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
416 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
417 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
418 sasl_errstring(rc, NULL, NULL));
419 sasl_dispose(&conn);
420 sasl_done();
421 return FAIL;
422
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 */
428 HDEBUG(D_auth)
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);
432 sasl_dispose(&conn);
433 sasl_done();
434 return DEFER;
435
436 case SASL_OK:
437 HDEBUG(D_auth)
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 */
468 sasl_dispose(&conn);
469 sasl_done();
470
471 /* Expand server_condition as an authorization check */
472 return auth_check_serv_cond(ablock);
473
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;
485 }
486 }
487 /* NOTREACHED */
488 return 0; /* Stop compiler complaints */
489 }
490
491 /*************************************************
492 * Diagnostic API *
493 *************************************************/
494
495 void
496 auth_cyrus_sasl_version_report(FILE *f)
497 {
498 const char *implementation, *version;
499 sasl_version_info(&implementation, &version, NULL, NULL, NULL, NULL);
500 fprintf(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);
504 }
505
506 /*************************************************
507 * Client entry point *
508 *************************************************/
509
510 /* For interface, see auths/README */
511
512 int
513 auth_cyrus_sasl_client(
514 auth_instance *ablock, /* authenticator block */
515 void * sx, /* connexction */
516 int timeout, /* command timeout */
517 uschar *buffer, /* for reading response */
518 int buffsize) /* size of buffer */
519 {
520 /* We don't support clients (yet) in this implementation of cyrus_sasl */
521 return FAIL;
522 }
523
524 #endif /*!MACRO_PREDEF*/
525 #endif /* AUTH_CYRUS_SASL */
526
527 /* End of cyrus_sasl.c */