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