cyrus_sasl authenticator was not using the expanded hostname (even
[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 */