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