bool{} is false for empty strings
[exim.git] / src / src / auths / cyrus_sasl.c
CommitLineData
0756eb3c
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
0a49a7a4 5/* Copyright (c) University of Cambridge 1995 - 2009 */
0756eb3c
PH
6/* See the file NOTICE for conditions of use and distribution. */
7
c5ddb310 8/* This code was originally contributed by Matthew Byng-Maddick */
0756eb3c
PH
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
19functions if they are not wanted, because we need to have the Cyrus SASL header
20available for compiling. Therefore, compile these functions only if
21AUTH_CYRUS_SASL is defined. However, some compilers don't like compiling empty
22modules, so keep them happy with a dummy when skipping the rest. Make it
23reference itself to stop picky compilers complaining that it is unused, and put
24in a dummy argument to stop even pickier compilers complaining about infinite
25loops. */
26
27#ifndef AUTH_CYRUS_SASL
28static 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
37optionlist 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
49address can appear in the tables drtables.c. */
50
51int auth_cyrus_sasl_options_count =
52 sizeof(auth_cyrus_sasl_options)/sizeof(optionlist);
53
16ff981e 54/* Default private options block for the cyrus_sasl authentication method. */
0756eb3c
PH
55
56auth_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
69enable consistency checks to be done, or anything else that needs
70to be set up. */
71
c5ddb310
PH
72
73/* Auxiliary function, passed in data to sasl_server_init(). */
74
75static int
76mysasl_config(void *context,
77 const char *plugin_name,
78 const char *option,
79 const char **result,
80 unsigned int *len)
81{
82if (context && !strcmp(option, "mech_list"))
83 {
84 *result = context;
85 if (len != NULL) *len = strlen(*result);
86 return SASL_OK;
87 }
88return SASL_FAIL;
89}
90
91/* Here's the real function */
92
0756eb3c
PH
93void
94auth_cyrus_sasl_init(auth_instance *ablock)
95{
96auth_cyrus_sasl_options_block *ob =
97 (auth_cyrus_sasl_options_block *)(ablock->options_block);
0756eb3c
PH
98uschar *list, *listptr, *buffer;
99int rc, i;
100unsigned int len;
101uschar *rs_point;
102
c5ddb310
PH
103sasl_conn_t *conn;
104sasl_callback_t cbs[]={
105 {SASL_CB_GETOPT, NULL, NULL },
106 {SASL_CB_LIST_END, NULL, NULL}};
107
0756eb3c
PH
108/* default the mechanism to our "public name" */
109if(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 */
c5ddb310
PH
115
116cbs[0].proc = &mysasl_config;
117cbs[0].context = ob->server_mech;
118
0756eb3c 119rc=sasl_server_init(cbs, "exim");
c5ddb310 120
0756eb3c
PH
121if( 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
125rc=sasl_server_new(CS ob->server_service, CS primary_hostname,
126 CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
127if( 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
131rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
132if( 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
136i=':';
137listptr=list;
138
139HDEBUG(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 */
145rs_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 */
150while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
151 strcmpic(buffer,ob->server_mech) );
152
153if(!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
157store_reset(rs_point);
158
159HDEBUG(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. */
162ablock->server = TRUE;
163
164sasl_dispose(&conn);
165sasl_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
178int
179auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
180{
181auth_cyrus_sasl_options_block *ob =
182 (auth_cyrus_sasl_options_block *)(ablock->options_block);
183uschar *output, *out2, *input, *clear, *hname;
184uschar *debug = NULL; /* Stops compiler complaining */
185sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
186sasl_conn_t *conn;
187int rc, firsttime=1, clen;
188unsigned int inlen, outlen;
189
190input=data;
191inlen=Ustrlen(data);
192
193HDEBUG(D_auth) debug=string_copy(data);
194
195hname=expand_string(ob->server_hostname);
196if(hname == NULL)
197 {
198 auth_defer_msg = expand_string_message;
199 return DEFER;
200 }
201
202if(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
213rc=sasl_server_init(cbs, "exim");
214if (rc != SASL_OK)
215 {
216 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
217 return DEFER;
218 }
219
acb1b346
PH
220rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL,
221 NULL, NULL, 0, &conn);
222
0756eb3c
PH
223if( rc != SASL_OK )
224 {
225 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
226 sasl_done();
227 return DEFER;
228 }
229
230rc=SASL_CONTINUE;
231
232while(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 {
f78eb7c6
PH
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;
0756eb3c
PH
331
332 HDEBUG(D_auth)
16ff981e 333 debug_printf("Cyrus SASL %s authentication succeeded for %s\n", ob->server_mech, out2);
0756eb3c
PH
334 /* close down the connection, freeing up library's memory */
335 sasl_dispose(&conn);
336 sasl_done();
16ff981e
PH
337
338 /* Expand server_condition as an authorization check */
339 return auth_check_serv_cond(ablock);
0756eb3c
PH
340 }
341 }
342/* NOTREACHED */
343return 0; /* Stop compiler complaints */
344}
345
6545de78
PP
346/*************************************************
347* Diagnostic API *
348*************************************************/
349
350void
351auth_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
0756eb3c
PH
361/*************************************************
362* Client entry point *
363*************************************************/
364
365/* For interface, see auths/README */
366
367int
368auth_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 */
377return FAIL;
378}
379
380#endif /* AUTH_CYRUS_SASL */
381
382/* End of cyrus_sasl.c */