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