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