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  */
112static 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  */
126static 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  */
140typedef 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
146typedef 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  */
158typedef 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  */
176static void xsasl_dovecot_server_done(XSASL_SERVER_IMPL *);
177static XSASL_SERVER *xsasl_dovecot_server_create(XSASL_SERVER_IMPL *,
178					        XSASL_SERVER_CREATE_ARGS *);
179static void xsasl_dovecot_server_free(XSASL_SERVER *);
180static int xsasl_dovecot_server_first(XSASL_SERVER *, const char *,
181				              const char *, VSTRING *);
182static int xsasl_dovecot_server_next(XSASL_SERVER *, const char *, VSTRING *);
183static const char *xsasl_dovecot_server_get_mechanism_list(XSASL_SERVER *);
184static const char *xsasl_dovecot_server_get_username(XSASL_SERVER *);
185
186/* xsasl_dovecot_server_mech_append - append server mechanism entry */
187
188static 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
206static 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
220static 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
252static 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
357static 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
371XSASL_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
388static 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
399static 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
457static 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
477static 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
496static 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
514static 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
545static 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
590static 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
608int     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
687static 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
708static 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