Home | History | Annotate | Download | only in plugin
      1 /*
      2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 #pragma ident	"%Z%%M%	%I%	%E% SMI"
      6 
      7 /* Generic SASL plugin utility functions
      8  * Rob Siemborski
      9  * $Id: plugin_common.c,v 1.13 2003/02/13 19:56:05 rjs3 Exp $
     10  */
     11 /*
     12  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
     13  *
     14  * Redistribution and use in source and binary forms, with or without
     15  * modification, are permitted provided that the following conditions
     16  * are met:
     17  *
     18  * 1. Redistributions of source code must retain the above copyright
     19  *    notice, this list of conditions and the following disclaimer.
     20  *
     21  * 2. Redistributions in binary form must reproduce the above copyright
     22  *    notice, this list of conditions and the following disclaimer in
     23  *    the documentation and/or other materials provided with the
     24  *    distribution.
     25  *
     26  * 3. The name "Carnegie Mellon University" must not be used to
     27  *    endorse or promote products derived from this software without
     28  *    prior written permission. For permission or any other legal
     29  *    details, please contact
     30  *      Office of Technology Transfer
     31  *      Carnegie Mellon University
     32  *      5000 Forbes Avenue
     33  *      Pittsburgh, PA  15213-3890
     34  *      (412) 268-4387, fax: (412) 268-7395
     35  *      tech-transfer (at) andrew.cmu.edu
     36  *
     37  * 4. Redistributions of any form whatsoever must retain the following
     38  *    acknowledgment:
     39  *    "This product includes software developed by Computing Services
     40  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
     41  *
     42  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
     43  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
     44  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
     45  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     46  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
     47  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     48  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     49  */
     50 
     51 #include <config.h>
     52 #ifndef macintosh
     53 #ifdef WIN32
     54 # include <winsock.h>
     55 #else
     56 # include <sys/socket.h>
     57 # include <netinet/in.h>
     58 # include <arpa/inet.h>
     59 # include <netdb.h>
     60 #endif /* WIN32 */
     61 #endif /* macintosh */
     62 #ifdef HAVE_UNISTD_H
     63 #include <unistd.h>
     64 #endif
     65 #include <fcntl.h>
     66 #include <sasl.h>
     67 #include <saslutil.h>
     68 #include <saslplug.h>
     69 
     70 #include <errno.h>
     71 #include <ctype.h>
     72 
     73 #ifdef HAVE_INTTYPES_H
     74 #include <inttypes.h>
     75 #endif
     76 
     77 #include "plugin_common.h"
     78 
     79 /* translate IPv4 mapped IPv6 address to IPv4 address */
     80 static void sockaddr_unmapped(
     81 #ifdef IN6_IS_ADDR_V4MAPPED
     82   struct sockaddr *sa, socklen_t *len
     83 #else
     84   struct sockaddr *sa __attribute__((unused)),
     85   socklen_t *len __attribute__((unused))
     86 #endif
     87 )
     88 {
     89 #ifdef IN6_IS_ADDR_V4MAPPED
     90     struct sockaddr_in6 *sin6;
     91     struct sockaddr_in *sin4;
     92     uint32_t addr;
     93 #ifdef _SUN_SDK_
     94     in_port_t port;
     95 #else
     96     int port;
     97 #endif /* _SUN_SDK_ */
     98 
     99     if (sa->sa_family != AF_INET6)
    100 	return;
    101 /* LINTED pointer alignment */
    102     sin6 = (struct sockaddr_in6 *)sa;
    103     if (!IN6_IS_ADDR_V4MAPPED((&sin6->sin6_addr)))
    104 	return;
    105 /* LINTED pointer alignment */
    106     sin4 = (struct sockaddr_in *)sa;
    107 /* LINTED pointer alignment */
    108     addr = *(uint32_t *)&sin6->sin6_addr.s6_addr[12];
    109     port = sin6->sin6_port;
    110     memset(sin4, 0, sizeof(struct sockaddr_in));
    111     sin4->sin_addr.s_addr = addr;
    112     sin4->sin_port = port;
    113     sin4->sin_family = AF_INET;
    114 #ifdef HAVE_SOCKADDR_SA_LEN
    115     sin4->sin_len = sizeof(struct sockaddr_in);
    116 #endif
    117     *len = sizeof(struct sockaddr_in);
    118 #else
    119     return;
    120 #endif
    121 }
    122 
    123 int _plug_ipfromstring(const sasl_utils_t *utils, const char *addr,
    124 		       struct sockaddr *out, socklen_t outlen)
    125 {
    126     int i, j;
    127     socklen_t len;
    128 #ifdef WINNT /* _SUN_SDK_ */
    129     struct sockaddr_in ss;
    130 #else
    131     struct sockaddr_storage ss;
    132 #endif	/* _SUN_SDK_ */
    133     struct addrinfo hints, *ai = NULL;
    134     char hbuf[NI_MAXHOST];
    135 #ifdef _SUN_SDK_
    136     const char *start, *end, *p;
    137 #endif	/* _SUN_SDK_ */
    138 
    139     if(!utils || !addr || !out) {
    140 	if(utils) PARAMERROR( utils );
    141 	return SASL_BADPARAM;
    142     }
    143 
    144 #ifdef _SUN_SDK_
    145     end = strchr(addr, ']');
    146     if (end != NULL) {
    147 	/* This an rfc 2732 ipv6 address */
    148 	start = strchr(addr, '[');
    149 	if (start >= end || start == NULL) {
    150 	    if(utils) PARAMERROR( utils );
    151 	    return SASL_BADPARAM;
    152 	}
    153 	for (i = 0, p = start + 1; p < end; p++) {
    154 	    hbuf[i++] = *p;
    155 	    if (i >= NI_MAXHOST)
    156 		break;
    157 	}
    158 	p = strchr(end, ':');
    159 	if (p == NULL)
    160 		p = end + 1;
    161 	else
    162 		p = p + 1;
    163     } else {
    164 	for (i = 0; addr[i] != '\0' && addr[i] != ';'; ) {
    165 	    hbuf[i] = addr[i];
    166 	    if (++i >= NI_MAXHOST)
    167 		break;
    168 	}
    169 	if (addr[i] == ';')
    170 	     p = &addr[i+1];
    171 	else
    172 	     p = &addr[i];
    173     }
    174     if (i >= NI_MAXHOST) {
    175 	if(utils) PARAMERROR( utils );
    176 	return SASL_BADPARAM;
    177     }
    178     hbuf[i] = '\0';
    179     for (j = 0; p[j] != '\0'; j++)
    180 	if (!isdigit((int)(p[j]))) {
    181 	    PARAMERROR( utils );
    182 	    return SASL_BADPARAM;
    183 	}
    184 #else
    185     /* Parse the address */
    186     for (i = 0; addr[i] != '\0' && addr[i] != ';'; i++) {
    187 	if (i >= NI_MAXHOST) {
    188 	    if(utils) PARAMERROR( utils );
    189 	    return SASL_BADPARAM;
    190 	}
    191 	hbuf[i] = addr[i];
    192     }
    193     hbuf[i] = '\0';
    194 
    195     if (addr[i] == ';')
    196 	i++;
    197     /* XXX/FIXME: Do we need this check? */
    198     for (j = i; addr[j] != '\0'; j++)
    199 	if (!isdigit((int)(addr[j]))) {
    200 	    PARAMERROR( utils );
    201 	    return SASL_BADPARAM;
    202 	}
    203 #endif /* _SUN_SDK_ */
    204 
    205     memset(&hints, 0, sizeof(hints));
    206     hints.ai_family = PF_UNSPEC;
    207     hints.ai_socktype = SOCK_STREAM;
    208     hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
    209 
    210 #ifdef _SUN_SDK_
    211     if (getaddrinfo(hbuf, p, &hints, &ai) != 0) {
    212 #else
    213     if (getaddrinfo(hbuf, &addr[i], &hints, &ai) != 0) {
    214 #endif /* _SUN_SDK_ */
    215 	PARAMERROR( utils );
    216 	return SASL_BADPARAM;
    217     }
    218 
    219     len = ai->ai_addrlen;
    220 #ifdef _SUN_SDK_
    221     if (len > sizeof(ss))
    222 	return (SASL_BUFOVER);
    223 #endif /* _SUN_SDK_ */
    224     memcpy(&ss, ai->ai_addr, len);
    225     freeaddrinfo(ai);
    226     sockaddr_unmapped((struct sockaddr *)&ss, &len);
    227     if (outlen < len) {
    228 	PARAMERROR( utils );
    229 	return SASL_BUFOVER;
    230     }
    231 
    232     memcpy(out, &ss, len);
    233 
    234     return SASL_OK;
    235 }
    236 
    237 int _plug_iovec_to_buf(const sasl_utils_t *utils, const struct iovec *vec,
    238 		       unsigned numiov, buffer_info_t **output)
    239 {
    240     unsigned i;
    241     int ret;
    242     buffer_info_t *out;
    243     char *pos;
    244 
    245     if(!utils || !vec || !output) {
    246 	if(utils) PARAMERROR( utils );
    247 	return SASL_BADPARAM;
    248     }
    249 
    250     if(!(*output)) {
    251 	*output = utils->malloc(sizeof(buffer_info_t));
    252 	if(!*output) {
    253 	    MEMERROR(utils);
    254 	    return SASL_NOMEM;
    255 	}
    256 	memset(*output,0,sizeof(buffer_info_t));
    257     }
    258 
    259     out = *output;
    260 
    261     out->curlen = 0;
    262     for(i=0; i<numiov; i++)
    263 	out->curlen += vec[i].iov_len;
    264 
    265     ret = _plug_buf_alloc(utils, &out->data, &out->reallen, out->curlen);
    266 
    267     if(ret != SASL_OK) {
    268 	MEMERROR(utils);
    269 	return SASL_NOMEM;
    270     }
    271 
    272     memset(out->data, 0, out->reallen);
    273     pos = out->data;
    274 
    275     for(i=0; i<numiov; i++) {
    276 	memcpy(pos, vec[i].iov_base, vec[i].iov_len);
    277 	pos += vec[i].iov_len;
    278     }
    279 
    280     return SASL_OK;
    281 }
    282 
    283 /* Basically a conditional call to realloc(), if we need more */
    284 int _plug_buf_alloc(const sasl_utils_t *utils, char **rwbuf,
    285 		    unsigned *curlen, unsigned newlen)
    286 {
    287     if(!utils || !rwbuf || !curlen) {
    288 	PARAMERROR(utils);
    289 	return SASL_BADPARAM;
    290     }
    291 
    292     if(!(*rwbuf)) {
    293 	*rwbuf = utils->malloc(newlen);
    294 	if (*rwbuf == NULL) {
    295 	    *curlen = 0;
    296 	    MEMERROR(utils);
    297 	    return SASL_NOMEM;
    298 	}
    299 	*curlen = newlen;
    300     } else if(*rwbuf && *curlen < newlen) {
    301 #ifdef _SUN_SDK_
    302 	unsigned needed = 2*(*curlen);
    303 #else
    304 	size_t needed = 2*(*curlen);
    305 #endif /* _SUN_SDK_ */
    306 
    307 	while(needed < newlen)
    308 	    needed *= 2;
    309 
    310 	*rwbuf = utils->realloc(*rwbuf, needed);
    311 	if (*rwbuf == NULL) {
    312 	    *curlen = 0;
    313 	    MEMERROR(utils);
    314 	    return SASL_NOMEM;
    315 	}
    316 	*curlen = needed;
    317     }
    318 
    319     return SASL_OK;
    320 }
    321 
    322 /* copy a string */
    323 int _plug_strdup(const sasl_utils_t * utils, const char *in,
    324 		 char **out, int *outlen)
    325 {
    326 #ifdef _SUN_SDK_
    327   int len;
    328 #else
    329   size_t len = strlen(in);
    330 #endif /* _SUN_SDK_ */
    331 
    332   if(!utils || !in || !out) {
    333       if(utils) PARAMERROR(utils);
    334       return SASL_BADPARAM;
    335   }
    336 
    337 #ifdef _SUN_SDK_
    338   len = strlen(in);
    339 #endif /* _SUN_SDK_ */
    340   *out = utils->malloc(len + 1);
    341   if (!*out) {
    342       MEMERROR(utils);
    343       return SASL_NOMEM;
    344   }
    345 
    346   strcpy((char *) *out, in);
    347 
    348   if (outlen)
    349       *outlen = len;
    350 
    351   return SASL_OK;
    352 }
    353 
    354 void _plug_free_string(const sasl_utils_t *utils, char **str)
    355 {
    356   size_t len;
    357 
    358   if (!utils || !str || !(*str)) return;
    359 
    360   len = strlen(*str);
    361 
    362   utils->erasebuffer(*str, len);
    363   utils->free(*str);
    364 
    365   *str=NULL;
    366 }
    367 
    368 void _plug_free_secret(const sasl_utils_t *utils, sasl_secret_t **secret)
    369 {
    370     if(!utils || !secret || !(*secret)) return;
    371 
    372 #ifdef _SUN_SDK_
    373     utils->erasebuffer((char *)(*secret)->data, (*secret)->len);
    374 #else
    375     utils->erasebuffer((*secret)->data, (*secret)->len);
    376 #endif /* _SUN_SDK_ */
    377     utils->free(*secret);
    378     *secret = NULL;
    379 }
    380 
    381 /*
    382  * Trys to find the prompt with the lookingfor id in the prompt list
    383  * Returns it if found. NULL otherwise
    384  */
    385 sasl_interact_t *_plug_find_prompt(sasl_interact_t **promptlist,
    386 				   unsigned int lookingfor)
    387 {
    388     sasl_interact_t *prompt;
    389 
    390     if (promptlist && *promptlist) {
    391 	for (prompt = *promptlist; prompt->id != SASL_CB_LIST_END; ++prompt) {
    392 	    if (prompt->id==lookingfor)
    393 		return prompt;
    394 	}
    395     }
    396 
    397     return NULL;
    398 }
    399 
    400 /*
    401  * Retrieve the simple string given by the callback id.
    402  */
    403 int _plug_get_simple(const sasl_utils_t *utils, unsigned int id, int required,
    404 		     const char **result, sasl_interact_t **prompt_need)
    405 {
    406 
    407     int ret = SASL_FAIL;
    408     sasl_getsimple_t *simple_cb;
    409     void *simple_context;
    410     sasl_interact_t *prompt;
    411 
    412     *result = NULL;
    413 
    414     /* see if we were given the result in the prompt */
    415     prompt = _plug_find_prompt(prompt_need, id);
    416     if (prompt != NULL) {
    417 	/* We prompted, and got.*/
    418 
    419 	if (required && !prompt->result) {
    420 	    SETERROR(utils, "Unexpectedly missing a prompt result");
    421 	    return SASL_BADPARAM;
    422 	}
    423 
    424 	*result = prompt->result;
    425 	return SASL_OK;
    426     }
    427 
    428     /* Try to get the callback... */
    429     ret = utils->getcallback(utils->conn, id, &simple_cb, &simple_context);
    430 
    431     if (ret == SASL_FAIL && !required)
    432 	return SASL_OK;
    433 
    434     if (ret == SASL_OK && simple_cb) {
    435 	ret = simple_cb(simple_context, id, result, NULL);
    436 	if (ret != SASL_OK)
    437 	    return ret;
    438 
    439 	if (required && !*result) {
    440 	    PARAMERROR(utils);
    441 	    return SASL_BADPARAM;
    442 	}
    443     }
    444 
    445     return ret;
    446 }
    447 
    448 /*
    449  * Retrieve the user password.
    450  */
    451 int _plug_get_password(const sasl_utils_t *utils, sasl_secret_t **password,
    452 		       unsigned int *iscopy, sasl_interact_t **prompt_need)
    453 {
    454     int ret = SASL_FAIL;
    455     sasl_getsecret_t *pass_cb;
    456     void *pass_context;
    457     sasl_interact_t *prompt;
    458 
    459     *password = NULL;
    460     *iscopy = 0;
    461 
    462     /* see if we were given the password in the prompt */
    463     prompt = _plug_find_prompt(prompt_need, SASL_CB_PASS);
    464     if (prompt != NULL) {
    465 	/* We prompted, and got.*/
    466 
    467 	if (!prompt->result) {
    468 	    SETERROR(utils, "Unexpectedly missing a prompt result");
    469 	    return SASL_BADPARAM;
    470 	}
    471 
    472 	/* copy what we got into a secret_t */
    473 	*password = (sasl_secret_t *) utils->malloc(sizeof(sasl_secret_t) +
    474 						    prompt->len + 1);
    475 	if (!*password) {
    476 	    MEMERROR(utils);
    477 	    return SASL_NOMEM;
    478 	}
    479 
    480 	(*password)->len=prompt->len;
    481 	memcpy((*password)->data, prompt->result, prompt->len);
    482 	(*password)->data[(*password)->len]=0;
    483 
    484 	*iscopy = 1;
    485 
    486 	return SASL_OK;
    487     }
    488 
    489     /* Try to get the callback... */
    490     ret = utils->getcallback(utils->conn, SASL_CB_PASS,
    491 			     &pass_cb, &pass_context);
    492 
    493     if (ret == SASL_OK && pass_cb) {
    494 	ret = pass_cb(utils->conn, pass_context, SASL_CB_PASS, password);
    495 	if (ret != SASL_OK)
    496 	    return ret;
    497 
    498 	if (!*password) {
    499 	    PARAMERROR(utils);
    500 	    return SASL_BADPARAM;
    501 	}
    502     }
    503 
    504     return ret;
    505 }
    506 
    507 /*
    508  * Retrieve the string given by the challenge prompt id.
    509  */
    510 int _plug_challenge_prompt(const sasl_utils_t *utils, unsigned int id,
    511 			   const char *challenge, const char *promptstr,
    512 			   const char **result, sasl_interact_t **prompt_need)
    513 {
    514     int ret = SASL_FAIL;
    515     sasl_chalprompt_t *chalprompt_cb;
    516     void *chalprompt_context;
    517     sasl_interact_t *prompt;
    518 
    519     *result = NULL;
    520 
    521     /* see if we were given the password in the prompt */
    522     prompt = _plug_find_prompt(prompt_need, id);
    523     if (prompt != NULL) {
    524 	/* We prompted, and got.*/
    525 
    526 	if (!prompt->result) {
    527 	    SETERROR(utils, "Unexpectedly missing a prompt result");
    528 	    return SASL_BADPARAM;
    529 	}
    530 
    531 	*result = prompt->result;
    532 	return SASL_OK;
    533     }
    534 
    535     /* Try to get the callback... */
    536     ret = utils->getcallback(utils->conn, id,
    537 			     &chalprompt_cb, &chalprompt_context);
    538 
    539     if (ret == SASL_OK && chalprompt_cb) {
    540 	ret = chalprompt_cb(chalprompt_context, id,
    541 			    challenge, promptstr, NULL, result, NULL);
    542 	if (ret != SASL_OK)
    543 	    return ret;
    544 
    545 	if (!*result) {
    546 	    PARAMERROR(utils);
    547 	    return SASL_BADPARAM;
    548 	}
    549     }
    550 
    551     return ret;
    552 }
    553 
    554 /*
    555  * Retrieve the client realm.
    556  */
    557 int _plug_get_realm(const sasl_utils_t *utils, const char **availrealms,
    558 		    const char **realm, sasl_interact_t **prompt_need)
    559 {
    560     int ret = SASL_FAIL;
    561     sasl_getrealm_t *realm_cb;
    562     void *realm_context;
    563     sasl_interact_t *prompt;
    564 
    565     *realm = NULL;
    566 
    567     /* see if we were given the result in the prompt */
    568     prompt = _plug_find_prompt(prompt_need, SASL_CB_GETREALM);
    569     if (prompt != NULL) {
    570 	/* We prompted, and got.*/
    571 
    572 	if (!prompt->result) {
    573 	    SETERROR(utils, "Unexpectedly missing a prompt result");
    574 	    return SASL_BADPARAM;
    575 	}
    576 
    577 	*realm = prompt->result;
    578 	return SASL_OK;
    579     }
    580 
    581     /* Try to get the callback... */
    582     ret = utils->getcallback(utils->conn, SASL_CB_GETREALM,
    583 			     &realm_cb, &realm_context);
    584 
    585     if (ret == SASL_OK && realm_cb) {
    586 	ret = realm_cb(realm_context, SASL_CB_GETREALM, availrealms, realm);
    587 	if (ret != SASL_OK)
    588 	    return ret;
    589 
    590 	if (!*realm) {
    591 	    PARAMERROR(utils);
    592 	    return SASL_BADPARAM;
    593 	}
    594     }
    595 
    596     return ret;
    597 }
    598 
    599 /*
    600  * Make the requested prompts. (prompt==NULL means we don't want it)
    601  */
    602 int _plug_make_prompts(const sasl_utils_t *utils,
    603 #ifdef _INTEGRATED_SOLARIS_
    604 		      void **h,
    605 #endif /* _INTEGRATED_SOLARIS_ */
    606 		       sasl_interact_t **prompts_res,
    607 		       const char *user_prompt, const char *user_def,
    608 		       const char *auth_prompt, const char *auth_def,
    609 		       const char *pass_prompt, const char *pass_def,
    610 		       const char *echo_chal,
    611 		       const char *echo_prompt, const char *echo_def,
    612 		       const char *realm_chal,
    613 		       const char *realm_prompt, const char *realm_def)
    614 {
    615     int num = 1;
    616     int alloc_size;
    617     sasl_interact_t *prompts;
    618 
    619     if (user_prompt) num++;
    620     if (auth_prompt) num++;
    621     if (pass_prompt) num++;
    622     if (echo_prompt) num++;
    623     if (realm_prompt) num++;
    624 
    625     if (num == 1) {
    626 	SETERROR( utils, "make_prompts() called with no actual prompts" );
    627 	return SASL_FAIL;
    628     }
    629 
    630     alloc_size = sizeof(sasl_interact_t)*num;
    631     prompts = utils->malloc(alloc_size);
    632     if (!prompts) {
    633 	MEMERROR( utils );
    634 	return SASL_NOMEM;
    635     }
    636     memset(prompts, 0, alloc_size);
    637 
    638     *prompts_res = prompts;
    639 
    640     if (user_prompt) {
    641 	(prompts)->id = SASL_CB_USER;
    642 #ifdef _INTEGRATED_SOLARIS_
    643 	(prompts)->challenge = convert_prompt(utils, h,
    644 		gettext("Authorization Name"));
    645 #else
    646 	(prompts)->challenge = "Authorization Name";
    647 #endif /* _INTEGRATED_SOLARIS_ */
    648 	(prompts)->prompt = user_prompt;
    649 	(prompts)->defresult = user_def;
    650 
    651 	prompts++;
    652     }
    653 
    654     if (auth_prompt) {
    655 	(prompts)->id = SASL_CB_AUTHNAME;
    656 #ifdef _INTEGRATED_SOLARIS_
    657 	(prompts)->challenge = convert_prompt(utils, h,
    658 		gettext( "Authentication Name"));
    659 #else
    660 	(prompts)->challenge = "Authentication Name";
    661 #endif /* _INTEGRATED_SOLARIS_ */
    662 	(prompts)->prompt = auth_prompt;
    663 	(prompts)->defresult = auth_def;
    664 
    665 	prompts++;
    666     }
    667 
    668     if (pass_prompt) {
    669 	(prompts)->id = SASL_CB_PASS;
    670 #ifdef _INTEGRATED_SOLARIS_
    671 	(prompts)->challenge = convert_prompt(utils, h, gettext("Password"));
    672 #else
    673 	(prompts)->challenge = "Password";
    674 #endif /* _INTEGRATED_SOLARIS_ */
    675 	(prompts)->prompt = pass_prompt;
    676 	(prompts)->defresult = pass_def;
    677 
    678 	prompts++;
    679     }
    680 
    681     if (echo_prompt) {
    682 	(prompts)->id = SASL_CB_ECHOPROMPT;
    683 	(prompts)->challenge = echo_chal;
    684 	(prompts)->prompt = echo_prompt;
    685 	(prompts)->defresult = echo_def;
    686 
    687 	prompts++;
    688     }
    689 
    690     if (realm_prompt) {
    691 	(prompts)->id = SASL_CB_GETREALM;
    692 	(prompts)->challenge = realm_chal;
    693 	(prompts)->prompt = realm_prompt;
    694 	(prompts)->defresult = realm_def;
    695 
    696 	prompts++;
    697     }
    698 
    699     /* add the ending one */
    700     (prompts)->id = SASL_CB_LIST_END;
    701     (prompts)->challenge = NULL;
    702     (prompts)->prompt = NULL;
    703     (prompts)->defresult = NULL;
    704 
    705     return SASL_OK;
    706 }
    707 
    708 /*
    709  * Decode and concatenate multiple packets using the given function
    710  * to decode each packet.
    711  */
    712 int _plug_decode(const sasl_utils_t *utils,
    713 		 void *context,
    714 		 const char *input, unsigned inputlen,
    715 		 char **output,		/* output buffer */
    716 		 unsigned *outputsize,	/* current size of output buffer */
    717 		 unsigned *outputlen,	/* length of data in output buffer */
    718 		 int (*decode_pkt)(void *context,
    719 				   const char **input, unsigned *inputlen,
    720 				   char **output, unsigned *outputlen))
    721 {
    722     char *tmp = NULL;
    723     unsigned tmplen = 0;
    724     int ret;
    725 
    726     *outputlen = 0;
    727 
    728     while (inputlen!=0)
    729     {
    730 	/* no need to free tmp */
    731       ret = decode_pkt(context, &input, &inputlen, &tmp, &tmplen);
    732 
    733       if(ret != SASL_OK) return ret;
    734 
    735       if (tmp!=NULL) /* if received 2 packets merge them together */
    736       {
    737 	  ret = _plug_buf_alloc(utils, output, outputsize,
    738 				*outputlen + tmplen + 1);
    739 	  if(ret != SASL_OK) return ret;
    740 
    741 	  memcpy(*output + *outputlen, tmp, tmplen);
    742 
    743 	  /* Protect stupid clients */
    744 	  *(*output + *outputlen + tmplen) = '\0';
    745 
    746 	  *outputlen+=tmplen;
    747       }
    748     }
    749 
    750     return SASL_OK;
    751 }
    752 
    753 /* returns the realm we should pretend to be in */
    754 int _plug_parseuser(const sasl_utils_t *utils,
    755 		    char **user, char **realm, const char *user_realm,
    756 		    const char *serverFQDN, const char *input)
    757 {
    758     int ret;
    759 #ifdef _SUN_SDK_
    760     const char *r;
    761 #else
    762     char *r;
    763 #endif /* _SUN_SDK_ */
    764 
    765     if(!user || !serverFQDN) {
    766 	PARAMERROR( utils );
    767 	return SASL_BADPARAM;
    768     }
    769 
    770     r = strchr(input, '@');
    771     if (!r) {
    772 	/* hmmm, the user didn't specify a realm */
    773 	if(user_realm && user_realm[0]) {
    774 	    ret = _plug_strdup(utils, user_realm, realm, NULL);
    775 	} else {
    776 	    /* Default to serverFQDN */
    777 	    ret = _plug_strdup(utils, serverFQDN, realm, NULL);
    778 	}
    779 
    780 	if (ret == SASL_OK) {
    781 	    ret = _plug_strdup(utils, input, user, NULL);
    782 	}
    783     } else {
    784 	r++;
    785 	ret = _plug_strdup(utils, r, realm, NULL);
    786 #ifdef _SUN_SDK_
    787 	if (ret == SASL_OK) {
    788 	    *user = utils->malloc(r - input);
    789 	    if (*user) {
    790 		memcpy(*user, input, r - input - 1);
    791 		(*user)[r - input - 1] = '\0';
    792 	    } else {
    793 		MEMERROR( utils );
    794 		ret = SASL_NOMEM;
    795 	    }
    796 	}
    797 #else
    798 	*--r = '\0';
    799 	*user = utils->malloc(r - input + 1);
    800 	if (*user) {
    801 	    strncpy(*user, input, r - input +1);
    802 	} else {
    803 	    MEMERROR( utils );
    804 	    ret = SASL_NOMEM;
    805 	}
    806 	*r = '@';
    807 #endif /* _SUN_SDK_ */
    808     }
    809 
    810     return ret;
    811 }
    812 
    813 #ifdef _INTEGRATED_SOLARIS_
    814 int
    815 use_locale(const char *lang_list, int is_client)
    816 {
    817     const char *s;
    818     const char *begin;
    819     const char *end;
    820     const char *i_default = "i-default";
    821     const int i_default_len = 9;
    822 
    823     if (lang_list == NULL)
    824 	return is_client;
    825 
    826     begin = lang_list;
    827 
    828     for (;;) {
    829 	/* skip over leading whitespace and commas */
    830 	while (isspace(*begin) || *begin == ',')
    831 	    begin++;
    832 	if (*begin == '\0')
    833 	    break;
    834 
    835 	/* Find the end of the language tag */
    836 	for (end = begin; end[1] != ',' && end[1] != '\0'; end++) {}
    837 
    838 	for (s = end; isspace(*s); s--) {}
    839 
    840 	if (s == begin && *begin == '*')
    841 	    return 1;
    842 
    843 	if (s - begin == (i_default_len - 1) &&
    844 		strncasecmp(begin, i_default, i_default_len) == 0)
    845 	    return 0;
    846 
    847 	begin = end + 1;
    848     }
    849 
    850     return is_client;
    851 }
    852 
    853 typedef struct prompt_list {
    854     char *prompt;
    855     struct prompt_list *next;
    856 } prompt_list;
    857 
    858 const char *
    859 convert_prompt(const sasl_utils_t *utils, void **h, const char *s)
    860 {
    861     sasl_getsimple_t *simple_cb;
    862     void *simple_context;
    863     const char *result = NULL;
    864     const char *s_locale;
    865     int ret;
    866     char *buf;
    867     const char *ret_buf;
    868     prompt_list *list;
    869     prompt_list *next;
    870 
    871     if (utils == NULL || utils->conn == NULL)
    872 	return s;
    873 
    874     if (s == NULL) {
    875 	for (list = (prompt_list *)*h; list != NULL; list = next) {
    876 	    if (list->prompt)
    877 		utils->free(list->prompt);
    878 	    next = list->next;
    879 	    utils->free(list);
    880 	}
    881 	*h = NULL;
    882 	return NULL;
    883     }
    884 
    885     ret = utils->getcallback(utils->conn, SASL_CB_LANGUAGE, &simple_cb,
    886 	&simple_context);
    887 
    888     if (ret == SASL_OK && simple_cb) {
    889 	ret = simple_cb(simple_context, SASL_CB_LANGUAGE, &result, NULL);
    890     } else
    891 	ret = SASL_FAIL;
    892     if (ret == SASL_OK && !use_locale(result, 1))
    893 	return s;
    894 
    895     s_locale = dgettext(TEXT_DOMAIN, s);
    896     if (s == s_locale) {
    897 	return s;
    898     }
    899 
    900     buf = local_to_utf(utils, s_locale);
    901 
    902     if (buf != NULL) {
    903 	list = utils->malloc(sizeof (prompt_list));
    904 	if (list == NULL) {
    905 	    utils->free(buf);
    906 	    buf = NULL;
    907 	} else {
    908 	    list->prompt = buf;
    909 	    list->next = *h;
    910 	    *h = list;
    911 	}
    912     }
    913 
    914     ret_buf = (buf == NULL) ? s : buf;
    915 
    916     return ret_buf;
    917 }
    918 
    919 #include <iconv.h>
    920 #include <langinfo.h>
    921 
    922 /*
    923  * local_to_utf converts a string in the current codeset to utf-8.
    924  * If no codeset is specified, then codeset 646 will be used.
    925  * Upon successful completion, this function will return a non-NULL buffer
    926  * that is allocated by local_to_utf.
    927  *
    928  * If utils is NULL, local_to_utf will use the standard memory allocation
    929  * functions, otherwise the memory functions defined in sasl_utils_t will
    930  * be used.
    931  *
    932  * local_to_utf will return NULL in the case of any error
    933  */
    934 char *
    935 local_to_utf(const sasl_utils_t *utils, const char *s)
    936 {
    937 	const char *code_set = nl_langinfo(CODESET);
    938 	iconv_t cd;
    939 	char *buf, *tmp;
    940 	size_t in_len;
    941 	size_t buf_size;
    942 	size_t ileft, oleft;
    943 	const char *inptr;
    944 	char *outptr;
    945 	size_t ret;
    946 
    947 	if (s == NULL)
    948 	    return NULL;
    949 
    950 	if (code_set == NULL)
    951 	    code_set = "646";
    952 
    953 	if (strcasecmp(code_set, "UTF-8") == 0) {
    954 	    if (utils == NULL)
    955 		buf = strdup(s);
    956 	    else {
    957 		if (_plug_strdup(utils, s, &buf, NULL) != SASL_OK)
    958 			buf = NULL;
    959 	    }
    960 	    return buf;
    961 	}
    962 	cd = iconv_open("UTF-8", code_set);
    963 	if (cd == (iconv_t)-1)
    964 	    return NULL;
    965 
    966 	in_len = strlen(s);
    967 	buf_size = 4 * (in_len + 1);	/* guess */
    968 
    969 	if (utils == NULL)
    970 	    buf = malloc(buf_size);
    971 	else
    972 	    buf = utils->malloc(buf_size);
    973 
    974 	if (buf == NULL) {
    975 	    (void) iconv_close(cd);
    976 	    return NULL;
    977 	}
    978 	inptr = s;
    979 	ileft = in_len;
    980 	outptr = buf;
    981 	oleft = buf_size;
    982 	for (;;) {
    983 	    ret = iconv(cd, &inptr, &ileft, &outptr, &oleft);
    984 	    if (ret == (size_t)(-1)) {
    985 		if (errno == E2BIG) {
    986 		    oleft += buf_size;
    987 		    buf_size *= 2;
    988 		    if (utils == NULL)
    989 			tmp = realloc(buf, buf_size);
    990 		    else
    991 			tmp = utils->realloc(buf, buf_size);
    992 		    if (tmp == NULL) {
    993 			oleft = (size_t)(-1);
    994 			break;
    995 		    }
    996 		    outptr = tmp + (outptr-buf);
    997 		    buf = tmp;
    998 		    continue;
    999 		}
   1000 		oleft = (size_t)(-1);
   1001 		break;
   1002 	    }
   1003 	    if (inptr == NULL)
   1004 		break;
   1005 	    inptr = NULL;
   1006 	    ileft = 0;
   1007 	}
   1008 	if (oleft > 0) {
   1009 	    *outptr = '\0';
   1010 	} else if (oleft != (size_t)(-1)) {
   1011 	    if (utils == NULL)
   1012 		tmp = realloc(buf, buf_size + 1);
   1013 	    else
   1014 		tmp = utils->realloc(buf, buf_size + 1);
   1015 	    if (tmp == NULL) {
   1016 		oleft = (size_t)(-1);
   1017 	    } else {
   1018 		buf = tmp;
   1019 		buf[buf_size] = '\0';
   1020 	    }
   1021 	}
   1022 	if (oleft == (size_t)(-1)) {
   1023 	    if (utils == NULL)
   1024 		free(buf);
   1025 	    else
   1026 		utils->free(buf);
   1027 	    buf = NULL;
   1028 	}
   1029 
   1030 	(void) iconv_close(cd);
   1031 	return buf;
   1032 }
   1033 #endif /* _INTEGRATED_SOLARIS_ */
   1034