Merge branch 'tom_dev'
[exim.git] / src / src / auths / cyrus_sasl.c
1 /* $Cambridge: exim/src/src/auths/cyrus_sasl.c,v 1.6 2009/11/16 19:50:38 nm4 Exp $ */
2
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
6
7 /* Copyright (c) University of Cambridge 1995 - 2009 */
8 /* See the file NOTICE for conditions of use and distribution. */
9
10 /* This code was originally contributed by Matthew Byng-Maddick */
11
12 /* Copyright (c) A L Digital 2004 */
13
14 /* A generic (mechanism independent) Cyrus SASL authenticator. */
15
16
17 #include "../exim.h"
18
19
20 /* We can't just compile this code and allow the library mechanism to omit the
21 functions if they are not wanted, because we need to have the Cyrus SASL header
22 available for compiling. Therefore, compile these functions only if
23 AUTH_CYRUS_SASL is defined. However, some compilers don't like compiling empty
24 modules, so keep them happy with a dummy when skipping the rest. Make it
25 reference itself to stop picky compilers complaining that it is unused, and put
26 in a dummy argument to stop even pickier compilers complaining about infinite
27 loops. */
28
29 #ifndef AUTH_CYRUS_SASL
30 static void dummy(int x) { dummy(x-1); }
31 #else
32
33
34 #include <sasl/sasl.h>
35 #include "cyrus_sasl.h"
36
37 /* Options specific to the cyrus_sasl authentication mechanism. */
38
39 optionlist auth_cyrus_sasl_options[] = {
40 { "server_hostname", opt_stringptr,
41 (void *)(offsetof(auth_cyrus_sasl_options_block, server_hostname)) },
42 { "server_mech", opt_stringptr,
43 (void *)(offsetof(auth_cyrus_sasl_options_block, server_mech)) },
44 { "server_realm", opt_stringptr,
45 (void *)(offsetof(auth_cyrus_sasl_options_block, server_realm)) },
46 { "server_service", opt_stringptr,
47 (void *)(offsetof(auth_cyrus_sasl_options_block, server_service)) }
48 };
49
50 /* Size of the options list. An extern variable has to be used so that its
51 address can appear in the tables drtables.c. */
52
53 int auth_cyrus_sasl_options_count =
54 sizeof(auth_cyrus_sasl_options)/sizeof(optionlist);
55
56 /* Default private options block for the cyrus_sasl authentication method. */
57
58 auth_cyrus_sasl_options_block auth_cyrus_sasl_option_defaults = {
59 US"smtp", /* server_service */
60 US"$primary_hostname", /* server_hostname */
61 NULL, /* server_realm */
62 NULL /* server_mech */
63 };
64
65
66 /*************************************************
67 * Initialization entry point *
68 *************************************************/
69
70 /* Called for each instance, after its options have been read, to
71 enable consistency checks to be done, or anything else that needs
72 to be set up. */
73
74
75 /* Auxiliary function, passed in data to sasl_server_init(). */
76
77 static int
78 mysasl_config(void *context,
79 const char *plugin_name,
80 const char *option,
81 const char **result,
82 unsigned int *len)
83 {
84 if (context && !strcmp(option, "mech_list"))
85 {
86 *result = context;
87 if (len != NULL) *len = strlen(*result);
88 return SASL_OK;
89 }
90 return SASL_FAIL;
91 }
92
93 /* Here's the real function */
94
95 void
96 auth_cyrus_sasl_init(auth_instance *ablock)
97 {
98 auth_cyrus_sasl_options_block *ob =
99 (auth_cyrus_sasl_options_block *)(ablock->options_block);
100 uschar *list, *listptr, *buffer;
101 int rc, i;
102 unsigned int len;
103 uschar *rs_point;
104
105 sasl_conn_t *conn;
106 sasl_callback_t cbs[]={
107 {SASL_CB_GETOPT, NULL, NULL },
108 {SASL_CB_LIST_END, NULL, NULL}};
109
110 /* default the mechanism to our "public name" */
111 if(ob->server_mech == NULL)
112 ob->server_mech=string_copy(ablock->public_name);
113
114 /* we're going to initialise the library to check that there is an
115 * authenticator of type whatever mechanism we're using
116 */
117
118 cbs[0].proc = &mysasl_config;
119 cbs[0].context = ob->server_mech;
120
121 rc=sasl_server_init(cbs, "exim");
122
123 if( rc != SASL_OK )
124 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
125 "couldn't initialise Cyrus SASL library.", ablock->name);
126
127 rc=sasl_server_new(CS ob->server_service, CS primary_hostname,
128 CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
129 if( rc != SASL_OK )
130 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
131 "couldn't initialise Cyrus SASL server connection.", ablock->name);
132
133 rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
134 if( rc != SASL_OK )
135 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
136 "couldn't get Cyrus SASL mechanism list.", ablock->name);
137
138 i=':';
139 listptr=list;
140
141 HDEBUG(D_auth) debug_printf("Cyrus SASL knows about: %s\n", list);
142
143 /* the store_get / store_reset mechanism is hierarchical
144 * the hierarchy is stored for us behind our back. This point
145 * creates a hierarchy point for this function.
146 */
147 rs_point=store_get(0);
148
149 /* loop until either we get to the end of the list, or we match the
150 * public name of this authenticator
151 */
152 while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
153 strcmpic(buffer,ob->server_mech) );
154
155 if(!buffer)
156 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
157 "Cyrus SASL doesn't know about mechanism %s.", ablock->name, ob->server_mech);
158
159 store_reset(rs_point);
160
161 HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
162
163 /* make sure that if we get here then we're allowed to advertise. */
164 ablock->server = TRUE;
165
166 sasl_dispose(&conn);
167 sasl_done();
168 }
169
170 /*************************************************
171 * Server entry point *
172 *************************************************/
173
174 /* For interface, see auths/README */
175
176 /* note, we don't care too much about memory allocation in this, because this is entirely
177 * within a shortlived child
178 */
179
180 int
181 auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
182 {
183 auth_cyrus_sasl_options_block *ob =
184 (auth_cyrus_sasl_options_block *)(ablock->options_block);
185 uschar *output, *out2, *input, *clear, *hname;
186 uschar *debug = NULL; /* Stops compiler complaining */
187 sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
188 sasl_conn_t *conn;
189 int rc, firsttime=1, clen;
190 unsigned int inlen, outlen;
191
192 input=data;
193 inlen=Ustrlen(data);
194
195 HDEBUG(D_auth) debug=string_copy(data);
196
197 hname=expand_string(ob->server_hostname);
198 if(hname == NULL)
199 {
200 auth_defer_msg = expand_string_message;
201 return DEFER;
202 }
203
204 if(inlen)
205 {
206 clen=auth_b64decode(input, &clear);
207 if(clen < 0)
208 {
209 return BAD64;
210 }
211 input=clear;
212 inlen=clen;
213 }
214
215 rc=sasl_server_init(cbs, "exim");
216 if (rc != SASL_OK)
217 {
218 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
219 return DEFER;
220 }
221
222 rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL,
223 NULL, NULL, 0, &conn);
224
225 if( rc != SASL_OK )
226 {
227 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
228 sasl_done();
229 return DEFER;
230 }
231
232 rc=SASL_CONTINUE;
233
234 while(rc==SASL_CONTINUE)
235 {
236 if(firsttime)
237 {
238 firsttime=0;
239 HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
240 rc=sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
241 (const char **)(&output), &outlen);
242 }
243 else
244 {
245 /* make sure that we have a null-terminated string */
246 out2=store_get(outlen+1);
247 memcpy(out2,output,outlen);
248 out2[outlen]='\0';
249 if((rc=auth_get_data(&input, out2, outlen))!=OK)
250 {
251 /* we couldn't get the data, so free up the library before
252 * returning whatever error we get */
253 sasl_dispose(&conn);
254 sasl_done();
255 return rc;
256 }
257 inlen=Ustrlen(input);
258
259 HDEBUG(D_auth) debug=string_copy(input);
260 if(inlen)
261 {
262 clen=auth_b64decode(input, &clear);
263 if(clen < 0)
264 {
265 sasl_dispose(&conn);
266 sasl_done();
267 return BAD64;
268 }
269 input=clear;
270 inlen=clen;
271 }
272
273 HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
274 rc=sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
275 }
276 if(rc==SASL_BADPROT)
277 {
278 sasl_dispose(&conn);
279 sasl_done();
280 return UNEXPECTED;
281 }
282 else if( rc==SASL_FAIL || rc==SASL_BUFOVER
283 || rc==SASL_BADMAC || rc==SASL_BADAUTH
284 || rc==SASL_NOAUTHZ || rc==SASL_ENCRYPT
285 || rc==SASL_EXPIRED || rc==SASL_DISABLED
286 || rc==SASL_NOUSER )
287 {
288 /* these are considered permanent failure codes */
289 HDEBUG(D_auth)
290 debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
291 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
292 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
293 sasl_errstring(rc, NULL, NULL));
294 sasl_dispose(&conn);
295 sasl_done();
296 return FAIL;
297 }
298 else if(rc==SASL_NOMECH)
299 {
300 /* this is a temporary failure, because the mechanism is not
301 * available for this user. If it wasn't available at all, we
302 * shouldn't have got here in the first place...
303 */
304 HDEBUG(D_auth)
305 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
306 auth_defer_msg =
307 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
308 sasl_dispose(&conn);
309 sasl_done();
310 return DEFER;
311 }
312 else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
313 {
314 /* Anything else is a temporary failure, and we'll let SASL print out
315 * the error string for us
316 */
317 HDEBUG(D_auth)
318 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
319 auth_defer_msg =
320 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
321 sasl_dispose(&conn);
322 sasl_done();
323 return DEFER;
324 }
325 else if(rc==SASL_OK)
326 {
327 /* Get the username and copy it into $auth1 and $1. The former is now the
328 preferred variable; the latter is the original variable. */
329 rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
330 auth_vars[0] = expand_nstring[1] = string_copy(out2);
331 expand_nlength[1] = Ustrlen(expand_nstring[1]);
332 expand_nmax = 1;
333
334 HDEBUG(D_auth)
335 debug_printf("Cyrus SASL %s authentication succeeded for %s\n", ob->server_mech, out2);
336 /* close down the connection, freeing up library's memory */
337 sasl_dispose(&conn);
338 sasl_done();
339
340 /* Expand server_condition as an authorization check */
341 return auth_check_serv_cond(ablock);
342 }
343 }
344 /* NOTREACHED */
345 return 0; /* Stop compiler complaints */
346 }
347
348 /*************************************************
349 * Diagnostic API *
350 *************************************************/
351
352 void
353 auth_cyrus_sasl_version_report(FILE *f)
354 {
355 const char *implementation, *version;
356 sasl_version_info(&implementation, &version, NULL, NULL, NULL, NULL);
357 fprintf(f, "Library version: Cyrus SASL: Compile: %d.%d.%d\n"
358 " Runtime: %s [%s]\n",
359 SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
360 version, implementation);
361 }
362
363 /*************************************************
364 * Client entry point *
365 *************************************************/
366
367 /* For interface, see auths/README */
368
369 int
370 auth_cyrus_sasl_client(
371 auth_instance *ablock, /* authenticator block */
372 smtp_inblock *inblock, /* input connection */
373 smtp_outblock *outblock, /* output connection */
374 int timeout, /* command timeout */
375 uschar *buffer, /* for reading response */
376 int buffsize) /* size of buffer */
377 {
378 /* We don't support clients (yet) in this implementation of cyrus_sasl */
379 return FAIL;
380 }
381
382 #endif /* AUTH_CYRUS_SASL */
383
384 /* End of cyrus_sasl.c */