1 /*	$NetBSD: xsasl_cyrus_server.c,v 1.3 2020/03/18 19:05:22 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	xsasl_cyrus_server 3
6 /* SUMMARY
7 /*	Cyrus SASL server-side plug-in
8 /* SYNOPSIS
9 /*	#include <xsasl_cyrus_server.h>
10 /*
11 /*	XSASL_SERVER_IMPL *xsasl_cyrus_server_init(server_type, path_info)
12 /*	const char *server_type;
13 /*	const char *path_info;
14 /* DESCRIPTION
15 /*	This module implements the Cyrus SASL server-side authentication
16 /*	plug-in.
17 /*
18 /*	xsasl_cyrus_server_init() initializes the Cyrus SASL library and
19 /*	returns an implementation handle that can be used to generate
20 /*	SASL server instances.
21 /*
22 /*	Arguments:
23 /* .IP server_type
24 /*	The server type (cyrus). This argument is ignored, but it
25 /*	could be used when one implementation provides multiple
26 /*	variants.
27 /* .IP path_info
28 /*	The base name of the SASL server configuration file (example:
29 /*	smtpd becomes /usr/lib/sasl2/smtpd.conf).
30 /* DIAGNOSTICS
31 /*	Fatal: out of memory.
32 /*
33 /*	Panic: interface violation.
34 /*
35 /*	Other: the routines log a warning and return an error result
36 /*	as specified in xsasl_server(3).
37 /* LICENSE
38 /* .ad
39 /* .fi
40 /*	The Secure Mailer license must be distributed with this software.
41 /* AUTHOR(S)
42 /*	Initial implementation by:
43 /*	Till Franke
44 /*	SuSE Rhein/Main AG
45 /*	65760 Eschborn, Germany
46 /*
47 /*	Adopted by:
48 /*	Wietse Venema
49 /*	IBM T.J. Watson Research
50 /*	P.O. Box 704
51 /*	Yorktown Heights, NY 10598, USA
52 /*
53 /*	Wietse Venema
54 /*	Google, Inc.
55 /*	111 8th Avenue
56 /*	New York, NY 10011, USA
57 /*--*/
58 
59 /* System library. */
60 
61 #include <sys_defs.h>
62 #include <sys/socket.h>
63 #include <stdlib.h>
64 #include <string.h>
65 
66 /* Utility library. */
67 
68 #include <msg.h>
69 #include <mymalloc.h>
70 #include <name_mask.h>
71 #include <stringops.h>
72 
73 /* Global library. */
74 
75 #include <mail_params.h>
76 
77 /* Application-specific. */
78 
79 #include <xsasl.h>
80 #include <xsasl_cyrus.h>
81 #include <xsasl_cyrus_common.h>
82 
83 #if defined(USE_SASL_AUTH) && defined(USE_CYRUS_SASL)
84 
85 #include <sasl.h>
86 #include <saslutil.h>
87 
88 /*
89  * Silly little macros.
90  */
91 #define STR(s)	vstring_str(s)
92 
93  /*
94   * Macros to handle API differences between SASLv1 and SASLv2. Specifics:
95   *
96   * The SASL_LOG_* constants were renamed in SASLv2.
97   *
98   * SASLv2's sasl_server_new takes two new parameters to specify local and
99   * remote IP addresses for auth mechs that use them.
100   *
101   * SASLv2's sasl_server_start and sasl_server_step no longer have the errstr
102   * parameter.
103   *
104   * SASLv2's sasl_decode64 function takes an extra parameter for the length of
105   * the output buffer.
106   *
107   * The other major change is that SASLv2 now takes more responsibility for
108   * deallocating memory that it allocates internally.  Thus, some of the
109   * function parameters are now 'const', to make sure we don't try to free
110   * them too.  This is dealt with in the code later on.
111   */
112 
113 #if SASL_VERSION_MAJOR < 2
114 /* SASL version 1.x */
115 #define SASL_SERVER_NEW(srv, fqdn, rlm, lport, rport, cb, secflags, pconn) \
116 	sasl_server_new(srv, fqdn, rlm, cb, secflags, pconn)
117 #define SASL_SERVER_START(conn, mech, clin, clinlen, srvout, srvoutlen, err) \
118 	sasl_server_start(conn, mech, clin, clinlen, srvout, srvoutlen, err)
119 #define SASL_SERVER_STEP(conn, clin, clinlen, srvout, srvoutlen, err) \
120 	sasl_server_step(conn, clin, clinlen, srvout, srvoutlen, err)
121 #define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
122 	sasl_decode64(in, inlen, out, outlen)
123 typedef char *MECHANISM_TYPE;
124 typedef unsigned MECHANISM_COUNT_TYPE;
125 typedef char *SERVEROUT_TYPE;
126 typedef void *VOID_SERVEROUT_TYPE;
127 
128 #endif
129 
130 #if SASL_VERSION_MAJOR >= 2
131 /* SASL version > 2.x */
132 #define SASL_SERVER_NEW(srv, fqdn, rlm, lport, rport, cb, secflags, pconn) \
133 	sasl_server_new(srv, fqdn, rlm, lport, rport, cb, secflags, pconn)
134 #define SASL_SERVER_START(conn, mech, clin, clinlen, srvout, srvoutlen, err) \
135 	sasl_server_start(conn, mech, clin, clinlen, srvout, srvoutlen)
136 #define SASL_SERVER_STEP(conn, clin, clinlen, srvout, srvoutlen, err) \
137 	sasl_server_step(conn, clin, clinlen, srvout, srvoutlen)
138 #define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
139 	sasl_decode64(in, inlen, out, outmaxlen, outlen)
140 typedef const char *MECHANISM_TYPE;
141 typedef int MECHANISM_COUNT_TYPE;
142 typedef const char *SERVEROUT_TYPE;
143 typedef const void *VOID_SERVEROUT_TYPE;
144 
145 #endif
146 
147 #ifndef NO_IP_CYRUS_SASL_AUTH
148 #define USE_IP_CYRUS_SASL_AUTH
149 #endif
150 
151  /*
152   * The XSASL_CYRUS_SERVER object is derived from the generic XSASL_SERVER
153   * object.
154   */
155 typedef struct {
156     XSASL_SERVER xsasl;			/* generic members, must be first */
157     VSTREAM *stream;			/* client-server connection */
158     sasl_conn_t *sasl_conn;		/* SASL context */
159     VSTRING *decoded;			/* decoded challenge or response */
160     char   *username;			/* authenticated user */
161     char   *mechanism_list;		/* applicable mechanisms */
162 } XSASL_CYRUS_SERVER;
163 
164  /*
165   * Forward declarations.
166   */
167 static void xsasl_cyrus_server_done(XSASL_SERVER_IMPL *);
168 static XSASL_SERVER *xsasl_cyrus_server_create(XSASL_SERVER_IMPL *,
169 					        XSASL_SERVER_CREATE_ARGS *);
170 static void xsasl_cyrus_server_free(XSASL_SERVER *);
171 static int xsasl_cyrus_server_first(XSASL_SERVER *, const char *,
172 				            const char *, VSTRING *);
173 static int xsasl_cyrus_server_next(XSASL_SERVER *, const char *, VSTRING *);
174 static int xsasl_cyrus_server_set_security(XSASL_SERVER *, const char *);
175 static const char *xsasl_cyrus_server_get_mechanism_list(XSASL_SERVER *);
176 static const char *xsasl_cyrus_server_get_username(XSASL_SERVER *);
177 
178  /*
179   * SASL callback interface structure. These call-backs have no per-session
180   * context.
181   */
182 #define NO_CALLBACK_CONTEXT	0
183 
184 static sasl_callback_t callbacks[] = {
185     {SASL_CB_LOG, (XSASL_CYRUS_CB) &xsasl_cyrus_log, NO_CALLBACK_CONTEXT},
186     {SASL_CB_LIST_END, 0, 0}
187 };
188 
189 /* xsasl_cyrus_server_init - create implementation handle */
190 
xsasl_cyrus_server_init(const char * unused_server_type,const char * path_info)191 XSASL_SERVER_IMPL *xsasl_cyrus_server_init(const char *unused_server_type,
192 					           const char *path_info)
193 {
194     const char *myname = "xsasl_cyrus_server_init";
195     XSASL_SERVER_IMPL *xp;
196     int     sasl_status;
197 
198 #if SASL_VERSION_MAJOR >= 2 && (SASL_VERSION_MINOR >= 2 \
199     || (SASL_VERSION_MINOR == 1 && SASL_VERSION_STEP >= 19))
200     int     sasl_major;
201     int     sasl_minor;
202     int     sasl_step;
203 
204     /*
205      * DLL hell guard.
206      */
207     sasl_version_info((const char **) 0, (const char **) 0,
208 		      &sasl_major, &sasl_minor,
209 		      &sasl_step, (int *) 0);
210     if (sasl_major != SASL_VERSION_MAJOR
211 #if 0
212 	|| sasl_minor != SASL_VERSION_MINOR
213 	|| sasl_step != SASL_VERSION_STEP
214 #endif
215 	) {
216 	msg_warn("incorrect SASL library version. "
217 	      "Postfix was built with include files from version %d.%d.%d, "
218 		 "but the run-time library version is %d.%d.%d",
219 		 SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
220 		 sasl_major, sasl_minor, sasl_step);
221 	return (0);
222     }
223 #endif
224 
225     if (*var_cyrus_conf_path) {
226 #ifdef SASL_PATH_TYPE_CONFIG			/* Cyrus SASL 2.1.22 */
227 	if (sasl_set_path(SASL_PATH_TYPE_CONFIG,
228 			  var_cyrus_conf_path) != SASL_OK)
229 	    msg_warn("failed to set Cyrus SASL configuration path: \"%s\"",
230 		     var_cyrus_conf_path);
231 #else
232 	msg_warn("%s is not empty, but setting the Cyrus SASL configuration "
233 		 "path is not supported with SASL library version %d.%d.%d",
234 		 VAR_CYRUS_CONF_PATH, SASL_VERSION_MAJOR,
235 		 SASL_VERSION_MINOR, SASL_VERSION_STEP);
236 #endif
237     }
238 
239     /*
240      * Initialize the library: load SASL plug-in routines, etc.
241      */
242     if (msg_verbose)
243 	msg_info("%s: SASL config file is %s.conf", myname, path_info);
244     if ((sasl_status = sasl_server_init(callbacks, path_info)) != SASL_OK) {
245 	msg_warn("SASL per-process initialization failed: %s",
246 		 xsasl_cyrus_strerror(sasl_status));
247 	return (0);
248     }
249 
250     /*
251      * Return a generic XSASL_SERVER_IMPL object. We don't need to extend it
252      * with our own methods or data.
253      */
254     xp = (XSASL_SERVER_IMPL *) mymalloc(sizeof(*xp));
255     xp->create = xsasl_cyrus_server_create;
256     xp->done = xsasl_cyrus_server_done;
257     return (xp);
258 }
259 
260 /* xsasl_cyrus_server_done - dispose of implementation */
261 
xsasl_cyrus_server_done(XSASL_SERVER_IMPL * impl)262 static void xsasl_cyrus_server_done(XSASL_SERVER_IMPL *impl)
263 {
264     myfree((void *) impl);
265     sasl_done();
266 }
267 
268 /* xsasl_cyrus_server_create - create server instance */
269 
xsasl_cyrus_server_create(XSASL_SERVER_IMPL * unused_impl,XSASL_SERVER_CREATE_ARGS * args)270 static XSASL_SERVER *xsasl_cyrus_server_create(XSASL_SERVER_IMPL *unused_impl,
271 				             XSASL_SERVER_CREATE_ARGS *args)
272 {
273     const char *myname = "xsasl_cyrus_server_create";
274     char   *server_addr_port = 0;
275     char   *client_addr_port = 0;
276     sasl_conn_t *sasl_conn = 0;
277     XSASL_CYRUS_SERVER *server = 0;
278     int     sasl_status;
279 
280     if (msg_verbose)
281 	msg_info("%s: SASL service=%s, realm=%s",
282 		 myname, args->service, args->user_realm ?
283 		 args->user_realm : "(null)");
284 
285     /*
286      * The optimizer will eliminate code duplication and/or dead code.
287      */
288 #define XSASL_CYRUS_SERVER_CREATE_ERROR_RETURN(x) \
289     do { \
290 	if (server) { \
291 	    xsasl_cyrus_server_free(&server->xsasl); \
292 	} else { \
293 	    if (sasl_conn) \
294 		sasl_dispose(&sasl_conn); \
295 	} \
296 	XSASL_CYRUS_SERVER_CREATE_RETURN(x); \
297     } while (0)
298 
299 #define XSASL_CYRUS_SERVER_CREATE_RETURN(x) \
300     do { \
301 	if (server_addr_port) \
302 	    myfree(server_addr_port); \
303 	if (client_addr_port) \
304 	    myfree(client_addr_port); \
305 	return (x); \
306     } while (0)
307 
308     /*
309      * Set up a new server context.
310      */
311 #define NO_SECURITY_LAYERS	(0)
312 #define NO_SESSION_CALLBACKS	((sasl_callback_t *) 0)
313 #define NO_AUTH_REALM		((char *) 0)
314 
315 #if SASL_VERSION_MAJOR >= 2 && defined(USE_IP_CYRUS_SASL_AUTH)
316 
317     /*
318      * Get IP address and port of local and remote endpoints for SASL. Some
319      * implementation supports "[ipv6addr]:port" and "ipv4addr:port" (e.g.,
320      * https://illumos.org/man/3sasl/sasl_server_new), They still support the
321      * historical "address;port" syntax, so we stick with that for now.
322      */
323     server_addr_port = (*args->server_addr && *args->server_port ?
324 			concatenate(args->server_addr, ";",
325 				    args->server_port, (char *) 0) : 0);
326     client_addr_port = (*args->client_addr && *args->client_port ?
327 			concatenate(args->client_addr, ";",
328 				    args->client_port, (char *) 0) : 0);
329 #else
330 
331     /*
332      * Don't give any IP address information to SASL.
333      */
334 #endif
335 
336     if ((sasl_status =
337 	 SASL_SERVER_NEW(args->service, var_myhostname,
338 			 args->user_realm ? args->user_realm : NO_AUTH_REALM,
339 			 server_addr_port, client_addr_port,
340 			 NO_SESSION_CALLBACKS, NO_SECURITY_LAYERS,
341 			 &sasl_conn)) != SASL_OK) {
342 	msg_warn("SASL per-connection server initialization: %s",
343 		 xsasl_cyrus_strerror(sasl_status));
344 	XSASL_CYRUS_SERVER_CREATE_ERROR_RETURN(0);
345     }
346 
347     /*
348      * Extend the XSASL_SERVER object with our own data. We use long-lived
349      * conversion buffers rather than local variables to avoid memory leaks
350      * in case of read/write timeout or I/O error.
351      */
352     server = (XSASL_CYRUS_SERVER *) mymalloc(sizeof(*server));
353     server->xsasl.free = xsasl_cyrus_server_free;
354     server->xsasl.first = xsasl_cyrus_server_first;
355     server->xsasl.next = xsasl_cyrus_server_next;
356     server->xsasl.get_mechanism_list = xsasl_cyrus_server_get_mechanism_list;
357     server->xsasl.get_username = xsasl_cyrus_server_get_username;
358     server->stream = args->stream;
359     server->sasl_conn = sasl_conn;
360     server->decoded = vstring_alloc(20);
361     server->username = 0;
362     server->mechanism_list = 0;
363 
364     if (xsasl_cyrus_server_set_security(&server->xsasl, args->security_options)
365 	!= XSASL_AUTH_OK)
366 	XSASL_CYRUS_SERVER_CREATE_ERROR_RETURN(0);
367 
368     XSASL_CYRUS_SERVER_CREATE_RETURN(&server->xsasl);
369 }
370 
371 /* xsasl_cyrus_server_set_security - set security properties */
372 
xsasl_cyrus_server_set_security(XSASL_SERVER * xp,const char * sasl_opts_val)373 static int xsasl_cyrus_server_set_security(XSASL_SERVER *xp,
374 					           const char *sasl_opts_val)
375 {
376     XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
377     sasl_security_properties_t sec_props;
378     int     sasl_status;
379 
380     /*
381      * Security options. Some information can be found in the sasl.h include
382      * file.
383      */
384     memset(&sec_props, 0, sizeof(sec_props));
385     sec_props.min_ssf = 0;
386     sec_props.max_ssf = 0;			/* don't allow real SASL
387 						 * security layer */
388     if (*sasl_opts_val == 0) {
389 	sec_props.security_flags = 0;
390     } else {
391 	sec_props.security_flags =
392 	    xsasl_cyrus_security_parse_opts(sasl_opts_val);
393 	if (sec_props.security_flags == 0) {
394 	    msg_warn("bad per-session SASL security properties");
395 	    return (XSASL_AUTH_FAIL);
396 	}
397     }
398     sec_props.maxbufsize = 0;
399     sec_props.property_names = 0;
400     sec_props.property_values = 0;
401 
402     if ((sasl_status = sasl_setprop(server->sasl_conn, SASL_SEC_PROPS,
403 				    &sec_props)) != SASL_OK) {
404 	msg_warn("SASL per-connection security setup; %s",
405 		 xsasl_cyrus_strerror(sasl_status));
406 	return (XSASL_AUTH_FAIL);
407     }
408     return (XSASL_AUTH_OK);
409 }
410 
411 /* xsasl_cyrus_server_get_mechanism_list - get available mechanisms */
412 
xsasl_cyrus_server_get_mechanism_list(XSASL_SERVER * xp)413 static const char *xsasl_cyrus_server_get_mechanism_list(XSASL_SERVER *xp)
414 {
415     const char *myname = "xsasl_cyrus_server_get_mechanism_list";
416     XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
417     MECHANISM_TYPE mechanism_list;
418     MECHANISM_COUNT_TYPE mechanism_count;
419     int     sasl_status;
420 
421     /*
422      * Get the list of authentication mechanisms.
423      */
424 #define UNSUPPORTED_USER	((char *) 0)
425 #define IGNORE_MECHANISM_LEN	((unsigned *) 0)
426 
427     if ((sasl_status = sasl_listmech(server->sasl_conn, UNSUPPORTED_USER,
428 				     "", " ", "",
429 				     &mechanism_list,
430 				     IGNORE_MECHANISM_LEN,
431 				     &mechanism_count)) != SASL_OK) {
432 	msg_warn("%s: %s", myname, xsasl_cyrus_strerror(sasl_status));
433 	return (0);
434     }
435     if (mechanism_count <= 0) {
436 	msg_warn("%s: no applicable SASL mechanisms", myname);
437 	return (0);
438     }
439     server->mechanism_list = mystrdup(mechanism_list);
440 #if SASL_VERSION_MAJOR < 2
441     /* SASL version 1 doesn't free memory that it allocates. */
442     free(mechanism_list);
443 #endif
444     return (server->mechanism_list);
445 }
446 
447 /* xsasl_cyrus_server_free - destroy server instance */
448 
xsasl_cyrus_server_free(XSASL_SERVER * xp)449 static void xsasl_cyrus_server_free(XSASL_SERVER *xp)
450 {
451     XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
452 
453     sasl_dispose(&server->sasl_conn);
454     vstring_free(server->decoded);
455     if (server->username)
456 	myfree(server->username);
457     if (server->mechanism_list)
458 	myfree(server->mechanism_list);
459     myfree((void *) server);
460 }
461 
462 /* xsasl_cyrus_server_auth_response - encode server first/next response */
463 
xsasl_cyrus_server_auth_response(int sasl_status,SERVEROUT_TYPE serverout,unsigned serveroutlen,VSTRING * reply)464 static int xsasl_cyrus_server_auth_response(int sasl_status,
465 					            SERVEROUT_TYPE serverout,
466 					            unsigned serveroutlen,
467 					            VSTRING *reply)
468 {
469     const char *myname = "xsasl_cyrus_server_auth_response";
470     unsigned enc_length;
471     unsigned enc_length_out;
472 
473     /*
474      * Encode the server first/next non-error response; otherwise return the
475      * unencoded error text that corresponds to the SASL error status.
476      *
477      * Regarding the hairy expression below: output from sasl_encode64() comes
478      * in multiples of four bytes for each triple of input bytes, plus four
479      * bytes for any incomplete last triple, plus one byte for the null
480      * terminator.
481      */
482     if (sasl_status == SASL_OK) {
483 	vstring_strcpy(reply, "");
484 	return (XSASL_AUTH_DONE);
485     } else if (sasl_status == SASL_CONTINUE) {
486 	if (msg_verbose)
487 	    msg_info("%s: uncoded server challenge: %.*s",
488 		     myname, (int) serveroutlen, serverout);
489 	enc_length = ((serveroutlen + 2) / 3) * 4 + 1;
490 	VSTRING_RESET(reply);			/* Fix 200512 */
491 	VSTRING_SPACE(reply, enc_length);
492 	if ((sasl_status = sasl_encode64(serverout, serveroutlen,
493 					 STR(reply), vstring_avail(reply),
494 					 &enc_length_out)) != SASL_OK)
495 	    msg_panic("%s: sasl_encode64 botch: %s",
496 		      myname, xsasl_cyrus_strerror(sasl_status));
497 	return (XSASL_AUTH_MORE);
498     } else {
499 	if (sasl_status == SASL_NOUSER)		/* privacy */
500 	    sasl_status = SASL_BADAUTH;
501 	vstring_strcpy(reply, xsasl_cyrus_strerror(sasl_status));
502 	switch (sasl_status) {
503 	case SASL_FAIL:
504 	case SASL_NOMEM:
505 	case SASL_TRYAGAIN:
506 	case SASL_UNAVAIL:
507 	    return XSASL_AUTH_TEMP;
508 	default:
509 	    return (XSASL_AUTH_FAIL);
510 	}
511     }
512 }
513 
514 /* xsasl_cyrus_server_first - per-session authentication */
515 
xsasl_cyrus_server_first(XSASL_SERVER * xp,const char * sasl_method,const char * init_response,VSTRING * reply)516 int     xsasl_cyrus_server_first(XSASL_SERVER *xp, const char *sasl_method,
517 			          const char *init_response, VSTRING *reply)
518 {
519     const char *myname = "xsasl_cyrus_server_first";
520     XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
521     char   *dec_buffer;
522     unsigned dec_length;
523     unsigned reply_len;
524     unsigned serveroutlen;
525     int     sasl_status;
526     SERVEROUT_TYPE serverout = 0;
527     int     xsasl_status;
528 
529 #if SASL_VERSION_MAJOR < 2
530     const char *errstr = 0;
531 
532 #endif
533 
534 #define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))
535 
536     if (msg_verbose)
537 	msg_info("%s: sasl_method %s%s%s", myname, sasl_method,
538 		 IFELSE(init_response, ", init_response ", ""),
539 		 IFELSE(init_response, init_response, ""));
540 
541     /*
542      * SASL authentication protocol start-up. Process any initial client
543      * response that was sent along in the AUTH command.
544      */
545     if (init_response) {
546 	reply_len = strlen(init_response);
547 	VSTRING_RESET(server->decoded);		/* Fix 200512 */
548 	VSTRING_SPACE(server->decoded, reply_len);
549 	if ((sasl_status = SASL_DECODE64(init_response, reply_len,
550 					 dec_buffer = STR(server->decoded),
551 					 vstring_avail(server->decoded),
552 					 &dec_length)) != SASL_OK) {
553 	    vstring_strcpy(reply, xsasl_cyrus_strerror(sasl_status));
554 	    return (XSASL_AUTH_FORM);
555 	}
556 	if (msg_verbose)
557 	    msg_info("%s: decoded initial response %s", myname, dec_buffer);
558     } else {
559 	dec_buffer = 0;
560 	dec_length = 0;
561     }
562     sasl_status = SASL_SERVER_START(server->sasl_conn, sasl_method, dec_buffer,
563 				    dec_length, &serverout,
564 				    &serveroutlen, &errstr);
565     xsasl_status = xsasl_cyrus_server_auth_response(sasl_status, serverout,
566 						    serveroutlen, reply);
567 #if SASL_VERSION_MAJOR < 2
568     /* SASL version 1 doesn't free memory that it allocates. */
569     free(serverout);
570 #endif
571     return (xsasl_status);
572 }
573 
574 /* xsasl_cyrus_server_next - continue authentication */
575 
xsasl_cyrus_server_next(XSASL_SERVER * xp,const char * request,VSTRING * reply)576 static int xsasl_cyrus_server_next(XSASL_SERVER *xp, const char *request,
577 				           VSTRING *reply)
578 {
579     const char *myname = "xsasl_cyrus_server_next";
580     XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
581     unsigned dec_length;
582     unsigned request_len;
583     unsigned serveroutlen;
584     int     sasl_status;
585     SERVEROUT_TYPE serverout = 0;
586     int     xsasl_status;
587 
588 #if SASL_VERSION_MAJOR < 2
589     const char *errstr = 0;
590 
591 #endif
592 
593     request_len = strlen(request);
594     VSTRING_RESET(server->decoded);		/* Fix 200512 */
595     VSTRING_SPACE(server->decoded, request_len);
596     if ((sasl_status = SASL_DECODE64(request, request_len,
597 				     STR(server->decoded),
598 				     vstring_avail(server->decoded),
599 				     &dec_length)) != SASL_OK) {
600 	vstring_strcpy(reply, xsasl_cyrus_strerror(sasl_status));
601 	return (XSASL_AUTH_FORM);
602     }
603     if (msg_verbose)
604 	msg_info("%s: decoded response: %.*s",
605 		 myname, (int) dec_length, STR(server->decoded));
606     sasl_status = SASL_SERVER_STEP(server->sasl_conn, STR(server->decoded),
607 				   dec_length, &serverout,
608 				   &serveroutlen, &errstr);
609     xsasl_status = xsasl_cyrus_server_auth_response(sasl_status, serverout,
610 						    serveroutlen, reply);
611 #if SASL_VERSION_MAJOR < 2
612     /* SASL version 1 doesn't free memory that it allocates. */
613     free(serverout);
614 #endif
615     return (xsasl_status);
616 }
617 
618 /* xsasl_cyrus_server_get_username - get authenticated username */
619 
xsasl_cyrus_server_get_username(XSASL_SERVER * xp)620 static const char *xsasl_cyrus_server_get_username(XSASL_SERVER *xp)
621 {
622     const char *myname = "xsasl_cyrus_server_get_username";
623     XSASL_CYRUS_SERVER *server = (XSASL_CYRUS_SERVER *) xp;
624     VOID_SERVEROUT_TYPE serverout = 0;
625     int     sasl_status;
626 
627     /*
628      * XXX Do not free(serverout).
629      */
630     sasl_status = sasl_getprop(server->sasl_conn, SASL_USERNAME, &serverout);
631     if (sasl_status != SASL_OK || serverout == 0) {
632 	msg_warn("%s: sasl_getprop SASL_USERNAME botch: %s",
633 		 myname, xsasl_cyrus_strerror(sasl_status));
634 	return (0);
635     }
636     if (server->username)
637 	myfree(server->username);
638     server->username = mystrdup(serverout);
639     printable(server->username, '?');
640     return (server->username);
641 }
642 
643 #endif
644