GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / plugins / plugin_common.c
1 /* Generic SASL plugin utility functions
2  * Rob Siemborski
3  * $Id: plugin_common.c,v 1.20 2004/06/23 18:43:37 rjs3 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 */
76 static 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
112 int _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 = 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
169 int _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 */
216 int _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         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 = needed;
245     } 
246
247     return SASL_OK;
248 }
249
250 /* copy a string */
251 int _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 = len;
271
272   return SASL_OK;
273 }
274
275 void _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, len);
284   utils->free(*str);
285
286   *str=NULL;
287 }
288
289 void _plug_free_secret(const sasl_utils_t *utils, sasl_secret_t **secret) 
290 {
291     if(!utils || !secret || !(*secret)) return;
292
293     utils->erasebuffer((*secret)->data, (*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  */
302 sasl_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  */
320 int _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  */
368 int _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  */
427 int _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  */
474 int _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  */
519 int _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
608 void _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  */
622 int _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
712 void _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 */
718 int _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
760 int _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
783 char * _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
820 void _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
906 SKIP_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)
918 unsigned int plug_sleep (unsigned int seconds)
919 {
920     long dwSec = seconds*1000;
921     Sleep (dwSec);
922     return 0;
923 }
924 #endif