1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2009 */
6 /* See the file NOTICE for conditions of use and distribution. */
8 /* This code was originally contributed by Matthew Byng-Maddick */
10 /* Copyright (c) A L Digital 2004 */
12 /* A generic (mechanism independent) Cyrus SASL authenticator. */
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
27 #ifndef AUTH_CYRUS_SASL
28 static void dummy(int x) { dummy(x-1); }
32 #include <sasl/sasl.h>
33 #include "cyrus_sasl.h"
35 /* Options specific to the cyrus_sasl authentication mechanism. */
37 optionlist 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)) }
48 /* Size of the options list. An extern variable has to be used so that its
49 address can appear in the tables drtables.c. */
51 int auth_cyrus_sasl_options_count =
52 sizeof(auth_cyrus_sasl_options)/sizeof(optionlist);
54 /* Default private options block for the cyrus_sasl authentication method. */
56 auth_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 */
64 /*************************************************
65 * Initialization entry point *
66 *************************************************/
68 /* Called for each instance, after its options have been read, to
69 enable consistency checks to be done, or anything else that needs
73 /* Auxiliary function, passed in data to sasl_server_init(). */
76 mysasl_config(void *context,
77 const char *plugin_name,
82 if (context && !strcmp(option, "mech_list"))
85 if (len != NULL) *len = strlen(*result);
91 /* Here's the real function */
94 auth_cyrus_sasl_init(auth_instance *ablock)
96 auth_cyrus_sasl_options_block *ob =
97 (auth_cyrus_sasl_options_block *)(ablock->options_block);
98 uschar *list, *listptr, *buffer;
101 uschar *rs_point, *expanded_hostname;
104 sasl_callback_t cbs[]={
105 {SASL_CB_GETOPT, NULL, NULL },
106 {SASL_CB_LIST_END, NULL, NULL}};
108 /* default the mechanism to our "public name" */
109 if(ob->server_mech == NULL)
110 ob->server_mech=string_copy(ablock->public_name);
112 expanded_hostname = expand_string(ob->server_hostname);
113 if (expanded_hostname == NULL)
114 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
115 "couldn't expand server_hostname [%s]: %s",
116 ablock->name, ob->server_hostname, expand_string_message);
118 /* we're going to initialise the library to check that there is an
119 * authenticator of type whatever mechanism we're using
122 cbs[0].proc = (int(*)(void)) &mysasl_config;
123 cbs[0].context = ob->server_mech;
125 rc=sasl_server_init(cbs, "exim");
128 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
129 "couldn't initialise Cyrus SASL library.", ablock->name);
131 rc=sasl_server_new(CS ob->server_service, CS expanded_hostname,
132 CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
134 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
135 "couldn't initialise Cyrus SASL server connection.", ablock->name);
137 rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
139 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
140 "couldn't get Cyrus SASL mechanism list.", ablock->name);
146 debug_printf("Initialised Cyrus SASL service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
147 ob->server_service, expanded_hostname, ob->server_realm);
148 debug_printf("Cyrus SASL knows mechanisms: %s\n", list);
151 /* the store_get / store_reset mechanism is hierarchical
152 * the hierarchy is stored for us behind our back. This point
153 * creates a hierarchy point for this function.
155 rs_point=store_get(0);
157 /* loop until either we get to the end of the list, or we match the
158 * public name of this authenticator
160 while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
161 strcmpic(buffer,ob->server_mech) );
164 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
165 "Cyrus SASL doesn't know about mechanism %s.", ablock->name, ob->server_mech);
167 store_reset(rs_point);
169 HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
171 /* make sure that if we get here then we're allowed to advertise. */
172 ablock->server = TRUE;
178 /*************************************************
179 * Server entry point *
180 *************************************************/
182 /* For interface, see auths/README */
184 /* note, we don't care too much about memory allocation in this, because this is entirely
185 * within a shortlived child
189 auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
191 auth_cyrus_sasl_options_block *ob =
192 (auth_cyrus_sasl_options_block *)(ablock->options_block);
193 uschar *output, *out2, *input, *clear, *hname;
194 uschar *debug = NULL; /* Stops compiler complaining */
195 sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
197 int rc, firsttime=1, clen, negotiated_ssf;
198 unsigned int inlen, outlen;
203 HDEBUG(D_auth) debug=string_copy(data);
205 hname=expand_string(ob->server_hostname);
208 auth_defer_msg = expand_string_message;
214 clen=auth_b64decode(input, &clear);
223 rc=sasl_server_init(cbs, "exim");
226 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
230 rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL,
231 NULL, NULL, 0, &conn);
234 debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
235 ob->server_service, hname, ob->server_realm);
239 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
246 rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, &tls_bits);
249 HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n",
250 tls_bits, sasl_errstring(rc, NULL, NULL));
251 auth_defer_msg = US"couldn't set Cyrus SASL EXTERNAL SSF";
256 HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_bits);
259 HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");
263 while(rc==SASL_CONTINUE)
268 HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
269 rc=sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
270 (const char **)(&output), &outlen);
274 /* make sure that we have a null-terminated string */
275 out2=store_get(outlen+1);
276 memcpy(out2,output,outlen);
278 if((rc=auth_get_data(&input, out2, outlen))!=OK)
280 /* we couldn't get the data, so free up the library before
281 * returning whatever error we get */
286 inlen=Ustrlen(input);
288 HDEBUG(D_auth) debug=string_copy(input);
291 clen=auth_b64decode(input, &clear);
302 HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
303 rc=sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
311 else if( rc==SASL_FAIL || rc==SASL_BUFOVER
312 || rc==SASL_BADMAC || rc==SASL_BADAUTH
313 || rc==SASL_NOAUTHZ || rc==SASL_ENCRYPT
314 || rc==SASL_EXPIRED || rc==SASL_DISABLED
317 /* these are considered permanent failure codes */
319 debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
320 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
321 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
322 sasl_errstring(rc, NULL, NULL));
327 else if(rc==SASL_NOMECH)
329 /* this is a temporary failure, because the mechanism is not
330 * available for this user. If it wasn't available at all, we
331 * shouldn't have got here in the first place...
334 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
336 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
341 else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
343 /* Anything else is a temporary failure, and we'll let SASL print out
344 * the error string for us
347 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
349 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
356 /* Get the username and copy it into $auth1 and $1. The former is now the
357 preferred variable; the latter is the original variable. */
358 rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
362 debug_printf("Cyrus SASL library will not tell us the username: %s\n",
363 sasl_errstring(rc, NULL, NULL));
364 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
365 "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech,
366 sasl_errstring(rc, NULL, NULL));
372 auth_vars[0] = expand_nstring[1] = string_copy(out2);
373 expand_nlength[1] = Ustrlen(expand_nstring[1]);
377 debug_printf("Cyrus SASL %s authentication succeeded for %s\n",
378 ob->server_mech, auth_vars[0]);
380 rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf));
384 debug_printf("Cyrus SASL library will not tell us the SSF: %s\n",
385 sasl_errstring(rc, NULL, NULL));
386 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
387 "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech,
388 sasl_errstring(rc, NULL, NULL));
394 debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf);
395 if (negotiated_ssf > 0)
398 debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf);
399 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
400 "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf);
406 /* close down the connection, freeing up library's memory */
410 /* Expand server_condition as an authorization check */
411 return auth_check_serv_cond(ablock);
415 return 0; /* Stop compiler complaints */
418 /*************************************************
420 *************************************************/
423 auth_cyrus_sasl_version_report(FILE *f)
425 const char *implementation, *version;
426 sasl_version_info(&implementation, &version, NULL, NULL, NULL, NULL);
427 fprintf(f, "Library version: Cyrus SASL: Compile: %d.%d.%d\n"
428 " Runtime: %s [%s]\n",
429 SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
430 version, implementation);
433 /*************************************************
434 * Client entry point *
435 *************************************************/
437 /* For interface, see auths/README */
440 auth_cyrus_sasl_client(
441 auth_instance *ablock, /* authenticator block */
442 smtp_inblock *inblock, /* input connection */
443 smtp_outblock *outblock, /* output connection */
444 int timeout, /* command timeout */
445 uschar *buffer, /* for reading response */
446 int buffsize) /* size of buffer */
448 /* We don't support clients (yet) in this implementation of cyrus_sasl */
452 #endif /* AUTH_CYRUS_SASL */
454 /* End of cyrus_sasl.c */