fea1def38392db142e64a48b94aab91c07a9c3db
[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;
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 /* we're going to initialise the library to check that there is an
113 * authenticator of type whatever mechanism we're using
114 */
115
116 cbs[0].proc = &mysasl_config;
117 cbs[0].context = ob->server_mech;
118
119 rc=sasl_server_init(cbs, "exim");
120
121 if( rc != SASL_OK )
122 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
123 "couldn't initialise Cyrus SASL library.", ablock->name);
124
125 rc=sasl_server_new(CS ob->server_service, CS primary_hostname,
126 CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
127 if( rc != SASL_OK )
128 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
129 "couldn't initialise Cyrus SASL server connection.", ablock->name);
130
131 rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
132 if( rc != SASL_OK )
133 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
134 "couldn't get Cyrus SASL mechanism list.", ablock->name);
135
136 i=':';
137 listptr=list;
138
139 HDEBUG(D_auth) debug_printf("Cyrus SASL knows about: %s\n", list);
140
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.
144 */
145 rs_point=store_get(0);
146
147 /* loop until either we get to the end of the list, or we match the
148 * public name of this authenticator
149 */
150 while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
151 strcmpic(buffer,ob->server_mech) );
152
153 if(!buffer)
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);
156
157 store_reset(rs_point);
158
159 HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
160
161 /* make sure that if we get here then we're allowed to advertise. */
162 ablock->server = TRUE;
163
164 sasl_dispose(&conn);
165 sasl_done();
166 }
167
168 /*************************************************
169 * Server entry point *
170 *************************************************/
171
172 /* For interface, see auths/README */
173
174 /* note, we don't care too much about memory allocation in this, because this is entirely
175 * within a shortlived child
176 */
177
178 int
179 auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
180 {
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}};
186 sasl_conn_t *conn;
187 int rc, firsttime=1, clen;
188 unsigned int inlen, outlen;
189
190 input=data;
191 inlen=Ustrlen(data);
192
193 HDEBUG(D_auth) debug=string_copy(data);
194
195 hname=expand_string(ob->server_hostname);
196 if(hname == NULL)
197 {
198 auth_defer_msg = expand_string_message;
199 return DEFER;
200 }
201
202 if(inlen)
203 {
204 clen=auth_b64decode(input, &clear);
205 if(clen < 0)
206 {
207 return BAD64;
208 }
209 input=clear;
210 inlen=clen;
211 }
212
213 rc=sasl_server_init(cbs, "exim");
214 if (rc != SASL_OK)
215 {
216 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
217 return DEFER;
218 }
219
220 rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL,
221 NULL, NULL, 0, &conn);
222
223 if( rc != SASL_OK )
224 {
225 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
226 sasl_done();
227 return DEFER;
228 }
229
230 rc=SASL_CONTINUE;
231
232 while(rc==SASL_CONTINUE)
233 {
234 if(firsttime)
235 {
236 firsttime=0;
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);
240 }
241 else
242 {
243 /* make sure that we have a null-terminated string */
244 out2=store_get(outlen+1);
245 memcpy(out2,output,outlen);
246 out2[outlen]='\0';
247 if((rc=auth_get_data(&input, out2, outlen))!=OK)
248 {
249 /* we couldn't get the data, so free up the library before
250 * returning whatever error we get */
251 sasl_dispose(&conn);
252 sasl_done();
253 return rc;
254 }
255 inlen=Ustrlen(input);
256
257 HDEBUG(D_auth) debug=string_copy(input);
258 if(inlen)
259 {
260 clen=auth_b64decode(input, &clear);
261 if(clen < 0)
262 {
263 sasl_dispose(&conn);
264 sasl_done();
265 return BAD64;
266 }
267 input=clear;
268 inlen=clen;
269 }
270
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);
273 }
274 if(rc==SASL_BADPROT)
275 {
276 sasl_dispose(&conn);
277 sasl_done();
278 return UNEXPECTED;
279 }
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
284 || rc==SASL_NOUSER )
285 {
286 /* these are considered permanent failure codes */
287 HDEBUG(D_auth)
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));
292 sasl_dispose(&conn);
293 sasl_done();
294 return FAIL;
295 }
296 else if(rc==SASL_NOMECH)
297 {
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...
301 */
302 HDEBUG(D_auth)
303 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
304 auth_defer_msg =
305 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
306 sasl_dispose(&conn);
307 sasl_done();
308 return DEFER;
309 }
310 else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
311 {
312 /* Anything else is a temporary failure, and we'll let SASL print out
313 * the error string for us
314 */
315 HDEBUG(D_auth)
316 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
317 auth_defer_msg =
318 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
319 sasl_dispose(&conn);
320 sasl_done();
321 return DEFER;
322 }
323 else if(rc==SASL_OK)
324 {
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]);
330 expand_nmax = 1;
331
332 HDEBUG(D_auth)
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 */
335 sasl_dispose(&conn);
336 sasl_done();
337
338 /* Expand server_condition as an authorization check */
339 return auth_check_serv_cond(ablock);
340 }
341 }
342 /* NOTREACHED */
343 return 0; /* Stop compiler complaints */
344 }
345
346 /*************************************************
347 * Diagnostic API *
348 *************************************************/
349
350 void
351 auth_cyrus_sasl_version_report(FILE *f)
352 {
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);
359 }
360
361 /*************************************************
362 * Client entry point *
363 *************************************************/
364
365 /* For interface, see auths/README */
366
367 int
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 */
375 {
376 /* We don't support clients (yet) in this implementation of cyrus_sasl */
377 return FAIL;
378 }
379
380 #endif /* AUTH_CYRUS_SASL */
381
382 /* End of cyrus_sasl.c */