Various SASL fixes.
[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 - 2009 */
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) { dummy(x-1); }
29 #else
30
31
32 #include <sasl/sasl.h>
33 #include "cyrus_sasl.h"
34
35 /* Options specific to the cyrus_sasl authentication mechanism. */
36
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)) }
46 };
47
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. */
50
51 int auth_cyrus_sasl_options_count =
52 sizeof(auth_cyrus_sasl_options)/sizeof(optionlist);
53
54 /* Default private options block for the cyrus_sasl authentication method. */
55
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 */
61 };
62
63
64 /*************************************************
65 * Initialization entry point *
66 *************************************************/
67
68 /* Called for each instance, after its options have been read, to
69 enable consistency checks to be done, or anything else that needs
70 to be set up. */
71
72
73 /* Auxiliary function, passed in data to sasl_server_init(). */
74
75 static int
76 mysasl_config(void *context,
77 const char *plugin_name,
78 const char *option,
79 const char **result,
80 unsigned int *len)
81 {
82 if (context && !strcmp(option, "mech_list"))
83 {
84 *result = context;
85 if (len != NULL) *len = strlen(*result);
86 return SASL_OK;
87 }
88 return SASL_FAIL;
89 }
90
91 /* Here's the real function */
92
93 void
94 auth_cyrus_sasl_init(auth_instance *ablock)
95 {
96 auth_cyrus_sasl_options_block *ob =
97 (auth_cyrus_sasl_options_block *)(ablock->options_block);
98 uschar *list, *listptr, *buffer;
99 int rc, i;
100 unsigned int len;
101 uschar *rs_point, *expanded_hostname;
102
103 sasl_conn_t *conn;
104 sasl_callback_t cbs[]={
105 {SASL_CB_GETOPT, NULL, NULL },
106 {SASL_CB_LIST_END, NULL, NULL}};
107
108 /* default the mechanism to our "public name" */
109 if(ob->server_mech == NULL)
110 ob->server_mech=string_copy(ablock->public_name);
111
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);
117
118 /* we're going to initialise the library to check that there is an
119 * authenticator of type whatever mechanism we're using
120 */
121
122 cbs[0].proc = (int(*)(void)) &mysasl_config;
123 cbs[0].context = ob->server_mech;
124
125 rc=sasl_server_init(cbs, "exim");
126
127 if( rc != SASL_OK )
128 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
129 "couldn't initialise Cyrus SASL library.", ablock->name);
130
131 rc=sasl_server_new(CS ob->server_service, CS expanded_hostname,
132 CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
133 if( rc != SASL_OK )
134 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
135 "couldn't initialise Cyrus SASL server connection.", ablock->name);
136
137 rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
138 if( rc != SASL_OK )
139 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
140 "couldn't get Cyrus SASL mechanism list.", ablock->name);
141
142 i=':';
143 listptr=list;
144
145 HDEBUG(D_auth) {
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);
149 }
150
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.
154 */
155 rs_point=store_get(0);
156
157 /* loop until either we get to the end of the list, or we match the
158 * public name of this authenticator
159 */
160 while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
161 strcmpic(buffer,ob->server_mech) );
162
163 if(!buffer)
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);
166
167 store_reset(rs_point);
168
169 HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
170
171 /* make sure that if we get here then we're allowed to advertise. */
172 ablock->server = TRUE;
173
174 sasl_dispose(&conn);
175 sasl_done();
176 }
177
178 /*************************************************
179 * Server entry point *
180 *************************************************/
181
182 /* For interface, see auths/README */
183
184 /* note, we don't care too much about memory allocation in this, because this is entirely
185 * within a shortlived child
186 */
187
188 int
189 auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
190 {
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}};
196 sasl_conn_t *conn;
197 int rc, firsttime=1, clen, negotiated_ssf;
198 unsigned int inlen, outlen;
199
200 input=data;
201 inlen=Ustrlen(data);
202
203 HDEBUG(D_auth) debug=string_copy(data);
204
205 hname=expand_string(ob->server_hostname);
206 if(hname == NULL)
207 {
208 auth_defer_msg = expand_string_message;
209 return DEFER;
210 }
211
212 if(inlen)
213 {
214 clen=auth_b64decode(input, &clear);
215 if(clen < 0)
216 {
217 return BAD64;
218 }
219 input=clear;
220 inlen=clen;
221 }
222
223 rc=sasl_server_init(cbs, "exim");
224 if (rc != SASL_OK)
225 {
226 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
227 return DEFER;
228 }
229
230 rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL,
231 NULL, NULL, 0, &conn);
232
233 HDEBUG(D_auth)
234 debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
235 ob->server_service, hname, ob->server_realm);
236
237 if( rc != SASL_OK )
238 {
239 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
240 sasl_done();
241 return DEFER;
242 }
243
244 if (tls_cipher)
245 {
246 rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, &tls_bits);
247 if (rc != SASL_OK)
248 {
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";
252 sasl_done();
253 return DEFER;
254 }
255 else
256 HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_bits);
257 }
258 else
259 HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");
260
261 rc=SASL_CONTINUE;
262
263 while(rc==SASL_CONTINUE)
264 {
265 if(firsttime)
266 {
267 firsttime=0;
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);
271 }
272 else
273 {
274 /* make sure that we have a null-terminated string */
275 out2=store_get(outlen+1);
276 memcpy(out2,output,outlen);
277 out2[outlen]='\0';
278 if((rc=auth_get_data(&input, out2, outlen))!=OK)
279 {
280 /* we couldn't get the data, so free up the library before
281 * returning whatever error we get */
282 sasl_dispose(&conn);
283 sasl_done();
284 return rc;
285 }
286 inlen=Ustrlen(input);
287
288 HDEBUG(D_auth) debug=string_copy(input);
289 if(inlen)
290 {
291 clen=auth_b64decode(input, &clear);
292 if(clen < 0)
293 {
294 sasl_dispose(&conn);
295 sasl_done();
296 return BAD64;
297 }
298 input=clear;
299 inlen=clen;
300 }
301
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);
304 }
305 if(rc==SASL_BADPROT)
306 {
307 sasl_dispose(&conn);
308 sasl_done();
309 return UNEXPECTED;
310 }
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
315 || rc==SASL_NOUSER )
316 {
317 /* these are considered permanent failure codes */
318 HDEBUG(D_auth)
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));
323 sasl_dispose(&conn);
324 sasl_done();
325 return FAIL;
326 }
327 else if(rc==SASL_NOMECH)
328 {
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...
332 */
333 HDEBUG(D_auth)
334 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
335 auth_defer_msg =
336 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
337 sasl_dispose(&conn);
338 sasl_done();
339 return DEFER;
340 }
341 else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
342 {
343 /* Anything else is a temporary failure, and we'll let SASL print out
344 * the error string for us
345 */
346 HDEBUG(D_auth)
347 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
348 auth_defer_msg =
349 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
350 sasl_dispose(&conn);
351 sasl_done();
352 return DEFER;
353 }
354 else if(rc==SASL_OK)
355 {
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));
359 if (rc != SASL_OK)
360 {
361 HDEBUG(D_auth)
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));
367 sasl_dispose(&conn);
368 sasl_done();
369 return FAIL;
370 }
371
372 auth_vars[0] = expand_nstring[1] = string_copy(out2);
373 expand_nlength[1] = Ustrlen(expand_nstring[1]);
374 expand_nmax = 1;
375
376 HDEBUG(D_auth)
377 debug_printf("Cyrus SASL %s authentication succeeded for %s\n",
378 ob->server_mech, auth_vars[0]);
379
380 rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf));
381 if (rc != SASL_OK)
382 {
383 HDEBUG(D_auth)
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));
389 sasl_dispose(&conn);
390 sasl_done();
391 return FAIL;
392 }
393 HDEBUG(D_auth)
394 debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf);
395 if (negotiated_ssf > 0)
396 {
397 HDEBUG(D_auth)
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);
401 sasl_dispose(&conn);
402 sasl_done();
403 return FAIL;
404 }
405
406 /* close down the connection, freeing up library's memory */
407 sasl_dispose(&conn);
408 sasl_done();
409
410 /* Expand server_condition as an authorization check */
411 return auth_check_serv_cond(ablock);
412 }
413 }
414 /* NOTREACHED */
415 return 0; /* Stop compiler complaints */
416 }
417
418 /*************************************************
419 * Diagnostic API *
420 *************************************************/
421
422 void
423 auth_cyrus_sasl_version_report(FILE *f)
424 {
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);
431 }
432
433 /*************************************************
434 * Client entry point *
435 *************************************************/
436
437 /* For interface, see auths/README */
438
439 int
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 */
447 {
448 /* We don't support clients (yet) in this implementation of cyrus_sasl */
449 return FAIL;
450 }
451
452 #endif /* AUTH_CYRUS_SASL */
453
454 /* End of cyrus_sasl.c */