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;
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 /* we're going to initialise the library to check that there is an
113 * authenticator of type whatever mechanism we're using
116 cbs[0].proc = &mysasl_config;
117 cbs[0].context = ob->server_mech;
119 rc=sasl_server_init(cbs, "exim");
122 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
123 "couldn't initialise Cyrus SASL library.", ablock->name);
125 rc=sasl_server_new(CS ob->server_service, CS primary_hostname,
126 CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
128 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
129 "couldn't initialise Cyrus SASL server connection.", ablock->name);
131 rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
133 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
134 "couldn't get Cyrus SASL mechanism list.", ablock->name);
139 HDEBUG(D_auth) debug_printf("Cyrus SASL knows about: %s\n", list);
141 /* the store_get / store_reset mechanism is hierarchical
142 * the hierarchy is stored for us behind our back. This point
143 * creates a hierarchy point for this function.
145 rs_point=store_get(0);
147 /* loop until either we get to the end of the list, or we match the
148 * public name of this authenticator
150 while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
151 strcmpic(buffer,ob->server_mech) );
154 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
155 "Cyrus SASL doesn't know about mechanism %s.", ablock->name, ob->server_mech);
157 store_reset(rs_point);
159 HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
161 /* make sure that if we get here then we're allowed to advertise. */
162 ablock->server = TRUE;
168 /*************************************************
169 * Server entry point *
170 *************************************************/
172 /* For interface, see auths/README */
174 /* note, we don't care too much about memory allocation in this, because this is entirely
175 * within a shortlived child
179 auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
181 auth_cyrus_sasl_options_block *ob =
182 (auth_cyrus_sasl_options_block *)(ablock->options_block);
183 uschar *output, *out2, *input, *clear, *hname;
184 uschar *debug = NULL; /* Stops compiler complaining */
185 sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
187 int rc, firsttime=1, clen;
188 unsigned int inlen, outlen;
193 HDEBUG(D_auth) debug=string_copy(data);
195 hname=expand_string(ob->server_hostname);
198 auth_defer_msg = expand_string_message;
204 clen=auth_b64decode(input, &clear);
213 rc=sasl_server_init(cbs, "exim");
216 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
220 rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL,
221 NULL, NULL, 0, &conn);
225 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
232 while(rc==SASL_CONTINUE)
237 HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
238 rc=sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
239 (const char **)(&output), &outlen);
243 /* make sure that we have a null-terminated string */
244 out2=store_get(outlen+1);
245 memcpy(out2,output,outlen);
247 if((rc=auth_get_data(&input, out2, outlen))!=OK)
249 /* we couldn't get the data, so free up the library before
250 * returning whatever error we get */
255 inlen=Ustrlen(input);
257 HDEBUG(D_auth) debug=string_copy(input);
260 clen=auth_b64decode(input, &clear);
271 HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
272 rc=sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
280 else if( rc==SASL_FAIL || rc==SASL_BUFOVER
281 || rc==SASL_BADMAC || rc==SASL_BADAUTH
282 || rc==SASL_NOAUTHZ || rc==SASL_ENCRYPT
283 || rc==SASL_EXPIRED || rc==SASL_DISABLED
286 /* these are considered permanent failure codes */
288 debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
289 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
290 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
291 sasl_errstring(rc, NULL, NULL));
296 else if(rc==SASL_NOMECH)
298 /* this is a temporary failure, because the mechanism is not
299 * available for this user. If it wasn't available at all, we
300 * shouldn't have got here in the first place...
303 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
305 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
310 else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
312 /* Anything else is a temporary failure, and we'll let SASL print out
313 * the error string for us
316 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
318 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
325 /* Get the username and copy it into $auth1 and $1. The former is now the
326 preferred variable; the latter is the original variable. */
327 rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
328 auth_vars[0] = expand_nstring[1] = string_copy(out2);
329 expand_nlength[1] = Ustrlen(expand_nstring[1]);
333 debug_printf("Cyrus SASL %s authentication succeeded for %s\n", ob->server_mech, out2);
334 /* close down the connection, freeing up library's memory */
338 /* Expand server_condition as an authorization check */
339 return auth_check_serv_cond(ablock);
343 return 0; /* Stop compiler complaints */
346 /*************************************************
348 *************************************************/
351 auth_cyrus_sasl_version_report(FILE *f)
353 const char *implementation, *version;
354 sasl_version_info(&implementation, &version, NULL, NULL, NULL, NULL);
355 fprintf(f, "Library version: Cyrus SASL: Compile: %d.%d.%d\n"
356 " Runtime: %s [%s]\n",
357 SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
358 version, implementation);
361 /*************************************************
362 * Client entry point *
363 *************************************************/
365 /* For interface, see auths/README */
368 auth_cyrus_sasl_client(
369 auth_instance *ablock, /* authenticator block */
370 smtp_inblock *inblock, /* input connection */
371 smtp_outblock *outblock, /* output connection */
372 int timeout, /* command timeout */
373 uschar *buffer, /* for reading response */
374 int buffsize) /* size of buffer */
376 /* We don't support clients (yet) in this implementation of cyrus_sasl */
380 #endif /* AUTH_CYRUS_SASL */
382 /* End of cyrus_sasl.c */