fea1def38392db142e64a48b94aab91c07a9c3db
[users/jgh/exim.git] / src / src / auths / cyrus_sasl.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2009 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 /* This code was originally contributed by Matthew Byng-Maddick */
9
10 /* Copyright (c) A L Digital 2004 */
11
12 /* A generic (mechanism independent) Cyrus SASL authenticator. */
13
14
15 #include "../exim.h"
16
17
18 /* We can't just compile this code and allow the library mechanism to omit the
19 functions if they are not wanted, because we need to have the Cyrus SASL header
20 available for compiling. Therefore, compile these functions only if
21 AUTH_CYRUS_SASL is defined. However, some compilers don't like compiling empty
22 modules, so keep them happy with a dummy when skipping the rest. Make it
23 reference itself to stop picky compilers complaining that it is unused, and put
24 in a dummy argument to stop even pickier compilers complaining about infinite
25 loops. */
26
27 #ifndef AUTH_CYRUS_SASL
28 static void dummy(int x) { dummy(x-1); }
29 #else
30
31
32 #include <sasl/sasl.h>
33 #include "cyrus_sasl.h"
34
35 /* Options specific to the cyrus_sasl authentication mechanism. */
36
37 optionlist auth_cyrus_sasl_options[] = {
38   { "server_hostname",      opt_stringptr,
39       (void *)(offsetof(auth_cyrus_sasl_options_block, server_hostname)) },
40   { "server_mech",          opt_stringptr,
41       (void *)(offsetof(auth_cyrus_sasl_options_block, server_mech)) },
42   { "server_realm",         opt_stringptr,
43       (void *)(offsetof(auth_cyrus_sasl_options_block, server_realm)) },
44   { "server_service",       opt_stringptr,
45       (void *)(offsetof(auth_cyrus_sasl_options_block, server_service)) }
46 };
47
48 /* Size of the options list. An extern variable has to be used so that its
49 address can appear in the tables drtables.c. */
50
51 int auth_cyrus_sasl_options_count =
52   sizeof(auth_cyrus_sasl_options)/sizeof(optionlist);
53
54 /* Default private options block for the cyrus_sasl authentication method. */
55
56 auth_cyrus_sasl_options_block auth_cyrus_sasl_option_defaults = {
57   US"smtp",         /* server_service */
58   US"$primary_hostname", /* server_hostname */
59   NULL,             /* server_realm */
60   NULL              /* server_mech */
61 };
62
63
64 /*************************************************
65 *          Initialization entry point            *
66 *************************************************/
67
68 /* Called for each instance, after its options have been read, to
69 enable consistency checks to be done, or anything else that needs
70 to be set up. */
71
72
73 /* Auxiliary function, passed in data to sasl_server_init(). */
74
75 static int
76 mysasl_config(void *context,
77               const char *plugin_name,
78               const char *option,
79               const char **result,
80               unsigned int *len)
81 {
82 if (context && !strcmp(option, "mech_list"))
83   {
84   *result = context;
85   if (len != NULL) *len = strlen(*result);
86   return SASL_OK;
87   }
88 return SASL_FAIL;
89 }
90
91 /* Here's the real function */
92
93 void
94 auth_cyrus_sasl_init(auth_instance *ablock)
95 {
96 auth_cyrus_sasl_options_block *ob =
97   (auth_cyrus_sasl_options_block *)(ablock->options_block);
98 uschar *list, *listptr, *buffer;
99 int rc, i;
100 unsigned int len;
101 uschar *rs_point;
102
103 sasl_conn_t *conn;
104 sasl_callback_t cbs[]={
105   {SASL_CB_GETOPT, NULL, NULL },
106   {SASL_CB_LIST_END, NULL, NULL}};
107
108 /* default the mechanism to our "public name" */
109 if(ob->server_mech == NULL)
110   ob->server_mech=string_copy(ablock->public_name);
111
112 /* we're going to initialise the library to check that there is an
113  * authenticator of type whatever mechanism we're using
114  */
115
116 cbs[0].proc = &mysasl_config;
117 cbs[0].context = ob->server_mech;
118
119 rc=sasl_server_init(cbs, "exim");
120
121 if( rc != SASL_OK )
122   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
123       "couldn't initialise Cyrus SASL library.", ablock->name);
124
125 rc=sasl_server_new(CS ob->server_service, CS primary_hostname,
126                    CS ob->server_realm, NULL, NULL, NULL, 0, &conn);
127 if( rc != SASL_OK )
128   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
129       "couldn't initialise Cyrus SASL server connection.", ablock->name);
130
131 rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
132 if( rc != SASL_OK )
133   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
134       "couldn't get Cyrus SASL mechanism list.", ablock->name);
135
136 i=':';
137 listptr=list;
138
139 HDEBUG(D_auth) debug_printf("Cyrus SASL knows about: %s\n", list);
140
141 /* the store_get / store_reset mechanism is hierarchical
142  * the hierarchy is stored for us behind our back. This point
143  * creates a hierarchy point for this function.
144  */
145 rs_point=store_get(0);
146
147 /* loop until either we get to the end of the list, or we match the
148  * public name of this authenticator
149  */
150 while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
151        strcmpic(buffer,ob->server_mech) );
152
153 if(!buffer)
154   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
155       "Cyrus SASL doesn't know about mechanism %s.", ablock->name, ob->server_mech);
156
157 store_reset(rs_point);
158
159 HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
160
161 /* make sure that if we get here then we're allowed to advertise. */
162 ablock->server = TRUE;
163
164 sasl_dispose(&conn);
165 sasl_done();
166 }
167
168 /*************************************************
169 *             Server entry point                 *
170 *************************************************/
171
172 /* For interface, see auths/README */
173
174 /* note, we don't care too much about memory allocation in this, because this is entirely
175  * within a shortlived child
176  */
177
178 int
179 auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
180 {
181 auth_cyrus_sasl_options_block *ob =
182   (auth_cyrus_sasl_options_block *)(ablock->options_block);
183 uschar *output, *out2, *input, *clear, *hname;
184 uschar *debug = NULL;   /* Stops compiler complaining */
185 sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
186 sasl_conn_t *conn;
187 int rc, firsttime=1, clen;
188 unsigned int inlen, outlen;
189
190 input=data;
191 inlen=Ustrlen(data);
192
193 HDEBUG(D_auth) debug=string_copy(data);
194
195 hname=expand_string(ob->server_hostname);
196 if(hname == NULL)
197   {
198   auth_defer_msg = expand_string_message;
199   return DEFER;
200   }
201
202 if(inlen)
203   {
204   clen=auth_b64decode(input, &clear);
205   if(clen < 0)
206     {
207     return BAD64;
208     }
209   input=clear;
210   inlen=clen;
211   }
212
213 rc=sasl_server_init(cbs, "exim");
214 if (rc != SASL_OK)
215   {
216   auth_defer_msg = US"couldn't initialise Cyrus SASL library";
217   return DEFER;
218   }
219
220 rc=sasl_server_new(CS ob->server_service, CS hname, CS ob->server_realm, NULL,
221   NULL, NULL, 0, &conn);
222
223 if( rc != SASL_OK )
224   {
225   auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
226   sasl_done();
227   return DEFER;
228   }
229
230 rc=SASL_CONTINUE;
231
232 while(rc==SASL_CONTINUE)
233   {
234   if(firsttime)
235     {
236     firsttime=0;
237     HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
238     rc=sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
239            (const char **)(&output), &outlen);
240     }
241   else
242     {
243     /* make sure that we have a null-terminated string */
244     out2=store_get(outlen+1);
245     memcpy(out2,output,outlen);
246     out2[outlen]='\0';
247     if((rc=auth_get_data(&input, out2, outlen))!=OK)
248       {
249       /* we couldn't get the data, so free up the library before
250        * returning whatever error we get */
251       sasl_dispose(&conn);
252       sasl_done();
253       return rc;
254       }
255     inlen=Ustrlen(input);
256
257     HDEBUG(D_auth) debug=string_copy(input);
258     if(inlen)
259       {
260       clen=auth_b64decode(input, &clear);
261       if(clen < 0)
262        {
263         sasl_dispose(&conn);
264         sasl_done();
265        return BAD64;
266        }
267       input=clear;
268       inlen=clen;
269       }
270
271     HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
272     rc=sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
273     }
274   if(rc==SASL_BADPROT)
275     {
276     sasl_dispose(&conn);
277     sasl_done();
278     return UNEXPECTED;
279     }
280   else if( rc==SASL_FAIL     || rc==SASL_BUFOVER
281        || rc==SASL_BADMAC   || rc==SASL_BADAUTH
282        || rc==SASL_NOAUTHZ  || rc==SASL_ENCRYPT
283        || rc==SASL_EXPIRED  || rc==SASL_DISABLED
284        || rc==SASL_NOUSER   )
285     {
286     /* these are considered permanent failure codes */
287     HDEBUG(D_auth)
288       debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
289     log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
290        "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
291        sasl_errstring(rc, NULL, NULL));
292     sasl_dispose(&conn);
293     sasl_done();
294     return FAIL;
295     }
296   else if(rc==SASL_NOMECH)
297     {
298     /* this is a temporary failure, because the mechanism is not
299      * available for this user. If it wasn't available at all, we
300      * shouldn't have got here in the first place...
301      */
302     HDEBUG(D_auth)
303       debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
304     auth_defer_msg =
305         string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
306     sasl_dispose(&conn);
307     sasl_done();
308     return DEFER;
309     }
310   else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
311     {
312     /* Anything else is a temporary failure, and we'll let SASL print out
313      * the error string for us
314      */
315     HDEBUG(D_auth)
316       debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
317     auth_defer_msg =
318         string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
319     sasl_dispose(&conn);
320     sasl_done();
321     return DEFER;
322     }
323   else if(rc==SASL_OK)
324     {
325     /* Get the username and copy it into $auth1 and $1. The former is now the
326     preferred variable; the latter is the original variable. */
327     rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
328     auth_vars[0] = expand_nstring[1] = string_copy(out2);
329     expand_nlength[1] = Ustrlen(expand_nstring[1]);
330     expand_nmax = 1;
331
332     HDEBUG(D_auth)
333       debug_printf("Cyrus SASL %s authentication succeeded for %s\n", ob->server_mech, out2);
334     /* close down the connection, freeing up library's memory */
335     sasl_dispose(&conn);
336     sasl_done();
337
338     /* Expand server_condition as an authorization check */
339     return auth_check_serv_cond(ablock);
340     }
341   }
342 /* NOTREACHED */
343 return 0;  /* Stop compiler complaints */
344 }
345
346 /*************************************************
347 *                Diagnostic API                  *
348 *************************************************/
349
350 void
351 auth_cyrus_sasl_version_report(FILE *f)
352 {
353   const char *implementation, *version;
354   sasl_version_info(&implementation, &version, NULL, NULL, NULL, NULL);
355   fprintf(f, "Library version: Cyrus SASL: Compile: %d.%d.%d\n"
356              "                             Runtime: %s [%s]\n",
357           SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
358           version, implementation);
359 }
360
361 /*************************************************
362 *              Client entry point                *
363 *************************************************/
364
365 /* For interface, see auths/README */
366
367 int
368 auth_cyrus_sasl_client(
369   auth_instance *ablock,                 /* authenticator block */
370   smtp_inblock *inblock,                 /* input connection */
371   smtp_outblock *outblock,               /* output connection */
372   int timeout,                           /* command timeout */
373   uschar *buffer,                          /* for reading response */
374   int buffsize)                          /* size of buffer */
375 {
376 /* We don't support clients (yet) in this implementation of cyrus_sasl */
377 return FAIL;
378 }
379
380 #endif  /* AUTH_CYRUS_SASL */
381
382 /* End of cyrus_sasl.c */