xref: /netbsd-src/external/ibm-public/postfix/dist/src/xsasl/xsasl_dovecot_server.c (revision aceb213538ec08a74028e213127af18aa17bf1cf)
1 /*	$NetBSD: xsasl_dovecot_server.c,v 1.1.1.4 2012/02/17 08:36:25 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	xsasl_dovecot_server 3
6 /* SUMMARY
7 /*	Dovecot SASL server-side plug-in
8 /* SYNOPSIS
9 /*	XSASL_SERVER_IMPL *xsasl_dovecot_server_init(server_type, appl_name)
10 /*	const char *server_type;
11 /*	const char *appl_name;
12 /* DESCRIPTION
13 /*	This module implements the Dovecot SASL server-side authentication
14 /*	plug-in.
15 /*
16 /* .IP server_type
17 /*	The plug-in type that was specified to xsasl_server_init().
18 /*	The argument is ignored, because the Dovecot plug-in
19 /*	implements only one plug-in type.
20 /* .IP path_info
21 /*	The location of the Dovecot authentication server's UNIX-domain
22 /*	socket. Note: the Dovecot plug-in uses late binding, therefore
23 /*	all connect operations are done with Postfix privileges.
24 /* DIAGNOSTICS
25 /*	Fatal: out of memory.
26 /*
27 /*	Panic: interface violation.
28 /*
29 /*	Other: the routines log a warning and return an error result
30 /*	as specified in xsasl_server(3).
31 /* LICENSE
32 /* .ad
33 /* .fi
34 /*	The Secure Mailer license must be distributed with this software.
35 /* AUTHOR(S)
36 /*	Initial implementation by:
37 /*	Timo Sirainen
38 /*	Procontrol
39 /*	Finland
40 /*
41 /*	Adopted by:
42 /*	Wietse Venema
43 /*	IBM T.J. Watson Research
44 /*	P.O. Box 704
45 /*	Yorktown Heights, NY 10598, USA
46 /*--*/
47 
48 /* System library. */
49 
50 #include <sys_defs.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 
55 #ifdef STRCASECMP_IN_STRINGS_H
56 #include <strings.h>
57 #endif
58 
59 /* Utility library. */
60 
61 #include <msg.h>
62 #include <mymalloc.h>
63 #include <connect.h>
64 #include <split_at.h>
65 #include <stringops.h>
66 #include <vstream.h>
67 #include <vstring_vstream.h>
68 #include <name_mask.h>
69 #include <argv.h>
70 #include <myaddrinfo.h>
71 
72 /* Global library. */
73 
74 #include <mail_params.h>
75 
76 /* Application-specific. */
77 
78 #include <xsasl.h>
79 #include <xsasl_dovecot.h>
80 
81 #ifdef USE_SASL_AUTH
82 
83 /* Major version changes are not backwards compatible,
84    minor version numbers can be ignored. */
85 #define AUTH_PROTOCOL_MAJOR_VERSION 1
86 #define AUTH_PROTOCOL_MINOR_VERSION 0
87 
88  /*
89   * Encorce read/write time limits, so that we can produce accurate
90   * diagnostics instead of getting killed by the watchdog timer.
91   */
92 #define AUTH_TIMEOUT	10
93 
94  /*
95   * Security property bitmasks.
96   */
97 #define SEC_PROPS_NOPLAINTEXT	(1 << 0)
98 #define SEC_PROPS_NOACTIVE	(1 << 1)
99 #define SEC_PROPS_NODICTIONARY	(1 << 2)
100 #define SEC_PROPS_NOANONYMOUS	(1 << 3)
101 #define SEC_PROPS_FWD_SECRECY	(1 << 4)
102 #define SEC_PROPS_MUTUAL_AUTH	(1 << 5)
103 #define SEC_PROPS_PRIVATE	(1 << 6)
104 
105 #define SEC_PROPS_POS_MASK	(SEC_PROPS_MUTUAL_AUTH | SEC_PROPS_FWD_SECRECY)
106 #define SEC_PROPS_NEG_MASK	(SEC_PROPS_NOPLAINTEXT | SEC_PROPS_NOACTIVE | \
107 				SEC_PROPS_NODICTIONARY | SEC_PROPS_NOANONYMOUS)
108 
109  /*
110   * Security properties as specified in the Postfix main.cf file.
111   */
112 static const NAME_MASK xsasl_dovecot_conf_sec_props[] = {
113     "noplaintext", SEC_PROPS_NOPLAINTEXT,
114     "noactive", SEC_PROPS_NOACTIVE,
115     "nodictionary", SEC_PROPS_NODICTIONARY,
116     "noanonymous", SEC_PROPS_NOANONYMOUS,
117     "forward_secrecy", SEC_PROPS_FWD_SECRECY,
118     "mutual_auth", SEC_PROPS_MUTUAL_AUTH,
119     0, 0,
120 };
121 
122  /*
123   * Security properties as specified in the Dovecot protocol. See
124   * http://wiki.dovecot.org/Authentication_Protocol.
125   */
126 static const NAME_MASK xsasl_dovecot_serv_sec_props[] = {
127     "plaintext", SEC_PROPS_NOPLAINTEXT,
128     "active", SEC_PROPS_NOACTIVE,
129     "dictionary", SEC_PROPS_NODICTIONARY,
130     "anonymous", SEC_PROPS_NOANONYMOUS,
131     "forward-secrecy", SEC_PROPS_FWD_SECRECY,
132     "mutual-auth", SEC_PROPS_MUTUAL_AUTH,
133     "private", SEC_PROPS_PRIVATE,
134     0, 0,
135 };
136 
137  /*
138   * Class variables.
139   */
140 typedef struct XSASL_DCSRV_MECH {
141     char   *mech_name;			/* mechanism name */
142     int     sec_props;			/* mechanism properties */
143     struct XSASL_DCSRV_MECH *next;
144 } XSASL_DCSRV_MECH;
145 
146 typedef struct {
147     XSASL_SERVER_IMPL xsasl;
148     VSTREAM *sasl_stream;
149     char   *socket_path;
150     XSASL_DCSRV_MECH *mechanism_list;	/* unfiltered mechanism list */
151     unsigned int request_id_counter;
152 } XSASL_DOVECOT_SERVER_IMPL;
153 
154  /*
155   * The XSASL_DOVECOT_SERVER object is derived from the generic XSASL_SERVER
156   * object.
157   */
158 typedef struct {
159     XSASL_SERVER xsasl;			/* generic members, must be first */
160     XSASL_DOVECOT_SERVER_IMPL *impl;
161     unsigned int last_request_id;
162     char   *service;
163     char   *username;			/* authenticated user */
164     VSTRING *sasl_line;
165     unsigned int sec_props;		/* Postfix mechanism filter */
166     int     tls_flag;			/* TLS enabled in this session */
167     char   *mechanism_list;		/* filtered mechanism list */
168     ARGV   *mechanism_argv;		/* ditto */
169     char   *client_addr;		/* remote IP address */
170     char   *server_addr;		/* remote IP address */
171 } XSASL_DOVECOT_SERVER;
172 
173  /*
174   * Forward declarations.
175   */
176 static void xsasl_dovecot_server_done(XSASL_SERVER_IMPL *);
177 static XSASL_SERVER *xsasl_dovecot_server_create(XSASL_SERVER_IMPL *,
178 					        XSASL_SERVER_CREATE_ARGS *);
179 static void xsasl_dovecot_server_free(XSASL_SERVER *);
180 static int xsasl_dovecot_server_first(XSASL_SERVER *, const char *,
181 				              const char *, VSTRING *);
182 static int xsasl_dovecot_server_next(XSASL_SERVER *, const char *, VSTRING *);
183 static const char *xsasl_dovecot_server_get_mechanism_list(XSASL_SERVER *);
184 static const char *xsasl_dovecot_server_get_username(XSASL_SERVER *);
185 
186 /* xsasl_dovecot_server_mech_append - append server mechanism entry */
187 
188 static void xsasl_dovecot_server_mech_append(XSASL_DCSRV_MECH **mech_list,
189 			               const char *mech_name, int sec_props)
190 {
191     XSASL_DCSRV_MECH **mpp;
192     XSASL_DCSRV_MECH *mp;
193 
194     for (mpp = mech_list; *mpp != 0; mpp = &mpp[0]->next)
195 	 /* void */ ;
196 
197     mp = (XSASL_DCSRV_MECH *) mymalloc(sizeof(*mp));
198     mp->mech_name = mystrdup(mech_name);
199     mp->sec_props = sec_props;
200     mp->next = 0;
201     *mpp = mp;
202 }
203 
204 /* xsasl_dovecot_server_mech_free - destroy server mechanism list */
205 
206 static void xsasl_dovecot_server_mech_free(XSASL_DCSRV_MECH *mech_list)
207 {
208     XSASL_DCSRV_MECH *mp;
209     XSASL_DCSRV_MECH *next;
210 
211     for (mp = mech_list; mp != 0; mp = next) {
212 	myfree(mp->mech_name);
213 	next = mp->next;
214 	myfree((char *) mp);
215     }
216 }
217 
218 /* xsasl_dovecot_server_mech_filter - filter server mechanism list */
219 
220 static char *xsasl_dovecot_server_mech_filter(ARGV *mechanism_argv,
221 				           XSASL_DCSRV_MECH *mechanism_list,
222 					            unsigned int conf_props)
223 {
224     const char *myname = "xsasl_dovecot_server_mech_filter";
225     unsigned int pos_conf_props = (conf_props & SEC_PROPS_POS_MASK);
226     unsigned int neg_conf_props = (conf_props & SEC_PROPS_NEG_MASK);
227     VSTRING *mechanisms_str = vstring_alloc(10);
228     XSASL_DCSRV_MECH *mp;
229 
230     /*
231      * Match Postfix properties against Dovecot server properties.
232      */
233     for (mp = mechanism_list; mp != 0; mp = mp->next) {
234 	if ((mp->sec_props & pos_conf_props) == pos_conf_props
235 	    && (mp->sec_props & neg_conf_props) == 0) {
236 	    if (VSTRING_LEN(mechanisms_str) > 0)
237 		VSTRING_ADDCH(mechanisms_str, ' ');
238 	    vstring_strcat(mechanisms_str, mp->mech_name);
239 	    argv_add(mechanism_argv, mp->mech_name, (char *) 0);
240 	    if (msg_verbose)
241 		msg_info("%s: keep mechanism: %s", myname, mp->mech_name);
242 	} else {
243 	    if (msg_verbose)
244 		msg_info("%s: skip mechanism: %s", myname, mp->mech_name);
245 	}
246     }
247     return (vstring_export(mechanisms_str));
248 }
249 
250 /* xsasl_dovecot_server_connect - initial auth server handshake */
251 
252 static int xsasl_dovecot_server_connect(XSASL_DOVECOT_SERVER_IMPL *xp)
253 {
254     const char *myname = "xsasl_dovecot_server_connect";
255     VSTRING *line_str;
256     VSTREAM *sasl_stream;
257     char   *line, *cmd, *mech_name;
258     unsigned int major_version, minor_version;
259     int     fd, success;
260     int     sec_props;
261     const char *path;
262 
263     if (msg_verbose)
264 	msg_info("%s: Connecting", myname);
265 
266     /*
267      * Not documented, but necessary for testing.
268      */
269     path = xp->socket_path;
270     if (strncmp(path, "inet:", 5) == 0) {
271 	fd = inet_connect(path + 5, BLOCKING, AUTH_TIMEOUT);
272     } else {
273 	if (strncmp(path, "unix:", 5) == 0)
274 	    path += 5;
275 	fd = unix_connect(path, BLOCKING, AUTH_TIMEOUT);
276     }
277     if (fd < 0) {
278 	msg_warn("SASL: Connect to %s failed: %m", xp->socket_path);
279 	return (-1);
280     }
281     sasl_stream = vstream_fdopen(fd, O_RDWR);
282     vstream_control(sasl_stream,
283 		    VSTREAM_CTL_PATH, xp->socket_path,
284 		    VSTREAM_CTL_TIMEOUT, AUTH_TIMEOUT,
285 		    VSTREAM_CTL_END);
286 
287     /* XXX Encapsulate for logging. */
288     vstream_fprintf(sasl_stream,
289 		    "VERSION\t%u\t%u\n"
290 		    "CPID\t%u\n",
291 		    AUTH_PROTOCOL_MAJOR_VERSION,
292 		    AUTH_PROTOCOL_MINOR_VERSION,
293 		    (unsigned int) getpid());
294     if (vstream_fflush(sasl_stream) == VSTREAM_EOF) {
295 	msg_warn("SASL: Couldn't send handshake: %m");
296 	return (-1);
297     }
298     success = 0;
299     line_str = vstring_alloc(256);
300     /* XXX Encapsulate for logging. */
301     while (vstring_get_nonl(line_str, sasl_stream) != VSTREAM_EOF) {
302 	line = vstring_str(line_str);
303 
304 	if (msg_verbose)
305 	    msg_info("%s: auth reply: %s", myname, line);
306 
307 	cmd = line;
308 	line = split_at(line, '\t');
309 
310 	if (strcmp(cmd, "VERSION") == 0) {
311 	    if (sscanf(line, "%u\t%u", &major_version, &minor_version) != 2) {
312 		msg_warn("SASL: Protocol version error");
313 		break;
314 	    }
315 	    if (major_version != AUTH_PROTOCOL_MAJOR_VERSION) {
316 		/* Major version is different from ours. */
317 		msg_warn("SASL: Protocol version mismatch (%d vs. %d)",
318 			 major_version, AUTH_PROTOCOL_MAJOR_VERSION);
319 		break;
320 	    }
321 	} else if (strcmp(cmd, "MECH") == 0 && line != NULL) {
322 	    mech_name = line;
323 	    line = split_at(line, '\t');
324 	    if (line != 0) {
325 		sec_props =
326 		    name_mask_delim_opt(myname,
327 					xsasl_dovecot_serv_sec_props,
328 					line, "\t",
329 				     NAME_MASK_ANY_CASE | NAME_MASK_IGNORE);
330 		if ((sec_props & SEC_PROPS_PRIVATE) != 0)
331 		    continue;
332 	    } else
333 		sec_props = 0;
334 	    xsasl_dovecot_server_mech_append(&xp->mechanism_list, mech_name,
335 					     sec_props);
336 	} else if (strcmp(cmd, "DONE") == 0) {
337 	    /* Handshake finished. */
338 	    success = 1;
339 	    break;
340 	} else {
341 	    /* ignore any unknown commands */
342 	}
343     }
344     vstring_free(line_str);
345 
346     if (!success) {
347 	/* handshake failed */
348 	(void) vstream_fclose(sasl_stream);
349 	return (-1);
350     }
351     xp->sasl_stream = sasl_stream;
352     return (0);
353 }
354 
355 /* xsasl_dovecot_server_disconnect - dispose of server connection state */
356 
357 static void xsasl_dovecot_server_disconnect(XSASL_DOVECOT_SERVER_IMPL *xp)
358 {
359     if (xp->sasl_stream) {
360 	(void) vstream_fclose(xp->sasl_stream);
361 	xp->sasl_stream = 0;
362     }
363     if (xp->mechanism_list) {
364 	xsasl_dovecot_server_mech_free(xp->mechanism_list);
365 	xp->mechanism_list = 0;
366     }
367 }
368 
369 /* xsasl_dovecot_server_init - create implementation handle */
370 
371 XSASL_SERVER_IMPL *xsasl_dovecot_server_init(const char *server_type,
372 					             const char *path_info)
373 {
374     XSASL_DOVECOT_SERVER_IMPL *xp;
375 
376     xp = (XSASL_DOVECOT_SERVER_IMPL *) mymalloc(sizeof(*xp));
377     xp->xsasl.create = xsasl_dovecot_server_create;
378     xp->xsasl.done = xsasl_dovecot_server_done;
379     xp->socket_path = mystrdup(path_info);
380     xp->sasl_stream = 0;
381     xp->mechanism_list = 0;
382     xp->request_id_counter = 0;
383     return (&xp->xsasl);
384 }
385 
386 /* xsasl_dovecot_server_done - dispose of implementation */
387 
388 static void xsasl_dovecot_server_done(XSASL_SERVER_IMPL *impl)
389 {
390     XSASL_DOVECOT_SERVER_IMPL *xp = (XSASL_DOVECOT_SERVER_IMPL *) impl;
391 
392     xsasl_dovecot_server_disconnect(xp);
393     myfree(xp->socket_path);
394     myfree((char *) impl);
395 }
396 
397 /* xsasl_dovecot_server_create - create server instance */
398 
399 static XSASL_SERVER *xsasl_dovecot_server_create(XSASL_SERVER_IMPL *impl,
400 				             XSASL_SERVER_CREATE_ARGS *args)
401 {
402     const char *myname = "xsasl_dovecot_server_create";
403     XSASL_DOVECOT_SERVER *server;
404     struct sockaddr_storage ss;
405     struct sockaddr *sa = (struct sockaddr *) & ss;
406     SOCKADDR_SIZE salen;
407     MAI_HOSTADDR_STR server_addr;
408 
409     if (msg_verbose)
410 	msg_info("%s: SASL service=%s, realm=%s",
411 		 myname, args->service, args->user_realm ?
412 		 args->user_realm : "(null)");
413 
414     /*
415      * Extend the XSASL_SERVER_IMPL object with our own data. We use
416      * long-lived conversion buffers rather than local variables to avoid
417      * memory leaks in case of read/write timeout or I/O error.
418      */
419     server = (XSASL_DOVECOT_SERVER *) mymalloc(sizeof(*server));
420     server->xsasl.free = xsasl_dovecot_server_free;
421     server->xsasl.first = xsasl_dovecot_server_first;
422     server->xsasl.next = xsasl_dovecot_server_next;
423     server->xsasl.get_mechanism_list = xsasl_dovecot_server_get_mechanism_list;
424     server->xsasl.get_username = xsasl_dovecot_server_get_username;
425     server->impl = (XSASL_DOVECOT_SERVER_IMPL *) impl;
426     server->sasl_line = vstring_alloc(256);
427     server->username = 0;
428     server->service = mystrdup(args->service);
429     server->last_request_id = 0;
430     server->mechanism_list = 0;
431     server->mechanism_argv = 0;
432     server->tls_flag = args->tls_flag;
433     server->sec_props =
434 	name_mask_opt(myname, xsasl_dovecot_conf_sec_props,
435 		      args->security_options,
436 		      NAME_MASK_ANY_CASE | NAME_MASK_FATAL);
437     server->client_addr = mystrdup(args->client_addr);
438 
439     /*
440      * XXX Temporary code until smtpd_peer.c is updated.
441      */
442     if (args->server_addr && *args->server_addr) {
443 	server->server_addr = mystrdup(args->server_addr);
444     } else {
445 	salen = sizeof(ss);
446 	if (getsockname(vstream_fileno(args->stream), sa, &salen) < 0
447 	    || sockaddr_to_hostaddr(sa, salen, &server_addr, 0, 0) != 0)
448 	    server_addr.buf[0] = 0;
449 	server->server_addr = mystrdup(server_addr.buf);
450     }
451 
452     return (&server->xsasl);
453 }
454 
455 /* xsasl_dovecot_server_get_mechanism_list - get available mechanisms */
456 
457 static const char *xsasl_dovecot_server_get_mechanism_list(XSASL_SERVER *xp)
458 {
459     XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
460 
461     if (!server->impl->sasl_stream) {
462 	if (xsasl_dovecot_server_connect(server->impl) < 0)
463 	    return (0);
464     }
465     if (server->mechanism_list == 0) {
466 	server->mechanism_argv = argv_alloc(2);
467 	server->mechanism_list =
468 	    xsasl_dovecot_server_mech_filter(server->mechanism_argv,
469 					     server->impl->mechanism_list,
470 					     server->sec_props);
471     }
472     return (server->mechanism_list[0] ? server->mechanism_list : 0);
473 }
474 
475 /* xsasl_dovecot_server_free - destroy server instance */
476 
477 static void xsasl_dovecot_server_free(XSASL_SERVER *xp)
478 {
479     XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
480 
481     vstring_free(server->sasl_line);
482     if (server->username)
483 	myfree(server->username);
484     if (server->mechanism_list) {
485 	myfree(server->mechanism_list);
486 	argv_free(server->mechanism_argv);
487     }
488     myfree(server->service);
489     myfree(server->server_addr);
490     myfree(server->client_addr);
491     myfree((char *) server);
492 }
493 
494 /* xsasl_dovecot_server_auth_response - encode server first/next response */
495 
496 static int xsasl_dovecot_parse_reply(XSASL_DOVECOT_SERVER *server, char **line)
497 {
498     char   *id;
499 
500     if (*line == NULL) {
501 	msg_warn("SASL: Protocol error");
502 	return -1;
503     }
504     id = *line;
505     *line = split_at(*line, '\t');
506 
507     if (strtoul(id, NULL, 0) != server->last_request_id) {
508 	/* reply to another request, shouldn't really happen.. */
509 	return -1;
510     }
511     return 0;
512 }
513 
514 static void xsasl_dovecot_parse_reply_args(XSASL_DOVECOT_SERVER *server,
515 					         char *line, VSTRING *reply,
516 					           int success)
517 {
518     char   *next;
519 
520     if (server->username) {
521 	myfree(server->username);
522 	server->username = 0;
523     }
524 
525     /*
526      * Note: TAB is part of the Dovecot protocol and must not appear in
527      * legitimate Dovecot usernames, otherwise the protocol would break.
528      */
529     for (; line != NULL; line = next) {
530 	next = split_at(line, '\t');
531 	if (strncmp(line, "user=", 5) == 0) {
532 	    server->username = mystrdup(line + 5);
533 	    printable(server->username, '?');
534 	} else if (strncmp(line, "reason=", 7) == 0) {
535 	    if (!success) {
536 		printable(line + 7, '?');
537 		vstring_strcpy(reply, line + 7);
538 	    }
539 	}
540     }
541 }
542 
543 /* xsasl_dovecot_handle_reply - receive and process auth reply */
544 
545 static int xsasl_dovecot_handle_reply(XSASL_DOVECOT_SERVER *server,
546 				              VSTRING *reply)
547 {
548     const char *myname = "xsasl_dovecot_handle_reply";
549     char   *line, *cmd;
550 
551     /* XXX Encapsulate for logging. */
552     while (vstring_get_nonl(server->sasl_line,
553 			    server->impl->sasl_stream) != VSTREAM_EOF) {
554 	line = vstring_str(server->sasl_line);
555 
556 	if (msg_verbose)
557 	    msg_info("%s: auth reply: %s", myname, line);
558 
559 	cmd = line;
560 	line = split_at(line, '\t');
561 
562 	if (strcmp(cmd, "OK") == 0) {
563 	    if (xsasl_dovecot_parse_reply(server, &line) == 0) {
564 		/* authentication successful */
565 		xsasl_dovecot_parse_reply_args(server, line, reply, 1);
566 		return XSASL_AUTH_DONE;
567 	    }
568 	} else if (strcmp(cmd, "CONT") == 0) {
569 	    if (xsasl_dovecot_parse_reply(server, &line) == 0) {
570 		vstring_strcpy(reply, line);
571 		return XSASL_AUTH_MORE;
572 	    }
573 	} else if (strcmp(cmd, "FAIL") == 0) {
574 	    if (xsasl_dovecot_parse_reply(server, &line) == 0) {
575 		/* authentication failure */
576 		xsasl_dovecot_parse_reply_args(server, line, reply, 0);
577 		return XSASL_AUTH_FAIL;
578 	    }
579 	} else {
580 	    /* ignore */
581 	}
582     }
583 
584     vstring_strcpy(reply, "Connection lost to authentication server");
585     return XSASL_AUTH_FAIL;
586 }
587 
588 /* is_valid_base64 - input sanitized */
589 
590 static int is_valid_base64(const char *data)
591 {
592 
593     /*
594      * XXX Maybe use ISALNUM() (isascii && isalnum, i.e. locale independent).
595      */
596     for (; *data != '\0'; data++) {
597 	if (!((*data >= '0' && *data <= '9') ||
598 	      (*data >= 'a' && *data <= 'z') ||
599 	      (*data >= 'A' && *data <= 'Z') ||
600 	      *data == '+' || *data == '/' || *data == '='))
601 	    return 0;
602     }
603     return 1;
604 }
605 
606 /* xsasl_dovecot_server_first - per-session authentication */
607 
608 int     xsasl_dovecot_server_first(XSASL_SERVER *xp, const char *sasl_method,
609 			          const char *init_response, VSTRING *reply)
610 {
611     const char *myname = "xsasl_dovecot_server_first";
612     XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
613     int     i;
614     char  **cpp;
615 
616 #define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))
617 
618     if (msg_verbose)
619 	msg_info("%s: sasl_method %s%s%s", myname, sasl_method,
620 		 IFELSE(init_response, ", init_response ", ""),
621 		 IFELSE(init_response, init_response, ""));
622 
623     if (server->mechanism_argv == 0)
624 	msg_panic("%s: no mechanism list", myname);
625 
626     for (cpp = server->mechanism_argv->argv; /* see below */ ; cpp++) {
627 	if (*cpp == 0) {
628 	    vstring_strcpy(reply, "Invalid authentication mechanism");
629 	    return XSASL_AUTH_FAIL;
630 	}
631 	if (strcasecmp(sasl_method, *cpp) == 0)
632 	    break;
633     }
634     if (init_response)
635 	if (!is_valid_base64(init_response)) {
636 	    vstring_strcpy(reply, "Invalid base64 data in initial response");
637 	    return XSASL_AUTH_FAIL;
638 	}
639     for (i = 0; i < 2; i++) {
640 	if (!server->impl->sasl_stream) {
641 	    if (xsasl_dovecot_server_connect(server->impl) < 0)
642 		return (0);
643 	}
644 	/* send the request */
645 	server->last_request_id = ++server->impl->request_id_counter;
646 	/* XXX Encapsulate for logging. */
647 	vstream_fprintf(server->impl->sasl_stream,
648 			"AUTH\t%u\t%s\tservice=%s\tnologin\tlip=%s\trip=%s",
649 			server->last_request_id, sasl_method,
650 			server->service, server->server_addr,
651 			server->client_addr);
652 	if (server->tls_flag)
653 	    /* XXX Encapsulate for logging. */
654 	    vstream_fputs("\tsecured", server->impl->sasl_stream);
655 	if (init_response) {
656 
657 	    /*
658 	     * initial response is already base64 encoded, so we can send it
659 	     * directly.
660 	     */
661 	    /* XXX Encapsulate for logging. */
662 	    vstream_fprintf(server->impl->sasl_stream,
663 			    "\tresp=%s", init_response);
664 	}
665 	/* XXX Encapsulate for logging. */
666 	VSTREAM_PUTC('\n', server->impl->sasl_stream);
667 
668 	if (vstream_fflush(server->impl->sasl_stream) != VSTREAM_EOF)
669 	    break;
670 
671 	if (i == 1) {
672 	    vstring_strcpy(reply, "Can't connect to authentication server");
673 	    return XSASL_AUTH_FAIL;
674 	}
675 
676 	/*
677 	 * Reconnect and try again.
678 	 */
679 	xsasl_dovecot_server_disconnect(server->impl);
680     }
681 
682     return xsasl_dovecot_handle_reply(server, reply);
683 }
684 
685 /* xsasl_dovecot_server_next - continue authentication */
686 
687 static int xsasl_dovecot_server_next(XSASL_SERVER *xp, const char *request,
688 				             VSTRING *reply)
689 {
690     XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
691 
692     if (!is_valid_base64(request)) {
693 	vstring_strcpy(reply, "Invalid base64 data in continued response");
694 	return XSASL_AUTH_FAIL;
695     }
696     /* XXX Encapsulate for logging. */
697     vstream_fprintf(server->impl->sasl_stream,
698 		    "CONT\t%u\t%s\n", server->last_request_id, request);
699     if (vstream_fflush(server->impl->sasl_stream) == VSTREAM_EOF) {
700 	vstring_strcpy(reply, "Connection lost to authentication server");
701 	return XSASL_AUTH_FAIL;
702     }
703     return xsasl_dovecot_handle_reply(server, reply);
704 }
705 
706 /* xsasl_dovecot_server_get_username - get authenticated username */
707 
708 static const char *xsasl_dovecot_server_get_username(XSASL_SERVER *xp)
709 {
710     XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
711 
712     return (server->username);
713 }
714 
715 #endif
716