More compiler quietening.
[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 - 2012 */
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, *expanded_hostname;
102 char *realm_expanded;
103
104 sasl_conn_t *conn;
105 sasl_callback_t cbs[]={
106   {SASL_CB_GETOPT, NULL, NULL },
107   {SASL_CB_LIST_END, NULL, NULL}};
108
109 /* default the mechanism to our "public name" */
110 if(ob->server_mech == NULL)
111   ob->server_mech=string_copy(ablock->public_name);
112
113 expanded_hostname = expand_string(ob->server_hostname);
114 if (expanded_hostname == NULL)
115   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
116       "couldn't expand server_hostname [%s]: %s",
117       ablock->name, ob->server_hostname, expand_string_message);
118
119 realm_expanded=NULL;
120 if (ob->server_realm != NULL) {
121   realm_expanded = CS expand_string(ob->server_realm);
122   if (realm_expanded == NULL)
123     log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
124         "couldn't expand server_realm [%s]: %s",
125         ablock->name, ob->server_realm, expand_string_message);
126 }
127
128 /* we're going to initialise the library to check that there is an
129  * authenticator of type whatever mechanism we're using
130  */
131
132 cbs[0].proc = (int(*)(void)) &mysasl_config;
133 cbs[0].context = ob->server_mech;
134
135 rc=sasl_server_init(cbs, "exim");
136
137 if( rc != SASL_OK )
138   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
139       "couldn't initialise Cyrus SASL library.", ablock->name);
140
141 rc=sasl_server_new(CS ob->server_service, CS expanded_hostname,
142                    realm_expanded, NULL, NULL, NULL, 0, &conn);
143 if( rc != SASL_OK )
144   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
145       "couldn't initialise Cyrus SASL server connection.", ablock->name);
146
147 rc=sasl_listmech(conn, NULL, "", ":", "", (const char **)(&list), &len, &i);
148 if( rc != SASL_OK )
149   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
150       "couldn't get Cyrus SASL mechanism list.", ablock->name);
151
152 i=':';
153 listptr=list;
154
155 HDEBUG(D_auth) {
156   debug_printf("Initialised Cyrus SASL service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
157       ob->server_service, expanded_hostname, realm_expanded);
158   debug_printf("Cyrus SASL knows mechanisms: %s\n", list);
159 }
160
161 /* the store_get / store_reset mechanism is hierarchical
162  * the hierarchy is stored for us behind our back. This point
163  * creates a hierarchy point for this function.
164  */
165 rs_point=store_get(0);
166
167 /* loop until either we get to the end of the list, or we match the
168  * public name of this authenticator
169  */
170 while( ( buffer = string_nextinlist(&listptr, &i, NULL, 0) ) &&
171        strcmpic(buffer,ob->server_mech) );
172
173 if(!buffer)
174   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s authenticator:  "
175       "Cyrus SASL doesn't know about mechanism %s.", ablock->name, ob->server_mech);
176
177 store_reset(rs_point);
178
179 HDEBUG(D_auth) debug_printf("Cyrus SASL driver %s: %s initialised\n", ablock->name, ablock->public_name);
180
181 /* make sure that if we get here then we're allowed to advertise. */
182 ablock->server = TRUE;
183
184 sasl_dispose(&conn);
185 sasl_done();
186 }
187
188 /*************************************************
189 *             Server entry point                 *
190 *************************************************/
191
192 /* For interface, see auths/README */
193
194 /* note, we don't care too much about memory allocation in this, because this is entirely
195  * within a shortlived child
196  */
197
198 int
199 auth_cyrus_sasl_server(auth_instance *ablock, uschar *data)
200 {
201 auth_cyrus_sasl_options_block *ob =
202   (auth_cyrus_sasl_options_block *)(ablock->options_block);
203 uschar *output, *out2, *input, *clear, *hname;
204 uschar *debug = NULL;   /* Stops compiler complaining */
205 sasl_callback_t cbs[]={{SASL_CB_LIST_END, NULL, NULL}};
206 sasl_conn_t *conn;
207 char *realm_expanded;
208 int rc, i, firsttime=1, clen, *negotiated_ssf_ptr=NULL, negotiated_ssf;
209 unsigned int inlen, outlen;
210
211 input=data;
212 inlen=Ustrlen(data);
213
214 HDEBUG(D_auth) debug=string_copy(data);
215
216 hname=expand_string(ob->server_hostname);
217 realm_expanded=NULL;
218 if (hname && ob->server_realm)
219   realm_expanded= CS expand_string(ob->server_realm);
220 if((hname == NULL) ||
221    ((realm_expanded == NULL) && (ob->server_realm != NULL)))
222   {
223   auth_defer_msg = expand_string_message;
224   return DEFER;
225   }
226
227 if(inlen)
228   {
229   clen=auth_b64decode(input, &clear);
230   if(clen < 0)
231     {
232     return BAD64;
233     }
234   input=clear;
235   inlen=clen;
236   }
237
238 rc=sasl_server_init(cbs, "exim");
239 if (rc != SASL_OK)
240   {
241   auth_defer_msg = US"couldn't initialise Cyrus SASL library";
242   return DEFER;
243   }
244
245 rc=sasl_server_new(CS ob->server_service, CS hname, realm_expanded, NULL,
246   NULL, NULL, 0, &conn);
247
248 HDEBUG(D_auth)
249   debug_printf("Initialised Cyrus SASL server connection; service=\"%s\" fqdn=\"%s\" realm=\"%s\"\n",
250       ob->server_service, hname, realm_expanded);
251
252 if( rc != SASL_OK )
253   {
254   auth_defer_msg = US"couldn't initialise Cyrus SASL connection";
255   sasl_done();
256   return DEFER;
257   }
258
259 if (tls_in.cipher)
260   {
261   rc = sasl_setprop(conn, SASL_SSF_EXTERNAL, (sasl_ssf_t *) &tls_in.bits);
262   if (rc != SASL_OK)
263     {
264     HDEBUG(D_auth) debug_printf("Cyrus SASL EXTERNAL SSF set %d failed: %s\n",
265         tls_in.bits, sasl_errstring(rc, NULL, NULL));
266     auth_defer_msg = US"couldn't set Cyrus SASL EXTERNAL SSF";
267     sasl_done();
268     return DEFER;
269     }
270   else
271     HDEBUG(D_auth) debug_printf("Cyrus SASL set EXTERNAL SSF to %d\n", tls_in.bits);
272   }
273 else
274   HDEBUG(D_auth) debug_printf("Cyrus SASL: no TLS, no EXTERNAL SSF set\n");
275
276 /* So sasl_setprop() documents non-shorted IPv6 addresses which is incredibly
277 annoying; looking at cyrus-imapd-2.3.x source, the IP address is constructed
278 with their iptostring() function, which just wraps
279 getnameinfo(..., NI_NUMERICHOST|NI_NUMERICSERV), which is equivalent to the
280 inet_ntop which we wrap in our host_ntoa() function.
281
282 So the docs are too strict and we shouldn't worry about :: contractions. */
283
284 /* Set properties for remote and local host-ip;port */
285 for (i=0; i < 2; ++i)
286   {
287   struct sockaddr_storage ss;
288   int (*query)(int, struct sockaddr *, socklen_t *);
289   int propnum, port;
290   const uschar *label;
291   uschar *address, *address_port;
292   const char *s_err;
293   socklen_t sslen;
294
295   if (i)
296     {
297     query = &getpeername;
298     propnum = SASL_IPREMOTEPORT;
299     label = CUS"peer";
300     }
301   else
302     {
303     query = &getsockname;
304     propnum = SASL_IPLOCALPORT;
305     label = CUS"local";
306     }
307
308   sslen = sizeof(ss);
309   rc = query(fileno(smtp_in), (struct sockaddr *) &ss, &sslen);
310   if (rc < 0)
311     {
312     HDEBUG(D_auth)
313       debug_printf("Failed to get %s address information: %s\n",
314           label, strerror(errno));
315     break;
316     }
317
318   address = host_ntoa(-1, &ss, NULL, &port);
319   address_port = string_sprintf("%s;%d", address, port);
320
321   rc = sasl_setprop(conn, propnum, address_port);
322   if (rc != SASL_OK)
323     {
324     s_err = sasl_errdetail(conn);
325     HDEBUG(D_auth)
326       debug_printf("Failed to set %s SASL property: [%d] %s\n",
327           label, rc, s_err ? s_err : "<unknown reason>");
328     break;
329     }
330   HDEBUG(D_auth) debug_printf("Cyrus SASL set %s hostport to: %s\n",
331       label, address_port);
332   }
333
334 rc=SASL_CONTINUE;
335
336 while(rc==SASL_CONTINUE)
337   {
338   if(firsttime)
339     {
340     firsttime=0;
341     HDEBUG(D_auth) debug_printf("Calling sasl_server_start(%s,\"%s\")\n", ob->server_mech, debug);
342     rc=sasl_server_start(conn, CS ob->server_mech, inlen?CS input:NULL, inlen,
343            (const char **)(&output), &outlen);
344     }
345   else
346     {
347     /* make sure that we have a null-terminated string */
348     out2=store_get(outlen+1);
349     memcpy(out2,output,outlen);
350     out2[outlen]='\0';
351     if((rc=auth_get_data(&input, out2, outlen))!=OK)
352       {
353       /* we couldn't get the data, so free up the library before
354        * returning whatever error we get */
355       sasl_dispose(&conn);
356       sasl_done();
357       return rc;
358       }
359     inlen=Ustrlen(input);
360
361     HDEBUG(D_auth) debug=string_copy(input);
362     if(inlen)
363       {
364       clen=auth_b64decode(input, &clear);
365       if(clen < 0)
366        {
367         sasl_dispose(&conn);
368         sasl_done();
369        return BAD64;
370        }
371       input=clear;
372       inlen=clen;
373       }
374
375     HDEBUG(D_auth) debug_printf("Calling sasl_server_step(\"%s\")\n", debug);
376     rc=sasl_server_step(conn, CS input, inlen, (const char **)(&output), &outlen);
377     }
378   if(rc==SASL_BADPROT)
379     {
380     sasl_dispose(&conn);
381     sasl_done();
382     return UNEXPECTED;
383     }
384   else if( rc==SASL_FAIL     || rc==SASL_BUFOVER
385        || rc==SASL_BADMAC   || rc==SASL_BADAUTH
386        || rc==SASL_NOAUTHZ  || rc==SASL_ENCRYPT
387        || rc==SASL_EXPIRED  || rc==SASL_DISABLED
388        || rc==SASL_NOUSER   )
389     {
390     /* these are considered permanent failure codes */
391     HDEBUG(D_auth)
392       debug_printf("Cyrus SASL permanent failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
393     log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
394        "Cyrus SASL permanent failure: %s", ablock->name, ob->server_mech,
395        sasl_errstring(rc, NULL, NULL));
396     sasl_dispose(&conn);
397     sasl_done();
398     return FAIL;
399     }
400   else if(rc==SASL_NOMECH)
401     {
402     /* this is a temporary failure, because the mechanism is not
403      * available for this user. If it wasn't available at all, we
404      * shouldn't have got here in the first place...
405      */
406     HDEBUG(D_auth)
407       debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
408     auth_defer_msg =
409         string_sprintf("Cyrus SASL: mechanism %s not available", ob->server_mech);
410     sasl_dispose(&conn);
411     sasl_done();
412     return DEFER;
413     }
414   else if(!(rc==SASL_OK || rc==SASL_CONTINUE))
415     {
416     /* Anything else is a temporary failure, and we'll let SASL print out
417      * the error string for us
418      */
419     HDEBUG(D_auth)
420       debug_printf("Cyrus SASL temporary failure %d (%s)\n", rc, sasl_errstring(rc, NULL, NULL));
421     auth_defer_msg =
422         string_sprintf("Cyrus SASL: %s", sasl_errstring(rc, NULL, NULL));
423     sasl_dispose(&conn);
424     sasl_done();
425     return DEFER;
426     }
427   else if(rc==SASL_OK)
428     {
429     /* Get the username and copy it into $auth1 and $1. The former is now the
430     preferred variable; the latter is the original variable. */
431     rc = sasl_getprop(conn, SASL_USERNAME, (const void **)(&out2));
432     if (rc != SASL_OK)
433       {
434       HDEBUG(D_auth)
435         debug_printf("Cyrus SASL library will not tell us the username: %s\n",
436             sasl_errstring(rc, NULL, NULL));
437       log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
438          "Cyrus SASL username fetch problem: %s", ablock->name, ob->server_mech,
439          sasl_errstring(rc, NULL, NULL));
440       sasl_dispose(&conn);
441       sasl_done();
442       return FAIL;
443       }
444
445     auth_vars[0] = expand_nstring[1] = string_copy(out2);
446     expand_nlength[1] = Ustrlen(expand_nstring[1]);
447     expand_nmax = 1;
448
449     HDEBUG(D_auth)
450       debug_printf("Cyrus SASL %s authentication succeeded for %s\n",
451           ob->server_mech, auth_vars[0]);
452
453     rc = sasl_getprop(conn, SASL_SSF, (const void **)(&negotiated_ssf_ptr));
454     if (rc != SASL_OK)
455       {
456       HDEBUG(D_auth)
457         debug_printf("Cyrus SASL library will not tell us the SSF: %s\n",
458             sasl_errstring(rc, NULL, NULL));
459       log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
460           "Cyrus SASL SSF value not available: %s", ablock->name, ob->server_mech,
461           sasl_errstring(rc, NULL, NULL));
462       sasl_dispose(&conn);
463       sasl_done();
464       return FAIL;
465       }
466     negotiated_ssf = *negotiated_ssf_ptr;
467     HDEBUG(D_auth)
468       debug_printf("Cyrus SASL %s negotiated SSF: %d\n", ob->server_mech, negotiated_ssf);
469     if (negotiated_ssf > 0)
470       {
471       HDEBUG(D_auth)
472         debug_printf("Exim does not implement SASL wrapping (needed for SSF %d).\n", negotiated_ssf);
473       log_write(0, LOG_REJECT, "%s authenticator (%s):\n  "
474           "Cyrus SASL SSF %d not supported by Exim", ablock->name, ob->server_mech, negotiated_ssf);
475       sasl_dispose(&conn);
476       sasl_done();
477       return FAIL;
478       }
479
480     /* close down the connection, freeing up library's memory */
481     sasl_dispose(&conn);
482     sasl_done();
483
484     /* Expand server_condition as an authorization check */
485     return auth_check_serv_cond(ablock);
486     }
487   }
488 /* NOTREACHED */
489 return 0;  /* Stop compiler complaints */
490 }
491
492 /*************************************************
493 *                Diagnostic API                  *
494 *************************************************/
495
496 void
497 auth_cyrus_sasl_version_report(FILE *f)
498 {
499   const char *implementation, *version;
500   sasl_version_info(&implementation, &version, NULL, NULL, NULL, NULL);
501   fprintf(f, "Library version: Cyrus SASL: Compile: %d.%d.%d\n"
502              "                             Runtime: %s [%s]\n",
503           SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
504           version, implementation);
505 }
506
507 /*************************************************
508 *              Client entry point                *
509 *************************************************/
510
511 /* For interface, see auths/README */
512
513 int
514 auth_cyrus_sasl_client(
515   auth_instance *ablock,                 /* authenticator block */
516   smtp_inblock *inblock,                 /* input connection */
517   smtp_outblock *outblock,               /* output connection */
518   int timeout,                           /* command timeout */
519   uschar *buffer,                          /* for reading response */
520   int buffsize)                          /* size of buffer */
521 {
522 /* We don't support clients (yet) in this implementation of cyrus_sasl */
523 return FAIL;
524 }
525
526 #endif  /* AUTH_CYRUS_SASL */
527
528 /* End of cyrus_sasl.c */