Kjetil Homme's patch for extended macro features (redefinition,
[exim.git] / src / src / auths / cyrus_sasl.c
CommitLineData
0756eb3c
PH
1/* $Cambridge: exim/src/src/auths/cyrus_sasl.c,v 1.1 2004/10/07 13:10:01 ph10 Exp $ */
2
3/*************************************************
4* Exim - an Internet mail transport agent *
5*************************************************/
6
7/* Copyright (c) University of Cambridge 1995 - 2003 */
8/* See the file NOTICE for conditions of use and distribution. */
9
10/* This code was 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
21functions if they are not wanted, because we need to have the Cyrus SASL header
22available for compiling. Therefore, compile these functions only if
23AUTH_CYRUS_SASL is defined. However, some compilers don't like compiling empty
24modules, so keep them happy with a dummy when skipping the rest. Make it
25reference itself to stop picky compilers complaining that it is unused, and put
26in a dummy argument to stop even pickier compilers complaining about infinite
27loops. */
28
29#ifndef AUTH_CYRUS_SASL
30static 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
39optionlist 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
51address can appear in the tables drtables.c. */
52
53int auth_cyrus_sasl_options_count =
54 sizeof(auth_cyrus_sasl_options)/sizeof(optionlist);
55
56/* Default private options block for the contidion authentication method. */
57
58auth_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
71enable consistency checks to be done, or anything else that needs
72to be set up. */
73
74void
75auth_cyrus_sasl_init(auth_instance *ablock)
76{
77auth_cyrus_sasl_options_block *ob =
78 (auth_cyrus_sasl_options_block *)(ablock->options_block);
79sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
80sasl_conn_t *conn;
81uschar *list, *listptr, *buffer;
82int rc, i;
83unsigned int len;
84uschar *rs_point;
85
86/* default the mechanism to our "public name" */
87if(ob->server_mech == NULL)
88 ob->server_mech=string_copy(ablock->public_name);
89
90/* we're going to initialise the library to check that there is an
91 * authenticator of type whatever mechanism we're using
92 */
93rc=sasl_server_init(cbs, "exim");
94if( rc != SASL_OK )
95 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
96 "couldn't initialise Cyrus SASL library.", ablock->name);
97
98rc=sasl_server_new(CS ob->server_service, CS primary_hostname,
99 CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
100if( rc != SASL_OK )
101 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
102 "couldn't initialise Cyrus SASL server connection.", ablock->name);
103
104rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
105if( rc != SASL_OK )
106 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
107 "couldn't get Cyrus SASL mechanism list.", ablock->name);
108
109i=':';
110listptr=list;
111
112HDEBUG(D_auth) debug_printf("Cyrus SASL knows about: %s\n", list);
113
114/* the store_get / store_reset mechanism is hierarchical
115 * the hierarchy is stored for us behind our back. This point
116 * creates a hierarchy point for this function.
117 */
118rs_point=store_get(0);
119
120/* loop until either we get to the end of the list, or we match the
121 * public name of this authenticator
122 */
123while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
124 strcmpic(buffer,ob->server_mech) );
125
126if(!buffer)
127 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator: "
128 "Cyrus SASL doesn't know about mechanism %s.", ablock->name, ob->server_mech);
129
130store_reset(rs_point);
131
132HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
133
134/* make sure that if we get here then we're allowed to advertise. */
135ablock->server = TRUE;
136
137sasl_dispose(&conn);
138sasl_done();
139}
140
141/*************************************************
142* Server entry point *
143*************************************************/
144
145/* For interface, see auths/README */
146
147/* note, we don't care too much about memory allocation in this, because this is entirely
148 * within a shortlived child
149 */
150
151int
152auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
153{
154auth_cyrus_sasl_options_block *ob =
155 (auth_cyrus_sasl_options_block *)(ablock->options_block);
156uschar *output, *out2, *input, *clear, *hname;
157uschar *debug = NULL; /* Stops compiler complaining */
158sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
159sasl_conn_t *conn;
160int rc, firsttime=1, clen;
161unsigned int inlen, outlen;
162
163input=data;
164inlen=Ustrlen(data);
165
166HDEBUG(D_auth) debug=string_copy(data);
167
168hname=expand_string(ob->server_hostname);
169if(hname == NULL)
170 {
171 auth_defer_msg = expand_string_message;
172 return DEFER;
173 }
174
175if(inlen)
176 {
177 clen=auth_b64decode(input, &clear);
178 if(clen < 0)
179 {
180 return BAD64;
181 }
182 input=clear;
183 inlen=clen;
184 }
185
186rc=sasl_server_init(cbs, "exim");
187if (rc != SASL_OK)
188 {
189 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
190 return DEFER;
191 }
192
193rc=sasl_server_new(CS ob->server_service, CS ob->server_hostname,
194 CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
195if( rc != SASL_OK )
196 {
197 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
198 sasl_done();
199 return DEFER;
200 }
201
202rc=SASL_CONTINUE;
203
204while(rc==SASL_CONTINUE)
205 {
206 if(firsttime)
207 {
208 firsttime=0;
209 HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
210 rc=sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
211 (const char **)(&output), &outlen);
212 }
213 else
214 {
215 /* make sure that we have a null-terminated string */
216 out2=store_get(outlen+1);
217 memcpy(out2,output,outlen);
218 out2[outlen]='\0';
219 if((rc=auth_get_data(&input, out2, outlen))!=OK)
220 {
221 /* we couldn't get the data, so free up the library before
222 * returning whatever error we get */
223 sasl_dispose(&conn);
224 sasl_done();
225 return rc;
226 }
227 inlen=Ustrlen(input);
228
229 HDEBUG(D_auth) debug=string_copy(input);
230 if(inlen)
231 {
232 clen=auth_b64decode(input, &clear);
233 if(clen < 0)
234 {
235 sasl_dispose(&conn);
236 sasl_done();
237 return BAD64;
238 }
239 input=clear;
240 inlen=clen;
241 }
242
243 HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
244 rc=sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
245 }
246 if(rc==SASL_BADPROT)
247 {
248 sasl_dispose(&conn);
249 sasl_done();
250 return UNEXPECTED;
251 }
252 else if( rc==SASL_FAIL || rc==SASL_BUFOVER
253 || rc==SASL_BADMAC || rc==SASL_BADAUTH
254 || rc==SASL_NOAUTHZ || rc==SASL_ENCRYPT
255 || rc==SASL_EXPIRED || rc==SASL_DISABLED
256 || rc==SASL_NOUSER )
257 {
258 /* these are considered permanent failure codes */
259 HDEBUG(D_auth)
260 debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
261 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
262 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
263 sasl_errstring(rc, NULL, NULL));
264 sasl_dispose(&conn);
265 sasl_done();
266 return FAIL;
267 }
268 else if(rc==SASL_NOMECH)
269 {
270 /* this is a temporary failure, because the mechanism is not
271 * available for this user. If it wasn't available at all, we
272 * shouldn't have got here in the first place...
273 */
274 HDEBUG(D_auth)
275 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
276 auth_defer_msg =
277 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
278 sasl_dispose(&conn);
279 sasl_done();
280 return DEFER;
281 }
282 else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
283 {
284 /* Anything else is a temporary failure, and we'll let SASL print out
285 * the error string for us
286 */
287 HDEBUG(D_auth)
288 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
289 auth_defer_msg =
290 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
291 sasl_dispose(&conn);
292 sasl_done();
293 return DEFER;
294 }
295 else if(rc==SASL_OK)
296 {
297 /* get the username and copy it into $1 */
298 rc=sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
299 expand_nstring[1]=string_copy(out2);
300 expand_nlength[1]=Ustrlen(expand_nstring[1]);
301 expand_nmax=1;
302
303 HDEBUG(D_auth)
304 debug_printf("Cyrus SASL %s authentiction succeeded for %s\n", ob->server_mech, out2);
305 /* close down the connection, freeing up library's memory */
306 sasl_dispose(&conn);
307 sasl_done();
308 return OK;
309 }
310 }
311/* NOTREACHED */
312return 0; /* Stop compiler complaints */
313}
314
315/*************************************************
316* Client entry point *
317*************************************************/
318
319/* For interface, see auths/README */
320
321int
322auth_cyrus_sasl_client(
323 auth_instance *ablock, /* authenticator block */
324 smtp_inblock *inblock, /* input connection */
325 smtp_outblock *outblock, /* output connection */
326 int timeout, /* command timeout */
327 uschar *buffer, /* for reading response */
328 int buffsize) /* size of buffer */
329{
330/* We don't support clients (yet) in this implementation of cyrus_sasl */
331return FAIL;
332}
333
334#endif /* AUTH_CYRUS_SASL */
335
336/* End of cyrus_sasl.c */