2 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
5 #pragma ident "%Z%%M% %I% %E% SMI"
7 /* Generic SASL plugin utility functions
9 * $Id: plugin_common.c,v 1.13 2003/02/13 19:56:05 rjs3 Exp $
12 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in
23 * the documentation and/or other materials provided with the
26 * 3. The name "Carnegie Mellon University" must not be used to
27 * endorse or promote products derived from this software without
28 * prior written permission. For permission or any other legal
29 * details, please contact
30 * Office of Technology Transfer
31 * Carnegie Mellon University
33 * Pittsburgh, PA 15213-3890
34 * (412) 268-4387, fax: (412) 268-7395
35 * tech-transfer@andrew.cmu.edu
37 * 4. Redistributions of any form whatsoever must retain the following
39 * "This product includes software developed by Computing Services
40 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
42 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
43 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
44 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
45 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
46 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
47 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
48 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
56 # include <sys/socket.h>
57 # include <netinet/in.h>
58 # include <arpa/inet.h>
61 #endif /* macintosh */
73 #ifdef HAVE_INTTYPES_H
77 #include "plugin_common.h"
79 /* translate IPv4 mapped IPv6 address to IPv4 address */
80 static void sockaddr_unmapped(
81 #ifdef IN6_IS_ADDR_V4MAPPED
82 struct sockaddr
*sa
, socklen_t
*len
84 struct sockaddr
*sa
__attribute__((unused
)),
85 socklen_t
*len
__attribute__((unused
))
89 #ifdef IN6_IS_ADDR_V4MAPPED
90 struct sockaddr_in6
*sin6
;
91 struct sockaddr_in
*sin4
;
97 #endif /* _SUN_SDK_ */
99 if (sa
->sa_family
!= AF_INET6
)
101 /* LINTED pointer alignment */
102 sin6
= (struct sockaddr_in6
*)sa
;
103 if (!IN6_IS_ADDR_V4MAPPED((&sin6
->sin6_addr
)))
105 /* LINTED pointer alignment */
106 sin4
= (struct sockaddr_in
*)sa
;
107 /* LINTED pointer alignment */
108 addr
= *(uint32_t *)&sin6
->sin6_addr
.s6_addr
[12];
109 port
= sin6
->sin6_port
;
110 memset(sin4
, 0, sizeof(struct sockaddr_in
));
111 sin4
->sin_addr
.s_addr
= addr
;
112 sin4
->sin_port
= port
;
113 sin4
->sin_family
= AF_INET
;
114 #ifdef HAVE_SOCKADDR_SA_LEN
115 sin4
->sin_len
= sizeof(struct sockaddr_in
);
117 *len
= sizeof(struct sockaddr_in
);
123 int _plug_ipfromstring(const sasl_utils_t
*utils
, const char *addr
,
124 struct sockaddr
*out
, socklen_t outlen
)
128 #ifdef WINNT /* _SUN_SDK_ */
129 struct sockaddr_in ss
;
131 struct sockaddr_storage ss
;
132 #endif /* _SUN_SDK_ */
133 struct addrinfo hints
, *ai
= NULL
;
134 char hbuf
[NI_MAXHOST
];
136 const char *start
, *end
, *p
;
137 #endif /* _SUN_SDK_ */
139 if(!utils
|| !addr
|| !out
) {
140 if(utils
) PARAMERROR( utils
);
141 return SASL_BADPARAM
;
145 end
= strchr(addr
, ']');
147 /* This an rfc 2732 ipv6 address */
148 start
= strchr(addr
, '[');
149 if (start
>= end
|| start
== NULL
) {
150 if(utils
) PARAMERROR( utils
);
151 return SASL_BADPARAM
;
153 for (i
= 0, p
= start
+ 1; p
< end
; p
++) {
158 p
= strchr(end
, ':');
164 for (i
= 0; addr
[i
] != '\0' && addr
[i
] != ';'; ) {
166 if (++i
>= NI_MAXHOST
)
174 if (i
>= NI_MAXHOST
) {
175 if(utils
) PARAMERROR( utils
);
176 return SASL_BADPARAM
;
179 for (j
= 0; p
[j
] != '\0'; j
++)
180 if (!isdigit((int)(p
[j
]))) {
182 return SASL_BADPARAM
;
185 /* Parse the address */
186 for (i
= 0; addr
[i
] != '\0' && addr
[i
] != ';'; i
++) {
187 if (i
>= NI_MAXHOST
) {
188 if(utils
) PARAMERROR( utils
);
189 return SASL_BADPARAM
;
197 /* XXX/FIXME: Do we need this check? */
198 for (j
= i
; addr
[j
] != '\0'; j
++)
199 if (!isdigit((int)(addr
[j
]))) {
201 return SASL_BADPARAM
;
203 #endif /* _SUN_SDK_ */
205 memset(&hints
, 0, sizeof(hints
));
206 hints
.ai_family
= PF_UNSPEC
;
207 hints
.ai_socktype
= SOCK_STREAM
;
208 hints
.ai_flags
= AI_PASSIVE
| AI_NUMERICHOST
;
211 if (getaddrinfo(hbuf
, p
, &hints
, &ai
) != 0) {
213 if (getaddrinfo(hbuf
, &addr
[i
], &hints
, &ai
) != 0) {
214 #endif /* _SUN_SDK_ */
216 return SASL_BADPARAM
;
219 len
= ai
->ai_addrlen
;
221 if (len
> sizeof(ss
))
222 return (SASL_BUFOVER
);
223 #endif /* _SUN_SDK_ */
224 memcpy(&ss
, ai
->ai_addr
, len
);
226 sockaddr_unmapped((struct sockaddr
*)&ss
, &len
);
232 memcpy(out
, &ss
, len
);
237 int _plug_iovec_to_buf(const sasl_utils_t
*utils
, const struct iovec
*vec
,
238 unsigned numiov
, buffer_info_t
**output
)
245 if(!utils
|| !vec
|| !output
) {
246 if(utils
) PARAMERROR( utils
);
247 return SASL_BADPARAM
;
251 *output
= utils
->malloc(sizeof(buffer_info_t
));
256 memset(*output
,0,sizeof(buffer_info_t
));
262 for(i
=0; i
<numiov
; i
++)
263 out
->curlen
+= vec
[i
].iov_len
;
265 ret
= _plug_buf_alloc(utils
, &out
->data
, &out
->reallen
, out
->curlen
);
272 memset(out
->data
, 0, out
->reallen
);
275 for(i
=0; i
<numiov
; i
++) {
276 memcpy(pos
, vec
[i
].iov_base
, vec
[i
].iov_len
);
277 pos
+= vec
[i
].iov_len
;
283 /* Basically a conditional call to realloc(), if we need more */
284 int _plug_buf_alloc(const sasl_utils_t
*utils
, char **rwbuf
,
285 unsigned *curlen
, unsigned newlen
)
287 if(!utils
|| !rwbuf
|| !curlen
) {
289 return SASL_BADPARAM
;
293 *rwbuf
= utils
->malloc(newlen
);
294 if (*rwbuf
== NULL
) {
300 } else if(*rwbuf
&& *curlen
< newlen
) {
302 unsigned needed
= 2*(*curlen
);
304 size_t needed
= 2*(*curlen
);
305 #endif /* _SUN_SDK_ */
307 while(needed
< newlen
)
310 *rwbuf
= utils
->realloc(*rwbuf
, needed
);
311 if (*rwbuf
== NULL
) {
323 int _plug_strdup(const sasl_utils_t
* utils
, const char *in
,
324 char **out
, int *outlen
)
329 size_t len
= strlen(in
);
330 #endif /* _SUN_SDK_ */
332 if(!utils
|| !in
|| !out
) {
333 if(utils
) PARAMERROR(utils
);
334 return SASL_BADPARAM
;
339 #endif /* _SUN_SDK_ */
340 *out
= utils
->malloc(len
+ 1);
346 strcpy((char *) *out
, in
);
354 void _plug_free_string(const sasl_utils_t
*utils
, char **str
)
358 if (!utils
|| !str
|| !(*str
)) return;
362 utils
->erasebuffer(*str
, len
);
368 void _plug_free_secret(const sasl_utils_t
*utils
, sasl_secret_t
**secret
)
370 if(!utils
|| !secret
|| !(*secret
)) return;
373 utils
->erasebuffer((char *)(*secret
)->data
, (*secret
)->len
);
375 utils
->erasebuffer((*secret
)->data
, (*secret
)->len
);
376 #endif /* _SUN_SDK_ */
377 utils
->free(*secret
);
382 * Trys to find the prompt with the lookingfor id in the prompt list
383 * Returns it if found. NULL otherwise
385 sasl_interact_t
*_plug_find_prompt(sasl_interact_t
**promptlist
,
386 unsigned int lookingfor
)
388 sasl_interact_t
*prompt
;
390 if (promptlist
&& *promptlist
) {
391 for (prompt
= *promptlist
; prompt
->id
!= SASL_CB_LIST_END
; ++prompt
) {
392 if (prompt
->id
==lookingfor
)
401 * Retrieve the simple string given by the callback id.
403 int _plug_get_simple(const sasl_utils_t
*utils
, unsigned int id
, int required
,
404 const char **result
, sasl_interact_t
**prompt_need
)
408 sasl_getsimple_t
*simple_cb
;
409 void *simple_context
;
410 sasl_interact_t
*prompt
;
414 /* see if we were given the result in the prompt */
415 prompt
= _plug_find_prompt(prompt_need
, id
);
416 if (prompt
!= NULL
) {
417 /* We prompted, and got.*/
419 if (required
&& !prompt
->result
) {
420 SETERROR(utils
, "Unexpectedly missing a prompt result");
421 return SASL_BADPARAM
;
424 *result
= prompt
->result
;
428 /* Try to get the callback... */
429 ret
= utils
->getcallback(utils
->conn
, id
, &simple_cb
, &simple_context
);
431 if (ret
== SASL_FAIL
&& !required
)
434 if (ret
== SASL_OK
&& simple_cb
) {
435 ret
= simple_cb(simple_context
, id
, result
, NULL
);
439 if (required
&& !*result
) {
441 return SASL_BADPARAM
;
449 * Retrieve the user password.
451 int _plug_get_password(const sasl_utils_t
*utils
, sasl_secret_t
**password
,
452 unsigned int *iscopy
, sasl_interact_t
**prompt_need
)
455 sasl_getsecret_t
*pass_cb
;
457 sasl_interact_t
*prompt
;
462 /* see if we were given the password in the prompt */
463 prompt
= _plug_find_prompt(prompt_need
, SASL_CB_PASS
);
464 if (prompt
!= NULL
) {
465 /* We prompted, and got.*/
467 if (!prompt
->result
) {
468 SETERROR(utils
, "Unexpectedly missing a prompt result");
469 return SASL_BADPARAM
;
472 /* copy what we got into a secret_t */
473 *password
= (sasl_secret_t
*) utils
->malloc(sizeof(sasl_secret_t
) +
480 (*password
)->len
=prompt
->len
;
481 memcpy((*password
)->data
, prompt
->result
, prompt
->len
);
482 (*password
)->data
[(*password
)->len
]=0;
489 /* Try to get the callback... */
490 ret
= utils
->getcallback(utils
->conn
, SASL_CB_PASS
,
491 &pass_cb
, &pass_context
);
493 if (ret
== SASL_OK
&& pass_cb
) {
494 ret
= pass_cb(utils
->conn
, pass_context
, SASL_CB_PASS
, password
);
500 return SASL_BADPARAM
;
508 * Retrieve the string given by the challenge prompt id.
510 int _plug_challenge_prompt(const sasl_utils_t
*utils
, unsigned int id
,
511 const char *challenge
, const char *promptstr
,
512 const char **result
, sasl_interact_t
**prompt_need
)
515 sasl_chalprompt_t
*chalprompt_cb
;
516 void *chalprompt_context
;
517 sasl_interact_t
*prompt
;
521 /* see if we were given the password in the prompt */
522 prompt
= _plug_find_prompt(prompt_need
, id
);
523 if (prompt
!= NULL
) {
524 /* We prompted, and got.*/
526 if (!prompt
->result
) {
527 SETERROR(utils
, "Unexpectedly missing a prompt result");
528 return SASL_BADPARAM
;
531 *result
= prompt
->result
;
535 /* Try to get the callback... */
536 ret
= utils
->getcallback(utils
->conn
, id
,
537 &chalprompt_cb
, &chalprompt_context
);
539 if (ret
== SASL_OK
&& chalprompt_cb
) {
540 ret
= chalprompt_cb(chalprompt_context
, id
,
541 challenge
, promptstr
, NULL
, result
, NULL
);
547 return SASL_BADPARAM
;
555 * Retrieve the client realm.
557 int _plug_get_realm(const sasl_utils_t
*utils
, const char **availrealms
,
558 const char **realm
, sasl_interact_t
**prompt_need
)
561 sasl_getrealm_t
*realm_cb
;
563 sasl_interact_t
*prompt
;
567 /* see if we were given the result in the prompt */
568 prompt
= _plug_find_prompt(prompt_need
, SASL_CB_GETREALM
);
569 if (prompt
!= NULL
) {
570 /* We prompted, and got.*/
572 if (!prompt
->result
) {
573 SETERROR(utils
, "Unexpectedly missing a prompt result");
574 return SASL_BADPARAM
;
577 *realm
= prompt
->result
;
581 /* Try to get the callback... */
582 ret
= utils
->getcallback(utils
->conn
, SASL_CB_GETREALM
,
583 &realm_cb
, &realm_context
);
585 if (ret
== SASL_OK
&& realm_cb
) {
586 ret
= realm_cb(realm_context
, SASL_CB_GETREALM
, availrealms
, realm
);
592 return SASL_BADPARAM
;
600 * Make the requested prompts. (prompt==NULL means we don't want it)
602 int _plug_make_prompts(const sasl_utils_t
*utils
,
603 #ifdef _INTEGRATED_SOLARIS_
605 #endif /* _INTEGRATED_SOLARIS_ */
606 sasl_interact_t
**prompts_res
,
607 const char *user_prompt
, const char *user_def
,
608 const char *auth_prompt
, const char *auth_def
,
609 const char *pass_prompt
, const char *pass_def
,
610 const char *echo_chal
,
611 const char *echo_prompt
, const char *echo_def
,
612 const char *realm_chal
,
613 const char *realm_prompt
, const char *realm_def
)
617 sasl_interact_t
*prompts
;
619 if (user_prompt
) num
++;
620 if (auth_prompt
) num
++;
621 if (pass_prompt
) num
++;
622 if (echo_prompt
) num
++;
623 if (realm_prompt
) num
++;
626 SETERROR( utils
, "make_prompts() called with no actual prompts" );
630 alloc_size
= sizeof(sasl_interact_t
)*num
;
631 prompts
= utils
->malloc(alloc_size
);
636 memset(prompts
, 0, alloc_size
);
638 *prompts_res
= prompts
;
641 (prompts
)->id
= SASL_CB_USER
;
642 #ifdef _INTEGRATED_SOLARIS_
643 (prompts
)->challenge
= convert_prompt(utils
, h
,
644 gettext("Authorization Name"));
646 (prompts
)->challenge
= "Authorization Name";
647 #endif /* _INTEGRATED_SOLARIS_ */
648 (prompts
)->prompt
= user_prompt
;
649 (prompts
)->defresult
= user_def
;
655 (prompts
)->id
= SASL_CB_AUTHNAME
;
656 #ifdef _INTEGRATED_SOLARIS_
657 (prompts
)->challenge
= convert_prompt(utils
, h
,
658 gettext( "Authentication Name"));
660 (prompts
)->challenge
= "Authentication Name";
661 #endif /* _INTEGRATED_SOLARIS_ */
662 (prompts
)->prompt
= auth_prompt
;
663 (prompts
)->defresult
= auth_def
;
669 (prompts
)->id
= SASL_CB_PASS
;
670 #ifdef _INTEGRATED_SOLARIS_
671 (prompts
)->challenge
= convert_prompt(utils
, h
, gettext("Password"));
673 (prompts
)->challenge
= "Password";
674 #endif /* _INTEGRATED_SOLARIS_ */
675 (prompts
)->prompt
= pass_prompt
;
676 (prompts
)->defresult
= pass_def
;
682 (prompts
)->id
= SASL_CB_ECHOPROMPT
;
683 (prompts
)->challenge
= echo_chal
;
684 (prompts
)->prompt
= echo_prompt
;
685 (prompts
)->defresult
= echo_def
;
691 (prompts
)->id
= SASL_CB_GETREALM
;
692 (prompts
)->challenge
= realm_chal
;
693 (prompts
)->prompt
= realm_prompt
;
694 (prompts
)->defresult
= realm_def
;
699 /* add the ending one */
700 (prompts
)->id
= SASL_CB_LIST_END
;
701 (prompts
)->challenge
= NULL
;
702 (prompts
)->prompt
= NULL
;
703 (prompts
)->defresult
= NULL
;
709 * Decode and concatenate multiple packets using the given function
710 * to decode each packet.
712 int _plug_decode(const sasl_utils_t
*utils
,
714 const char *input
, unsigned inputlen
,
715 char **output
, /* output buffer */
716 unsigned *outputsize
, /* current size of output buffer */
717 unsigned *outputlen
, /* length of data in output buffer */
718 int (*decode_pkt
)(void *context
,
719 const char **input
, unsigned *inputlen
,
720 char **output
, unsigned *outputlen
))
730 /* no need to free tmp */
731 ret
= decode_pkt(context
, &input
, &inputlen
, &tmp
, &tmplen
);
733 if(ret
!= SASL_OK
) return ret
;
735 if (tmp
!=NULL
) /* if received 2 packets merge them together */
737 ret
= _plug_buf_alloc(utils
, output
, outputsize
,
738 *outputlen
+ tmplen
+ 1);
739 if(ret
!= SASL_OK
) return ret
;
741 memcpy(*output
+ *outputlen
, tmp
, tmplen
);
743 /* Protect stupid clients */
744 *(*output
+ *outputlen
+ tmplen
) = '\0';
753 /* returns the realm we should pretend to be in */
754 int _plug_parseuser(const sasl_utils_t
*utils
,
755 char **user
, char **realm
, const char *user_realm
,
756 const char *serverFQDN
, const char *input
)
763 #endif /* _SUN_SDK_ */
765 if(!user
|| !serverFQDN
) {
767 return SASL_BADPARAM
;
770 r
= strchr(input
, '@');
772 /* hmmm, the user didn't specify a realm */
773 if(user_realm
&& user_realm
[0]) {
774 ret
= _plug_strdup(utils
, user_realm
, realm
, NULL
);
776 /* Default to serverFQDN */
777 ret
= _plug_strdup(utils
, serverFQDN
, realm
, NULL
);
780 if (ret
== SASL_OK
) {
781 ret
= _plug_strdup(utils
, input
, user
, NULL
);
785 ret
= _plug_strdup(utils
, r
, realm
, NULL
);
787 if (ret
== SASL_OK
) {
788 *user
= utils
->malloc(r
- input
);
790 memcpy(*user
, input
, r
- input
- 1);
791 (*user
)[r
- input
- 1] = '\0';
799 *user
= utils
->malloc(r
- input
+ 1);
801 strncpy(*user
, input
, r
- input
+1);
807 #endif /* _SUN_SDK_ */
813 #ifdef _INTEGRATED_SOLARIS_
815 use_locale(const char *lang_list
, int is_client
)
820 const char *i_default
= "i-default";
821 const int i_default_len
= 9;
823 if (lang_list
== NULL
)
829 /* skip over leading whitespace and commas */
830 while (isspace(*begin
) || *begin
== ',')
835 /* Find the end of the language tag */
836 for (end
= begin
; end
[1] != ',' && end
[1] != '\0'; end
++) {}
838 for (s
= end
; isspace(*s
); s
--) {}
840 if (s
== begin
&& *begin
== '*')
843 if (s
- begin
== (i_default_len
- 1) &&
844 strncasecmp(begin
, i_default
, i_default_len
) == 0)
853 typedef struct prompt_list
{
855 struct prompt_list
*next
;
859 convert_prompt(const sasl_utils_t
*utils
, void **h
, const char *s
)
861 sasl_getsimple_t
*simple_cb
;
862 void *simple_context
;
863 const char *result
= NULL
;
864 const char *s_locale
;
871 if (utils
== NULL
|| utils
->conn
== NULL
)
875 for (list
= (prompt_list
*)*h
; list
!= NULL
; list
= next
) {
877 utils
->free(list
->prompt
);
885 ret
= utils
->getcallback(utils
->conn
, SASL_CB_LANGUAGE
, &simple_cb
,
888 if (ret
== SASL_OK
&& simple_cb
) {
889 ret
= simple_cb(simple_context
, SASL_CB_LANGUAGE
, &result
, NULL
);
892 if (ret
== SASL_OK
&& !use_locale(result
, 1))
895 s_locale
= dgettext(TEXT_DOMAIN
, s
);
900 buf
= local_to_utf(utils
, s_locale
);
903 list
= utils
->malloc(sizeof (prompt_list
));
914 ret_buf
= (buf
== NULL
) ? s
: buf
;
920 #include <langinfo.h>
923 * local_to_utf converts a string in the current codeset to utf-8.
924 * If no codeset is specified, then codeset 646 will be used.
925 * Upon successful completion, this function will return a non-NULL buffer
926 * that is allocated by local_to_utf.
928 * If utils is NULL, local_to_utf will use the standard memory allocation
929 * functions, otherwise the memory functions defined in sasl_utils_t will
932 * local_to_utf will return NULL in the case of any error
935 local_to_utf(const sasl_utils_t
*utils
, const char *s
)
937 const char *code_set
= nl_langinfo(CODESET
);
950 if (code_set
== NULL
)
953 if (strcasecmp(code_set
, "UTF-8") == 0) {
957 if (_plug_strdup(utils
, s
, &buf
, NULL
) != SASL_OK
)
962 cd
= iconv_open("UTF-8", code_set
);
963 if (cd
== (iconv_t
)-1)
967 buf_size
= 4 * (in_len
+ 1); /* guess */
970 buf
= malloc(buf_size
);
972 buf
= utils
->malloc(buf_size
);
975 (void) iconv_close(cd
);
983 ret
= iconv(cd
, &inptr
, &ileft
, &outptr
, &oleft
);
984 if (ret
== (size_t)(-1)) {
985 if (errno
== E2BIG
) {
989 tmp
= realloc(buf
, buf_size
);
991 tmp
= utils
->realloc(buf
, buf_size
);
993 oleft
= (size_t)(-1);
996 outptr
= tmp
+ (outptr
-buf
);
1000 oleft
= (size_t)(-1);
1010 } else if (oleft
!= (size_t)(-1)) {
1012 tmp
= realloc(buf
, buf_size
+ 1);
1014 tmp
= utils
->realloc(buf
, buf_size
+ 1);
1016 oleft
= (size_t)(-1);
1019 buf
[buf_size
] = '\0';
1022 if (oleft
== (size_t)(-1)) {
1030 (void) iconv_close(cd
);
1033 #endif /* _INTEGRATED_SOLARIS_ */