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, i, firsttime=1, clen, *negotiated_ssf_ptr=NULL, 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, (sasl_ssf_t *) &tls_in.bits);
264 HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n",
265 tls_in.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_in.bits);
274 HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");
276 /* So sasl_setprop() documents non-shorted IPv6 addresses which is incredibly
277 annoying; looking at cyrus-imapd-2.3.x source, the IP address is constructed
278 with their iptostring() function, which just wraps
279 getnameinfo(..., NI_NUMERICHOST|NI_NUMERICSERV), which is equivalent to the
280 inet_ntop which we wrap in our host_ntoa() function.
282 So the docs are too strict and we shouldn't worry about :: contractions. */
284 /* Set properties for remote and local host-ip;port */
285 for (i=0; i < 2; ++i)
287 struct sockaddr_storage ss;
288 int (*query)(int, struct sockaddr *, socklen_t *);
291 uschar *address, *address_port;
297 query = &getpeername;
298 propnum = SASL_IPREMOTEPORT;
303 query = &getsockname;
304 propnum = SASL_IPLOCALPORT;
309 rc = query(fileno(smtp_in), (struct sockaddr *) &ss, &sslen);
313 debug_printf("Failed to get %s address information: %s\n",
314 label, strerror(errno));
318 address = host_ntoa(-1, &ss, NULL, &port);
319 address_port = string_sprintf("%s;%d", address, port);
321 rc = sasl_setprop(conn, propnum, address_port);
324 s_err = sasl_errdetail(conn);
326 debug_printf("Failed to set %s SASL property: [%d] %s\n",
327 label, rc, s_err ? s_err : "<unknown reason>");
330 HDEBUG(D_auth) debug_printf("Cyrus SASL set %s hostport to: %s\n",
331 label, address_port);
336 while(rc==SASL_CONTINUE)
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);
347 /* make sure that we have a null-terminated string */
348 out2=store_get(outlen+1);
349 memcpy(out2,output,outlen);
351 if((rc=auth_get_data(&input, out2, outlen))!=OK)
353 /* we couldn't get the data, so free up the library before
354 * returning whatever error we get */
359 inlen=Ustrlen(input);
361 HDEBUG(D_auth) debug=string_copy(input);
364 clen=auth_b64decode(input, &clear);
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);
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
390 /* these are considered permanent failure codes */
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));
400 else if(rc==SASL_NOMECH)
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...
407 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
409 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
414 else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
416 /* Anything else is a temporary failure, and we'll let SASL print out
417 * the error string for us
420 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
422 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
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));
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));
445 auth_vars[0] = expand_nstring[1] = string_copy(out2);
446 expand_nlength[1] = Ustrlen(expand_nstring[1]);
450 debug_printf("Cyrus SASL %s authentication succeeded for %s\n",
451 ob->server_mech, auth_vars[0]);
453 rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf_ptr));
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));
466 negotiated_ssf = *negotiated_ssf_ptr;
468 debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf);
469 if (negotiated_ssf > 0)
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);
480 /* close down the connection, freeing up library's memory */
484 /* Expand server_condition as an authorization check */
485 return auth_check_serv_cond(ablock);
489 return 0; /* Stop compiler complaints */
492 /*************************************************
494 *************************************************/
497 auth_cyrus_sasl_version_report(FILE *f)
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);
507 /*************************************************
508 * Client entry point *
509 *************************************************/
511 /* For interface, see auths/README */
514 auth_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 */
522 /* We don't support clients (yet) in this implementation of cyrus_sasl */
526 #endif /* AUTH_CYRUS_SASL */
528 /* End of cyrus_sasl.c */