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