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