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 */