1 /* Generic SASL plugin utility functions
3 * $Id: plugin_common.c,v 1.20 2004/06/23 18:43:37 rjs3 Exp $
6 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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
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
27 * Pittsburgh, PA 15213-3890
28 * (412) 268-4387, fax: (412) 268-7395
29 * tech-transfer@andrew.cmu.edu
31 * 4. Redistributions of any form whatsoever must retain the following
33 * "This product includes software developed by Computing Services
34 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
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.
48 # include <winsock2.h>
50 # include <sys/socket.h>
51 # include <netinet/in.h>
52 # include <arpa/inet.h>
54 # include <sys/utsname.h>
56 #endif /* macintosh */
69 #ifdef HAVE_INTTYPES_H
73 #include "plugin_common.h"
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
80 struct sockaddr *sa __attribute__((unused)),
81 socklen_t *len __attribute__((unused))
85 #ifdef IN6_IS_ADDR_V4MAPPED
86 struct sockaddr_in6 *sin6;
87 struct sockaddr_in *sin4;
91 if (sa->sa_family != AF_INET6)
93 sin6 = (struct sockaddr_in6 *)sa;
94 if (!IN6_IS_ADDR_V4MAPPED((&sin6->sin6_addr)))
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);
106 *len = sizeof(struct sockaddr_in);
112 int _plug_ipfromstring(const sasl_utils_t *utils, const char *addr,
113 struct sockaddr *out, socklen_t outlen)
117 struct sockaddr_storage ss;
118 struct addrinfo hints, *ai = NULL;
119 char hbuf[NI_MAXHOST];
121 if(!utils || !addr || !out) {
122 if(utils) PARAMERROR( utils );
123 return SASL_BADPARAM;
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;
138 /* XXX/FIXME: Do we need this check? */
139 for (j = i; addr[j] != '\0'; j++)
140 if (!isdigit((int)(addr[j]))) {
142 return SASL_BADPARAM;
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;
150 if (getaddrinfo(hbuf, &addr[i], &hints, &ai) != 0) {
152 return SASL_BADPARAM;
155 len = ai->ai_addrlen;
156 memcpy(&ss, ai->ai_addr, len);
158 sockaddr_unmapped((struct sockaddr *)&ss, &len);
164 memcpy(out, &ss, len);
169 int _plug_iovec_to_buf(const sasl_utils_t *utils, const struct iovec *vec,
170 unsigned numiov, buffer_info_t **output)
177 if(!utils || !vec || !output) {
178 if(utils) PARAMERROR( utils );
179 return SASL_BADPARAM;
183 *output = utils->malloc(sizeof(buffer_info_t));
188 memset(*output,0,sizeof(buffer_info_t));
194 for(i=0; i<numiov; i++)
195 out->curlen += vec[i].iov_len;
197 ret = _plug_buf_alloc(utils, &out->data, &out->reallen, out->curlen);
204 memset(out->data, 0, out->reallen);
207 for(i=0; i<numiov; i++) {
208 memcpy(pos, vec[i].iov_base, vec[i].iov_len);
209 pos += vec[i].iov_len;
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)
219 if(!utils || !rwbuf || !curlen) {
221 return SASL_BADPARAM;
225 *rwbuf = utils->malloc(newlen);
226 if (*rwbuf == NULL) {
232 } else if(*rwbuf && *curlen < newlen) {
233 size_t needed = 2*(*curlen);
235 while(needed < newlen)
238 *rwbuf = utils->realloc(*rwbuf, needed);
239 if (*rwbuf == NULL) {
251 int _plug_strdup(const sasl_utils_t * utils, const char *in,
252 char **out, int *outlen)
254 size_t len = strlen(in);
256 if(!utils || !in || !out) {
257 if(utils) PARAMERROR(utils);
258 return SASL_BADPARAM;
261 *out = utils->malloc(len + 1);
267 strcpy((char *) *out, in);
275 void _plug_free_string(const sasl_utils_t *utils, char **str)
279 if (!utils || !str || !(*str)) return;
283 utils->erasebuffer(*str, len);
289 void _plug_free_secret(const sasl_utils_t *utils, sasl_secret_t **secret)
291 if(!utils || !secret || !(*secret)) return;
293 utils->erasebuffer((*secret)->data, (*secret)->len);
294 utils->free(*secret);
299 * Trys to find the prompt with the lookingfor id in the prompt list
300 * Returns it if found. NULL otherwise
302 sasl_interact_t *_plug_find_prompt(sasl_interact_t **promptlist,
303 unsigned int lookingfor)
305 sasl_interact_t *prompt;
307 if (promptlist && *promptlist) {
308 for (prompt = *promptlist; prompt->id != SASL_CB_LIST_END; ++prompt) {
309 if (prompt->id==lookingfor)
318 * Retrieve the simple string given by the callback id.
320 int _plug_get_simple(const sasl_utils_t *utils, unsigned int id, int required,
321 const char **result, sasl_interact_t **prompt_need)
325 sasl_getsimple_t *simple_cb;
326 void *simple_context;
327 sasl_interact_t *prompt;
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.*/
336 if (required && !prompt->result) {
337 SETERROR(utils, "Unexpectedly missing a prompt result");
338 return SASL_BADPARAM;
341 *result = prompt->result;
345 /* Try to get the callback... */
346 ret = utils->getcallback(utils->conn, id, &simple_cb, &simple_context);
348 if (ret == SASL_FAIL && !required)
351 if (ret == SASL_OK && simple_cb) {
352 ret = simple_cb(simple_context, id, result, NULL);
356 if (required && !*result) {
358 return SASL_BADPARAM;
366 * Retrieve the user password.
368 int _plug_get_password(const sasl_utils_t *utils, sasl_secret_t **password,
369 unsigned int *iscopy, sasl_interact_t **prompt_need)
372 sasl_getsecret_t *pass_cb;
374 sasl_interact_t *prompt;
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.*/
384 if (!prompt->result) {
385 SETERROR(utils, "Unexpectedly missing a prompt result");
386 return SASL_BADPARAM;
389 /* copy what we got into a secret_t */
390 *password = (sasl_secret_t *) utils->malloc(sizeof(sasl_secret_t) +
397 (*password)->len=prompt->len;
398 memcpy((*password)->data, prompt->result, prompt->len);
399 (*password)->data[(*password)->len]=0;
406 /* Try to get the callback... */
407 ret = utils->getcallback(utils->conn, SASL_CB_PASS,
408 &pass_cb, &pass_context);
410 if (ret == SASL_OK && pass_cb) {
411 ret = pass_cb(utils->conn, pass_context, SASL_CB_PASS, password);
417 return SASL_BADPARAM;
425 * Retrieve the string given by the challenge prompt id.
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)
432 sasl_chalprompt_t *chalprompt_cb;
433 void *chalprompt_context;
434 sasl_interact_t *prompt;
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.*/
443 if (!prompt->result) {
444 SETERROR(utils, "Unexpectedly missing a prompt result");
445 return SASL_BADPARAM;
448 *result = prompt->result;
452 /* Try to get the callback... */
453 ret = utils->getcallback(utils->conn, id,
454 &chalprompt_cb, &chalprompt_context);
456 if (ret == SASL_OK && chalprompt_cb) {
457 ret = chalprompt_cb(chalprompt_context, id,
458 challenge, promptstr, NULL, result, NULL);
464 return SASL_BADPARAM;
472 * Retrieve the client realm.
474 int _plug_get_realm(const sasl_utils_t *utils, const char **availrealms,
475 const char **realm, sasl_interact_t **prompt_need)
478 sasl_getrealm_t *realm_cb;
480 sasl_interact_t *prompt;
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.*/
489 if (!prompt->result) {
490 SETERROR(utils, "Unexpectedly missing a prompt result");
491 return SASL_BADPARAM;
494 *realm = prompt->result;
498 /* Try to get the callback... */
499 ret = utils->getcallback(utils->conn, SASL_CB_GETREALM,
500 &realm_cb, &realm_context);
502 if (ret == SASL_OK && realm_cb) {
503 ret = realm_cb(realm_context, SASL_CB_GETREALM, availrealms, realm);
509 return SASL_BADPARAM;
517 * Make the requested prompts. (prompt==NULL means we don't want it)
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)
531 sasl_interact_t *prompts;
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++;
540 SETERROR( utils, "make_prompts() called with no actual prompts" );
544 alloc_size = sizeof(sasl_interact_t)*num;
545 prompts = utils->malloc(alloc_size);
550 memset(prompts, 0, alloc_size);
552 *prompts_res = prompts;
555 (prompts)->id = SASL_CB_USER;
556 (prompts)->challenge = "Authorization Name";
557 (prompts)->prompt = user_prompt;
558 (prompts)->defresult = user_def;
564 (prompts)->id = SASL_CB_AUTHNAME;
565 (prompts)->challenge = "Authentication Name";
566 (prompts)->prompt = auth_prompt;
567 (prompts)->defresult = auth_def;
573 (prompts)->id = SASL_CB_PASS;
574 (prompts)->challenge = "Password";
575 (prompts)->prompt = pass_prompt;
576 (prompts)->defresult = pass_def;
582 (prompts)->id = SASL_CB_ECHOPROMPT;
583 (prompts)->challenge = echo_chal;
584 (prompts)->prompt = echo_prompt;
585 (prompts)->defresult = echo_def;
591 (prompts)->id = SASL_CB_GETREALM;
592 (prompts)->challenge = realm_chal;
593 (prompts)->prompt = realm_prompt;
594 (prompts)->defresult = realm_def;
599 /* add the ending one */
600 (prompts)->id = SASL_CB_LIST_END;
601 (prompts)->challenge = NULL;
602 (prompts)->prompt = NULL;
603 (prompts)->defresult = NULL;
608 void _plug_decode_init(decode_context_t *text,
609 const sasl_utils_t *utils, unsigned int in_maxbuf)
611 memset(text, 0, sizeof(decode_context_t));
615 text->in_maxbuf = in_maxbuf;
619 * Decode as much of the input as possible (possibly none),
620 * using decode_pkt() to decode individual packets.
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),
640 while (inputlen) { /* more input */
641 if (text->needsize) { /* need to get the rest of the 4-byte size */
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;
651 if (!text->needsize) { /* we have the entire 4-byte size */
652 memcpy(&(text->size), text->sizebuf, 4);
653 text->size = ntohl(text->size);
655 if (!text->size) /* should never happen */
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);
666 text->buffer = text->utils->malloc(text->in_maxbuf);
667 if (text->buffer == NULL) return SASL_NOMEM;
671 /* We do NOT have the entire 4-byte size...
672 * wait for more data */
677 diff = text->size - text->cursize; /* bytes needed for full packet */
679 if (inputlen < diff) { /* not a complete packet, need more input */
680 memcpy(text->buffer + text->cursize, input, inputlen);
681 text->cursize += inputlen;
685 /* copy the rest of the packet */
686 memcpy(text->buffer + text->cursize, input, diff);
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;
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;
699 memcpy(*output + *outputlen, tmp, tmplen);
700 *outputlen += tmplen;
702 /* protect stupid clients */
703 *(*output + *outputlen) = '\0';
705 /* reset for the next packet */
712 void _plug_decode_free(decode_context_t *text)
714 if (text->buffer) text->utils->free(text->buffer);
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)
725 if(!user || !serverFQDN) {
727 return SASL_BADPARAM;
730 r = strchr(input, '@');
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);
736 /* Default to serverFQDN */
737 ret = _plug_strdup(utils, serverFQDN, realm, NULL);
740 if (ret == SASL_OK) {
741 ret = _plug_strdup(utils, input, user, NULL);
745 ret = _plug_strdup(utils, r, realm, NULL);
747 *user = utils->malloc(r - input + 1);
749 strncpy(*user, input, r - input +1);
760 int _plug_make_fulluser(const sasl_utils_t *utils,
762 const char * useronly,
765 if(!fulluser || !useronly || !realm) {
767 return (SASL_BADPARAM);
770 *fulluser = utils->malloc (strlen(useronly) + strlen(realm) + 2);
771 if (*fulluser == NULL) {
776 strcpy (*fulluser, useronly);
777 strcat (*fulluser, "@");
778 strcat (*fulluser, realm);
783 char * _plug_get_error_message (const sasl_utils_t *utils,
796 FORMAT_MESSAGE_ALLOCATE_BUFFER |
797 FORMAT_MESSAGE_FROM_SYSTEM |
798 FORMAT_MESSAGE_IGNORE_INSERTS,
801 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
807 if (_plug_strdup (utils, lpMsgBuf, &return_value, NULL) != SASL_OK) {
811 LocalFree( lpMsgBuf );
813 if (_plug_strdup (utils, strerror(error), &return_value, NULL) != SASL_OK) {
817 return (return_value);
820 void _plug_snprintf_os_info (char * osbuf, int osbuf_len)
823 OSVERSIONINFOEX versioninfo;
827 DWORD dwOSVersionInfoSize;
828 DWORD dwMajorVersion;
829 DWORD dwMinorVersion;
831 TCHAR szCSDVersion[ 128 ];
832 //Only NT SP 6 and later
833 WORD wServicePackMajor;
834 WORD wServicePackMinor;
839 versioninfo.dwOSVersionInfoSize = sizeof (versioninfo);
840 sysname = "Unknown Windows";
842 if (GetVersionEx ((OSVERSIONINFO *) &versioninfo) == FALSE) {
843 snprintf(osbuf, osbuf_len, "%s", sysname);
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 */
853 case VER_PLATFORM_WIN32_WINDOWS: /* 95/98/ME */
854 switch (versioninfo.dwMinorVersion) {
856 sysname = "Windows 95";
859 sysname = "Windows 98";
862 sysname = "Windows Me";
865 sysname = "Unknown Windows 9X/ME series";
868 /* Clear the high order word, as it contains major/minor version */
869 versioninfo.dwBuildNumber &= 0xFFFF;
872 case VER_PLATFORM_WIN32_NT: /* NT/2000/XP/.NET */
873 if (versioninfo.dwMinorVersion > 99) {
875 switch (versioninfo.dwMajorVersion * 100 + versioninfo.dwMinorVersion) {
877 sysname = "Windows NT 3.51";
880 sysname = "Windows NT 4.0";
883 sysname = "Windows 2000";
886 sysname = "Windows XP/.NET"; /* or Windows .NET Server */
889 sysname = "Unknown Windows NT series";
899 snprintf(osbuf, osbuf_len,
902 versioninfo.szCSDVersion,
903 versioninfo.dwBuildNumber
913 snprintf(osbuf, osbuf_len, "%s %s", os.sysname, os.release);
918 unsigned int plug_sleep (unsigned int seconds)
920 long dwSec = seconds*1000;