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