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