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