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