1/* Generic SASL plugin utility functions
2 * Rob Siemborski
3 * $Id: plugin_common.c,v 1.22 2011/09/01 14:12:18 mel 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 = 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    /* XXX/FIXME: 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 = (socklen_t) 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    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 += 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, char **rwbuf,
217		    unsigned *curlen, unsigned 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	unsigned 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 = 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, (sasl_callback_ft *)&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			     (sasl_callback_ft *)&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			     (sasl_callback_ft *)&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			     (sasl_callback_ft *)&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 = 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    text->in_maxbuf = in_maxbuf;
616}
617
618/*
619 * Decode as much of the input as possible (possibly none),
620 * using decode_pkt() to decode individual packets.
621 */
622int _plug_decode(decode_context_t *text,
623		 const char *input, unsigned inputlen,
624		 char **output,		/* output buffer */
625		 unsigned *outputsize,	/* current size of output buffer */
626		 unsigned *outputlen,	/* length of data in output buffer */
627		 int (*decode_pkt)(void *rock,
628				   const char *input, unsigned inputlen,
629				   char **output, unsigned *outputlen),
630		 void *rock)
631{
632    unsigned int tocopy;
633    unsigned diff;
634    char *tmp;
635    unsigned tmplen;
636    int ret;
637
638    *outputlen = 0;
639
640    while (inputlen) { /* more input */
641	if (text->needsize) { /* need to get the rest of the 4-byte size */
642
643	    /* copy as many bytes (up to 4) as we have into size buffer */
644	    tocopy = (inputlen > text->needsize) ? text->needsize : inputlen;
645	    memcpy(text->sizebuf + 4 - text->needsize, input, tocopy);
646	    text->needsize -= tocopy;
647
648	    input += tocopy;
649	    inputlen -= tocopy;
650
651	    if (!text->needsize) { /* we have the entire 4-byte size */
652		memcpy(&(text->size), text->sizebuf, 4);
653		text->size = ntohl(text->size);
654
655		if (!text->size) /* should never happen */
656		    return SASL_FAIL;
657
658		if (text->size > text->in_maxbuf) {
659		    text->utils->log(NULL, SASL_LOG_ERR,
660				     "encoded packet size too big (%d > %d)",
661				     text->size, text->in_maxbuf);
662		    return SASL_FAIL;
663		}
664
665		if (!text->buffer)
666		    text->buffer = text->utils->malloc(text->in_maxbuf);
667		if (text->buffer == NULL) return SASL_NOMEM;
668
669		text->cursize = 0;
670	    } else {
671		/* We do NOT have the entire 4-byte size...
672		 * wait for more data */
673		return SASL_OK;
674	    }
675	}
676
677	diff = text->size - text->cursize; /* bytes needed for full packet */
678
679	if (inputlen < diff) {	/* not a complete packet, need more input */
680	    memcpy(text->buffer + text->cursize, input, inputlen);
681	    text->cursize += inputlen;
682	    return SASL_OK;
683	}
684
685	/* copy the rest of the packet */
686	memcpy(text->buffer + text->cursize, input, diff);
687	input += diff;
688	inputlen -= diff;
689
690	/* decode the packet (no need to free tmp) */
691	ret = decode_pkt(rock, text->buffer, text->size, &tmp, &tmplen);
692	if (ret != SASL_OK) return ret;
693
694	/* append the decoded packet to the output */
695	ret = _plug_buf_alloc(text->utils, output, outputsize,
696			      *outputlen + tmplen + 1); /* +1 for NUL */
697	if (ret != SASL_OK) return ret;
698
699	memcpy(*output + *outputlen, tmp, tmplen);
700	*outputlen += tmplen;
701
702	/* protect stupid clients */
703	*(*output + *outputlen) = '\0';
704
705	/* reset for the next packet */
706	text->needsize = 4;
707    }
708
709    return SASL_OK;
710}
711
712void _plug_decode_free(decode_context_t *text)
713{
714    if (text->buffer) text->utils->free(text->buffer);
715}
716
717/* returns the realm we should pretend to be in */
718int _plug_parseuser(const sasl_utils_t *utils,
719		    char **user, char **realm, const char *user_realm,
720		    const char *serverFQDN, const char *input)
721{
722    int ret;
723    char *r;
724
725    if(!user || !serverFQDN) {
726	PARAMERROR( utils );
727	return SASL_BADPARAM;
728    }
729
730    r = strchr(input, '@');
731    if (!r) {
732	/* hmmm, the user didn't specify a realm */
733	if(user_realm && user_realm[0]) {
734	    ret = _plug_strdup(utils, user_realm, realm, NULL);
735	} else {
736	    /* Default to serverFQDN */
737	    ret = _plug_strdup(utils, serverFQDN, realm, NULL);
738	}
739
740	if (ret == SASL_OK) {
741	    ret = _plug_strdup(utils, input, user, NULL);
742	}
743    } else {
744	r++;
745	ret = _plug_strdup(utils, r, realm, NULL);
746	*--r = '\0';
747	*user = utils->malloc(r - input + 1);
748	if (*user) {
749	    strncpy(*user, input, r - input +1);
750	} else {
751	    MEMERROR( utils );
752	    ret = SASL_NOMEM;
753	}
754	*r = '@';
755    }
756
757    return ret;
758}
759
760int _plug_make_fulluser(const sasl_utils_t *utils,
761			char **fulluser,
762			const char * useronly,
763			const char *realm)
764{
765    if(!fulluser || !useronly || !realm) {
766	PARAMERROR( utils );
767	return (SASL_BADPARAM);
768    }
769
770    *fulluser = utils->malloc (strlen(useronly) + strlen(realm) + 2);
771    if (*fulluser == NULL) {
772	MEMERROR( utils );
773	return (SASL_NOMEM);
774    }
775
776    strcpy (*fulluser, useronly);
777    strcat (*fulluser, "@");
778    strcat (*fulluser, realm);
779
780    return (SASL_OK);
781}
782
783char * _plug_get_error_message (const sasl_utils_t *utils,
784#ifdef WIN32
785				DWORD error
786#else
787				int error
788#endif
789				)
790{
791    char * return_value;
792#ifdef WIN32
793    LPVOID lpMsgBuf;
794
795    FormatMessage(
796	FORMAT_MESSAGE_ALLOCATE_BUFFER |
797	FORMAT_MESSAGE_FROM_SYSTEM |
798	FORMAT_MESSAGE_IGNORE_INSERTS,
799	NULL,
800	error,
801	MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
802	(LPTSTR) &lpMsgBuf,
803	0,
804	NULL
805    );
806
807    if (_plug_strdup (utils, lpMsgBuf, &return_value, NULL) != SASL_OK) {
808	return_value = NULL;
809    }
810
811    LocalFree( lpMsgBuf );
812#else /* !WIN32 */
813    if (_plug_strdup (utils, strerror(error), &return_value, NULL) != SASL_OK) {
814	return_value = NULL;
815    }
816#endif /* WIN32 */
817    return (return_value);
818}
819
820void _plug_snprintf_os_info (char * osbuf, int osbuf_len)
821{
822#ifdef WIN32
823    OSVERSIONINFOEX versioninfo;
824    char *sysname;
825
826/* :
827  DWORD dwOSVersionInfoSize;
828  DWORD dwMajorVersion;
829  DWORD dwMinorVersion;
830  DWORD dwBuildNumber;
831  TCHAR szCSDVersion[ 128 ];
832//Only NT SP 6 and later
833  WORD wServicePackMajor;
834  WORD wServicePackMinor;
835  WORD wSuiteMask;
836  BYTE wProductType;
837 */
838
839    versioninfo.dwOSVersionInfoSize = sizeof (versioninfo);
840    sysname = "Unknown Windows";
841
842    if (GetVersionEx ((OSVERSIONINFO *) &versioninfo) == FALSE) {
843	snprintf(osbuf, osbuf_len, "%s", sysname);
844	goto SKIP_OS_INFO;
845    }
846
847    switch (versioninfo.dwPlatformId) {
848	case VER_PLATFORM_WIN32s: /* Win32s on Windows 3.1 */
849	    sysname = "Win32s on Windows 3.1";
850/* I can't test if dwBuildNumber has any meaning on Win32s */
851	    break;
852
853	case VER_PLATFORM_WIN32_WINDOWS: /* 95/98/ME */
854	    switch (versioninfo.dwMinorVersion) {
855		case 0:
856		    sysname = "Windows 95";
857		    break;
858		case 10:
859		    sysname = "Windows 98";
860		    break;
861		case 90:
862		    sysname = "Windows Me";
863		    break;
864		default:
865		    sysname = "Unknown Windows 9X/ME series";
866		    break;
867	    }
868/* Clear the high order word, as it contains major/minor version */
869	    versioninfo.dwBuildNumber &= 0xFFFF;
870	    break;
871
872	case VER_PLATFORM_WIN32_NT: /* NT/2000/XP/.NET */
873	    if (versioninfo.dwMinorVersion > 99) {
874	    } else {
875		switch (versioninfo.dwMajorVersion * 100 + versioninfo.dwMinorVersion) {
876		    case 351:
877			sysname = "Windows NT 3.51";
878			break;
879		    case 400:
880			sysname = "Windows NT 4.0";
881			break;
882		    case 500:
883			sysname = "Windows 2000";
884			break;
885		    case 501:
886			sysname = "Windows XP/.NET"; /* or Windows .NET Server */
887			break;
888		    default:
889			sysname = "Unknown Windows NT series";
890			break;
891		}
892	    }
893	    break;
894
895	default:
896	    break;
897    }
898
899    snprintf(osbuf, osbuf_len,
900	     "%s %s (Build %u)",
901	     sysname,
902	     versioninfo.szCSDVersion,
903	     versioninfo.dwBuildNumber
904	     );
905
906SKIP_OS_INFO:
907    ;
908
909#else /* !WIN32 */
910    struct utsname os;
911
912    uname(&os);
913    snprintf(osbuf, osbuf_len, "%s %s", os.sysname, os.release);
914#endif /* WIN32 */
915}
916
917#if defined(WIN32)
918unsigned int plug_sleep (unsigned int seconds)
919{
920    long dwSec = seconds*1000;
921    Sleep (dwSec);
922    return 0;
923}
924#endif
925