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