Start
[exim.git] / src / src / auths / cyrus_sasl.c
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
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 contidion 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 void
75 auth_cyrus_sasl_init(auth_instance *ablock)
76 {
77 auth_cyrus_sasl_options_block *ob =
78 (auth_cyrus_sasl_options_block *)(ablock->options_block);
79 sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
80 sasl_conn_t *conn;
81 uschar *list, *listptr, *buffer;
82 int rc, i;
83 unsigned int len;
84 uschar *rs_point;
85
86 /* default the mechanism to our "public name" */
87 if(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 */
93 rc=sasl_server_init(cbs, "exim");
94 if( 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
98 rc=sasl_server_new(CS ob->server_service, CS primary_hostname,
99 CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
100 if( 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
104 rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
105 if( 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
109 i=':';
110 listptr=list;
111
112 HDEBUG(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 */
118 rs_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 */
123 while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
124 strcmpic(buffer,ob->server_mech) );
125
126 if(!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
130 store_reset(rs_point);
131
132 HDEBUG(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. */
135 ablock->server = TRUE;
136
137 sasl_dispose(&conn);
138 sasl_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
151 int
152 auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
153 {
154 auth_cyrus_sasl_options_block *ob =
155 (auth_cyrus_sasl_options_block *)(ablock->options_block);
156 uschar *output, *out2, *input, *clear, *hname;
157 uschar *debug = NULL; /* Stops compiler complaining */
158 sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
159 sasl_conn_t *conn;
160 int rc, firsttime=1, clen;
161 unsigned int inlen, outlen;
162
163 input=data;
164 inlen=Ustrlen(data);
165
166 HDEBUG(D_auth) debug=string_copy(data);
167
168 hname=expand_string(ob->server_hostname);
169 if(hname == NULL)
170 {
171 auth_defer_msg = expand_string_message;
172 return DEFER;
173 }
174
175 if(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
186 rc=sasl_server_init(cbs, "exim");
187 if (rc != SASL_OK)
188 {
189 auth_defer_msg = US"couldn't initialise Cyrus SASL library";
190 return DEFER;
191 }
192
193 rc=sasl_server_new(CS ob->server_service, CS ob->server_hostname,
194 CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
195 if( rc != SASL_OK )
196 {
197 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
198 sasl_done();
199 return DEFER;
200 }
201
202 rc=SASL_CONTINUE;
203
204 while(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 */
312 return 0; /* Stop compiler complaints */
313 }
314
315 /*************************************************
316 * Client entry point *
317 *************************************************/
318
319 /* For interface, see auths/README */
320
321 int
322 auth_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 */
331 return FAIL;
332 }
333
334 #endif /* AUTH_CYRUS_SASL */
335
336 /* End of cyrus_sasl.c */