849c3d1c0569d37e5536e30bea4128fb5a3ff699
[exim.git] / src / src / auths / cyrus_sasl.c
1 /* $Cambridge: exim/src/src/auths/cyrus_sasl.c,v 1.2 2005/04/05 14:02:30 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 hname, CS ob->server_realm, NULL,
194 NULL, NULL, 0, &conn);
195
196 if( rc != SASL_OK )
197 {
198 auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
199 sasl_done();
200 return DEFER;
201 }
202
203 rc=SASL_CONTINUE;
204
205 while(rc==SASL_CONTINUE)
206 {
207 if(firsttime)
208 {
209 firsttime=0;
210 HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
211 rc=sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
212 (const char **)(&output), &outlen);
213 }
214 else
215 {
216 /* make sure that we have a null-terminated string */
217 out2=store_get(outlen+1);
218 memcpy(out2,output,outlen);
219 out2[outlen]='\0';
220 if((rc=auth_get_data(&input, out2, outlen))!=OK)
221 {
222 /* we couldn't get the data, so free up the library before
223 * returning whatever error we get */
224 sasl_dispose(&conn);
225 sasl_done();
226 return rc;
227 }
228 inlen=Ustrlen(input);
229
230 HDEBUG(D_auth) debug=string_copy(input);
231 if(inlen)
232 {
233 clen=auth_b64decode(input, &clear);
234 if(clen < 0)
235 {
236 sasl_dispose(&conn);
237 sasl_done();
238 return BAD64;
239 }
240 input=clear;
241 inlen=clen;
242 }
243
244 HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
245 rc=sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
246 }
247 if(rc==SASL_BADPROT)
248 {
249 sasl_dispose(&conn);
250 sasl_done();
251 return UNEXPECTED;
252 }
253 else if( rc==SASL_FAIL || rc==SASL_BUFOVER
254 || rc==SASL_BADMAC || rc==SASL_BADAUTH
255 || rc==SASL_NOAUTHZ || rc==SASL_ENCRYPT
256 || rc==SASL_EXPIRED || rc==SASL_DISABLED
257 || rc==SASL_NOUSER )
258 {
259 /* these are considered permanent failure codes */
260 HDEBUG(D_auth)
261 debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
262 log_write(0, LOG_REJECT, "%s authenticator (%s):\n "
263 "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
264 sasl_errstring(rc, NULL, NULL));
265 sasl_dispose(&conn);
266 sasl_done();
267 return FAIL;
268 }
269 else if(rc==SASL_NOMECH)
270 {
271 /* this is a temporary failure, because the mechanism is not
272 * available for this user. If it wasn't available at all, we
273 * shouldn't have got here in the first place...
274 */
275 HDEBUG(D_auth)
276 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
277 auth_defer_msg =
278 string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
279 sasl_dispose(&conn);
280 sasl_done();
281 return DEFER;
282 }
283 else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
284 {
285 /* Anything else is a temporary failure, and we'll let SASL print out
286 * the error string for us
287 */
288 HDEBUG(D_auth)
289 debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
290 auth_defer_msg =
291 string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
292 sasl_dispose(&conn);
293 sasl_done();
294 return DEFER;
295 }
296 else if(rc==SASL_OK)
297 {
298 /* get the username and copy it into $1 */
299 rc=sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
300 expand_nstring[1]=string_copy(out2);
301 expand_nlength[1]=Ustrlen(expand_nstring[1]);
302 expand_nmax=1;
303
304 HDEBUG(D_auth)
305 debug_printf("Cyrus SASL %s authentiction succeeded for %s\n", ob->server_mech, out2);
306 /* close down the connection, freeing up library's memory */
307 sasl_dispose(&conn);
308 sasl_done();
309 return OK;
310 }
311 }
312 /* NOTREACHED */
313 return 0; /* Stop compiler complaints */
314 }
315
316 /*************************************************
317 * Client entry point *
318 *************************************************/
319
320 /* For interface, see auths/README */
321
322 int
323 auth_cyrus_sasl_client(
324 auth_instance *ablock, /* authenticator block */
325 smtp_inblock *inblock, /* input connection */
326 smtp_outblock *outblock, /* output connection */
327 int timeout, /* command timeout */
328 uschar *buffer, /* for reading response */
329 int buffsize) /* size of buffer */
330 {
331 /* We don't support clients (yet) in this implementation of cyrus_sasl */
332 return FAIL;
333 }
334
335 #endif /* AUTH_CYRUS_SASL */
336
337 /* End of cyrus_sasl.c */