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