xref: /titanic_41/usr/src/lib/libsasl/plugin/plugin_common.c (revision 02d09e03eb27f3a2dc299de704e45dae5173f43f)
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