1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2012 */
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;
102 char *realm_expanded;
105 sasl_callback_t cbs[]={
106 {SASL_CB_GETOPT, NULL, NULL },
107 {SASL_CB_LIST_END, NULL, NULL}};
109 /* default the mechanism to our "public name" */
110 if(ob->server_mech == NULL)
111 ob->server_mech=string_copy(ablock->public_name);
113 expanded_hostname = expand_string(ob->server_hostname);
114 if (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);
120 if (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);
128 /* we're going to initialise the library to check that there is an
129 * authenticator of type whatever mechanism we're using
132 cbs[0].proc = (int(*)(void)) &mysasl_config;
133 cbs[0].context = ob->server_mech;
135 rc=sasl_server_init(cbs, "exim");
138 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
139 "couldn't initialise Cyrus SASL library.", ablock->name);
141 rc=sasl_server_new(CS ob->server_service, CS expanded_hostname,
142 realm_expanded, NULL, NULL, NULL, 0, &conn);
144 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
145 "couldn't initialise Cyrus SASL server connection.", ablock->name);
147 rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
149 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
150 "couldn't get Cyrus SASL mechanism list.", ablock->name);
156 debug_printf("Initialised Cyrus SASL service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
157 ob->server_service, expanded_hostname, realm_expanded);
158 debug_printf("Cyrus SASL knows mechanisms: %s\n", list);
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.
165 rs_point=store_get(0);
167 /* loop until either we get to the end of the list, or we match the
168 * public name of this authenticator
170 while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
171 strcmpic(buffer,ob->server_mech) );
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);
177 store_reset(rs_point);
179 HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
181 /* make sure that if we get here then we're allowed to advertise. */
182 ablock->server = TRUE;
188 /*************************************************
189 * Server entry point *
190 *************************************************/
192 /* For interface, see auths/README */
194 /* note, we don't care too much about memory allocation in this, because this is entirely
195 * within a shortlived child
199 auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
201 auth_cyrus_sasl_options_block *ob =
202 (auth_cyrus_sasl_options_block *)(ablock->options_block);
203 uschar *output, *out2, *input, *clear, *hname;
204 uschar *debug = NULL; /* Stops compiler complaining */
205 sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
207 char *realm_expanded;
208 int rc, firsttime=1, clen, negotiated_ssf;
209 unsigned int inlen, outlen;
214 HDEBUG(D_auth) debug=string_copy(data);
216 hname=expand_string(ob->server_hostname);
218 if (hname && ob->server_realm)
219 realm_expanded= CS expand_string(ob->server_realm);
220 if((hname == NULL) ||
221 ((realm_expanded == NULL) && (ob->server_realm != NULL)))
223 auth_defer_msg = expand_string_message;
229 clen=auth_b64decode(input, &clear);
238 rc=sasl_server_init(cbs, "exim");
241 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
245 rc=sasl_server_new(CS ob->server_service, CS hname, realm_expanded, NULL,
246 NULL, NULL, 0, &conn);
249 debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
250 ob->server_service, hname, realm_expanded);
254 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
261 rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, &tls_bits);
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";
271 HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_bits);
274 HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");
278 while(rc==SASL_CONTINUE)
283 HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
284 rc=sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
285 (const char **)(&output), &outlen);
289 /* make sure that we have a null-terminated string */
290 out2=store_get(outlen+1);
291 memcpy(out2,output,outlen);
293 if((rc=auth_get_data(&input, out2, outlen))!=OK)
295 /* we couldn't get the data, so free up the library before
296 * returning whatever error we get */
301 inlen=Ustrlen(input);
303 HDEBUG(D_auth) debug=string_copy(input);
306 clen=auth_b64decode(input, &clear);
317 HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
318 rc=sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
326 else if( rc==SASL_FAIL || rc==SASL_BUFOVER
327 || rc==SASL_BADMAC || rc==SASL_BADAUTH
328 || rc==SASL_NOAUTHZ || rc==SASL_ENCRYPT
329 || rc==SASL_EXPIRED || rc==SASL_DISABLED
332 /* these are considered permanent failure codes */
334 debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
335 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
336 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
337 sasl_errstring(rc, NULL, NULL));
342 else if(rc==SASL_NOMECH)
344 /* this is a temporary failure, because the mechanism is not
345 * available for this user. If it wasn't available at all, we
346 * shouldn't have got here in the first place...
349 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
351 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
356 else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
358 /* Anything else is a temporary failure, and we'll let SASL print out
359 * the error string for us
362 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
364 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
371 /* Get the username and copy it into $auth1 and $1. The former is now the
372 preferred variable; the latter is the original variable. */
373 rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
377 debug_printf("Cyrus SASL library will not tell us the username: %s\n",
378 sasl_errstring(rc, NULL, NULL));
379 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
380 "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech,
381 sasl_errstring(rc, NULL, NULL));
387 auth_vars[0] = expand_nstring[1] = string_copy(out2);
388 expand_nlength[1] = Ustrlen(expand_nstring[1]);
392 debug_printf("Cyrus SASL %s authentication succeeded for %s\n",
393 ob->server_mech, auth_vars[0]);
395 rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf));
399 debug_printf("Cyrus SASL library will not tell us the SSF: %s\n",
400 sasl_errstring(rc, NULL, NULL));
401 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
402 "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech,
403 sasl_errstring(rc, NULL, NULL));
409 debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf);
410 if (negotiated_ssf > 0)
413 debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf);
414 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
415 "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf);
421 /* close down the connection, freeing up library's memory */
425 /* Expand server_condition as an authorization check */
426 return auth_check_serv_cond(ablock);
430 return 0; /* Stop compiler complaints */
433 /*************************************************
435 *************************************************/
438 auth_cyrus_sasl_version_report(FILE *f)
440 const char *implementation, *version;
441 sasl_version_info(&implementation, &version, NULL, NULL, NULL, NULL);
442 fprintf(f, "Library version: Cyrus SASL: Compile: %d.%d.%d\n"
443 " Runtime: %s [%s]\n",
444 SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
445 version, implementation);
448 /*************************************************
449 * Client entry point *
450 *************************************************/
452 /* For interface, see auths/README */
455 auth_cyrus_sasl_client(
456 auth_instance *ablock, /* authenticator block */
457 smtp_inblock *inblock, /* input connection */
458 smtp_outblock *outblock, /* output connection */
459 int timeout, /* command timeout */
460 uschar *buffer, /* for reading response */
461 int buffsize) /* size of buffer */
463 /* We don't support clients (yet) in this implementation of cyrus_sasl */
467 #endif /* AUTH_CYRUS_SASL */
469 /* End of cyrus_sasl.c */