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@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