#include <config.h>
#ifndef macintosh
#ifdef WIN32
# include <winsock.h>
#else
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
#endif
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#include <sasl.h>
#include <saslutil.h>
#include <saslplug.h>
#include <errno.h>
#include <ctype.h>
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#include "plugin_common.h"
static void sockaddr_unmapped(
#ifdef IN6_IS_ADDR_V4MAPPED
struct sockaddr *sa, socklen_t *len
#else
struct sockaddr *sa __attribute__((unused)),
socklen_t *len __attribute__((unused))
#endif
)
{
#ifdef IN6_IS_ADDR_V4MAPPED
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin4;
uint32_t addr;
#ifdef _SUN_SDK_
in_port_t port;
#else
int port;
#endif
if (sa->sa_family != AF_INET6)
return;
sin6 = (struct sockaddr_in6 *)sa;
if (!IN6_IS_ADDR_V4MAPPED((&sin6->sin6_addr)))
return;
sin4 = (struct sockaddr_in *)sa;
addr = *(uint32_t *)&sin6->sin6_addr.s6_addr[12];
port = sin6->sin6_port;
memset(sin4, 0, sizeof(struct sockaddr_in));
sin4->sin_addr.s_addr = addr;
sin4->sin_port = port;
sin4->sin_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
sin4->sin_len = sizeof(struct sockaddr_in);
#endif
*len = sizeof(struct sockaddr_in);
#else
return;
#endif
}
int _plug_ipfromstring(const sasl_utils_t *utils, const char *addr,
struct sockaddr *out, socklen_t outlen)
{
int i, j;
socklen_t len;
#ifdef WINNT
struct sockaddr_in ss;
#else
struct sockaddr_storage ss;
#endif
struct addrinfo hints, *ai = NULL;
char hbuf[NI_MAXHOST];
#ifdef _SUN_SDK_
const char *start, *end, *p;
#endif
if(!utils || !addr || !out) {
if(utils) PARAMERROR( utils );
return SASL_BADPARAM;
}
#ifdef _SUN_SDK_
end = strchr(addr, ']');
if (end != NULL) {
start = strchr(addr, '[');
if (start >= end || start == NULL) {
if(utils) PARAMERROR( utils );
return SASL_BADPARAM;
}
for (i = 0, p = start + 1; p < end; p++) {
hbuf[i++] = *p;
if (i >= NI_MAXHOST)
break;
}
p = strchr(end, ':');
if (p == NULL)
p = end + 1;
else
p = p + 1;
} else {
for (i = 0; addr[i] != '\0' && addr[i] != ';'; ) {
hbuf[i] = addr[i];
if (++i >= NI_MAXHOST)
break;
}
if (addr[i] == ';')
p = &addr[i+1];
else
p = &addr[i];
}
if (i >= NI_MAXHOST) {
if(utils) PARAMERROR( utils );
return SASL_BADPARAM;
}
hbuf[i] = '\0';
for (j = 0; p[j] != '\0'; j++)
if (!isdigit((int)(p[j]))) {
PARAMERROR( utils );
return SASL_BADPARAM;
}
#else
for (i = 0; addr[i] != '\0' && addr[i] != ';'; i++) {
if (i >= NI_MAXHOST) {
if(utils) PARAMERROR( utils );
return SASL_BADPARAM;
}
hbuf[i] = addr[i];
}
hbuf[i] = '\0';
if (addr[i] == ';')
i++;
for (j = i; addr[j] != '\0'; j++)
if (!isdigit((int)(addr[j]))) {
PARAMERROR( utils );
return SASL_BADPARAM;
}
#endif
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
#ifdef _SUN_SDK_
if (getaddrinfo(hbuf, p, &hints, &ai) != 0) {
#else
if (getaddrinfo(hbuf, &addr[i], &hints, &ai) != 0) {
#endif
PARAMERROR( utils );
return SASL_BADPARAM;
}
len = ai->ai_addrlen;
#ifdef _SUN_SDK_
if (len > sizeof(ss))
return (SASL_BUFOVER);
#endif
memcpy(&ss, ai->ai_addr, len);
freeaddrinfo(ai);
sockaddr_unmapped((struct sockaddr *)&ss, &len);
if (outlen < len) {
PARAMERROR( utils );
return SASL_BUFOVER;
}
memcpy(out, &ss, len);
return SASL_OK;
}
int _plug_iovec_to_buf(const sasl_utils_t *utils, const struct iovec *vec,
unsigned numiov, buffer_info_t **output)
{
unsigned i;
int ret;
buffer_info_t *out;
char *pos;
if(!utils || !vec || !output) {
if(utils) PARAMERROR( utils );
return SASL_BADPARAM;
}
if(!(*output)) {
*output = utils->malloc(sizeof(buffer_info_t));
if(!*output) {
MEMERROR(utils);
return SASL_NOMEM;
}
memset(*output,0,sizeof(buffer_info_t));
}
out = *output;
out->curlen = 0;
for(i=0; i<numiov; i++)
out->curlen += vec[i].iov_len;
ret = _plug_buf_alloc(utils, &out->data, &out->reallen, out->curlen);
if(ret != SASL_OK) {
MEMERROR(utils);
return SASL_NOMEM;
}
memset(out->data, 0, out->reallen);
pos = out->data;
for(i=0; i<numiov; i++) {
memcpy(pos, vec[i].iov_base, vec[i].iov_len);
pos += vec[i].iov_len;
}
return SASL_OK;
}
int _plug_buf_alloc(const sasl_utils_t *utils, char **rwbuf,
unsigned *curlen, unsigned newlen)
{
if(!utils || !rwbuf || !curlen) {
PARAMERROR(utils);
return SASL_BADPARAM;
}
if(!(*rwbuf)) {
*rwbuf = utils->malloc(newlen);
if (*rwbuf == NULL) {
*curlen = 0;
MEMERROR(utils);
return SASL_NOMEM;
}
*curlen = newlen;
} else if(*rwbuf && *curlen < newlen) {
#ifdef _SUN_SDK_
unsigned needed = 2*(*curlen);
#else
size_t needed = 2*(*curlen);
#endif
while(needed < newlen)
needed *= 2;
*rwbuf = utils->realloc(*rwbuf, needed);
if (*rwbuf == NULL) {
*curlen = 0;
MEMERROR(utils);
return SASL_NOMEM;
}
*curlen = needed;
}
return SASL_OK;
}
int _plug_strdup(const sasl_utils_t * utils, const char *in,
char **out, int *outlen)
{
#ifdef _SUN_SDK_
int len;
#else
size_t len = strlen(in);
#endif
if(!utils || !in || !out) {
if(utils) PARAMERROR(utils);
return SASL_BADPARAM;
}
#ifdef _SUN_SDK_
len = strlen(in);
#endif
*out = utils->malloc(len + 1);
if (!*out) {
MEMERROR(utils);
return SASL_NOMEM;
}
strcpy((char *) *out, in);
if (outlen)
*outlen = len;
return SASL_OK;
}
void _plug_free_string(const sasl_utils_t *utils, char **str)
{
size_t len;
if (!utils || !str || !(*str)) return;
len = strlen(*str);
utils->erasebuffer(*str, len);
utils->free(*str);
*str=NULL;
}
void _plug_free_secret(const sasl_utils_t *utils, sasl_secret_t **secret)
{
if(!utils || !secret || !(*secret)) return;
#ifdef _SUN_SDK_
utils->erasebuffer((char *)(*secret)->data, (*secret)->len);
#else
utils->erasebuffer((*secret)->data, (*secret)->len);
#endif
utils->free(*secret);
*secret = NULL;
}
sasl_interact_t *_plug_find_prompt(sasl_interact_t **promptlist,
unsigned int lookingfor)
{
sasl_interact_t *prompt;
if (promptlist && *promptlist) {
for (prompt = *promptlist; prompt->id != SASL_CB_LIST_END; ++prompt) {
if (prompt->id==lookingfor)
return prompt;
}
}
return NULL;
}
int _plug_get_simple(const sasl_utils_t *utils, unsigned int id, int required,
const char **result, sasl_interact_t **prompt_need)
{
int ret = SASL_FAIL;
sasl_getsimple_t *simple_cb;
void *simple_context;
sasl_interact_t *prompt;
*result = NULL;
prompt = _plug_find_prompt(prompt_need, id);
if (prompt != NULL) {
if (required && !prompt->result) {
SETERROR(utils, "Unexpectedly missing a prompt result");
return SASL_BADPARAM;
}
*result = prompt->result;
return SASL_OK;
}
ret = utils->getcallback(utils->conn, id, &simple_cb, &simple_context);
if (ret == SASL_FAIL && !required)
return SASL_OK;
if (ret == SASL_OK && simple_cb) {
ret = simple_cb(simple_context, id, result, NULL);
if (ret != SASL_OK)
return ret;
if (required && !*result) {
PARAMERROR(utils);
return SASL_BADPARAM;
}
}
return ret;
}
int _plug_get_password(const sasl_utils_t *utils, sasl_secret_t **password,
unsigned int *iscopy, sasl_interact_t **prompt_need)
{
int ret = SASL_FAIL;
sasl_getsecret_t *pass_cb;
void *pass_context;
sasl_interact_t *prompt;
*password = NULL;
*iscopy = 0;
prompt = _plug_find_prompt(prompt_need, SASL_CB_PASS);
if (prompt != NULL) {
if (!prompt->result) {
SETERROR(utils, "Unexpectedly missing a prompt result");
return SASL_BADPARAM;
}
*password = (sasl_secret_t *) utils->malloc(sizeof(sasl_secret_t) +
prompt->len + 1);
if (!*password) {
MEMERROR(utils);
return SASL_NOMEM;
}
(*password)->len=prompt->len;
memcpy((*password)->data, prompt->result, prompt->len);
(*password)->data[(*password)->len]=0;
*iscopy = 1;
return SASL_OK;
}
ret = utils->getcallback(utils->conn, SASL_CB_PASS,
&pass_cb, &pass_context);
if (ret == SASL_OK && pass_cb) {
ret = pass_cb(utils->conn, pass_context, SASL_CB_PASS, password);
if (ret != SASL_OK)
return ret;
if (!*password) {
PARAMERROR(utils);
return SASL_BADPARAM;
}
}
return ret;
}
int _plug_challenge_prompt(const sasl_utils_t *utils, unsigned int id,
const char *challenge, const char *promptstr,
const char **result, sasl_interact_t **prompt_need)
{
int ret = SASL_FAIL;
sasl_chalprompt_t *chalprompt_cb;
void *chalprompt_context;
sasl_interact_t *prompt;
*result = NULL;
prompt = _plug_find_prompt(prompt_need, id);
if (prompt != NULL) {
if (!prompt->result) {
SETERROR(utils, "Unexpectedly missing a prompt result");
return SASL_BADPARAM;
}
*result = prompt->result;
return SASL_OK;
}
ret = utils->getcallback(utils->conn, id,
&chalprompt_cb, &chalprompt_context);
if (ret == SASL_OK && chalprompt_cb) {
ret = chalprompt_cb(chalprompt_context, id,
challenge, promptstr, NULL, result, NULL);
if (ret != SASL_OK)
return ret;
if (!*result) {
PARAMERROR(utils);
return SASL_BADPARAM;
}
}
return ret;
}
int _plug_get_realm(const sasl_utils_t *utils, const char **availrealms,
const char **realm, sasl_interact_t **prompt_need)
{
int ret = SASL_FAIL;
sasl_getrealm_t *realm_cb;
void *realm_context;
sasl_interact_t *prompt;
*realm = NULL;
prompt = _plug_find_prompt(prompt_need, SASL_CB_GETREALM);
if (prompt != NULL) {
if (!prompt->result) {
SETERROR(utils, "Unexpectedly missing a prompt result");
return SASL_BADPARAM;
}
*realm = prompt->result;
return SASL_OK;
}
ret = utils->getcallback(utils->conn, SASL_CB_GETREALM,
&realm_cb, &realm_context);
if (ret == SASL_OK && realm_cb) {
ret = realm_cb(realm_context, SASL_CB_GETREALM, availrealms, realm);
if (ret != SASL_OK)
return ret;
if (!*realm) {
PARAMERROR(utils);
return SASL_BADPARAM;
}
}
return ret;
}
int _plug_make_prompts(const sasl_utils_t *utils,
#ifdef _INTEGRATED_SOLARIS_
void **h,
#endif
sasl_interact_t **prompts_res,
const char *user_prompt, const char *user_def,
const char *auth_prompt, const char *auth_def,
const char *pass_prompt, const char *pass_def,
const char *echo_chal,
const char *echo_prompt, const char *echo_def,
const char *realm_chal,
const char *realm_prompt, const char *realm_def)
{
int num = 1;
int alloc_size;
sasl_interact_t *prompts;
if (user_prompt) num++;
if (auth_prompt) num++;
if (pass_prompt) num++;
if (echo_prompt) num++;
if (realm_prompt) num++;
if (num == 1) {
SETERROR( utils, "make_prompts() called with no actual prompts" );
return SASL_FAIL;
}
alloc_size = sizeof(sasl_interact_t)*num;
prompts = utils->malloc(alloc_size);
if (!prompts) {
MEMERROR( utils );
return SASL_NOMEM;
}
memset(prompts, 0, alloc_size);
*prompts_res = prompts;
if (user_prompt) {
(prompts)->id = SASL_CB_USER;
#ifdef _INTEGRATED_SOLARIS_
(prompts)->challenge = convert_prompt(utils, h,
gettext("Authorization Name"));
#else
(prompts)->challenge = "Authorization Name";
#endif
(prompts)->prompt = user_prompt;
(prompts)->defresult = user_def;
prompts++;
}
if (auth_prompt) {
(prompts)->id = SASL_CB_AUTHNAME;
#ifdef _INTEGRATED_SOLARIS_
(prompts)->challenge = convert_prompt(utils, h,
gettext( "Authentication Name"));
#else
(prompts)->challenge = "Authentication Name";
#endif
(prompts)->prompt = auth_prompt;
(prompts)->defresult = auth_def;
prompts++;
}
if (pass_prompt) {
(prompts)->id = SASL_CB_PASS;
#ifdef _INTEGRATED_SOLARIS_
(prompts)->challenge = convert_prompt(utils, h, gettext("Password"));
#else
(prompts)->challenge = "Password";
#endif
(prompts)->prompt = pass_prompt;
(prompts)->defresult = pass_def;
prompts++;
}
if (echo_prompt) {
(prompts)->id = SASL_CB_ECHOPROMPT;
(prompts)->challenge = echo_chal;
(prompts)->prompt = echo_prompt;
(prompts)->defresult = echo_def;
prompts++;
}
if (realm_prompt) {
(prompts)->id = SASL_CB_GETREALM;
(prompts)->challenge = realm_chal;
(prompts)->prompt = realm_prompt;
(prompts)->defresult = realm_def;
prompts++;
}
(prompts)->id = SASL_CB_LIST_END;
(prompts)->challenge = NULL;
(prompts)->prompt = NULL;
(prompts)->defresult = NULL;
return SASL_OK;
}
int _plug_decode(const sasl_utils_t *utils,
void *context,
const char *input, unsigned inputlen,
char **output,
unsigned *outputsize,
unsigned *outputlen,
int (*decode_pkt)(void *context,
const char **input, unsigned *inputlen,
char **output, unsigned *outputlen))
{
char *tmp = NULL;
unsigned tmplen = 0;
int ret;
*outputlen = 0;
while (inputlen!=0)
{
ret = decode_pkt(context, &input, &inputlen, &tmp, &tmplen);
if(ret != SASL_OK) return ret;
if (tmp!=NULL)
{
ret = _plug_buf_alloc(utils, output, outputsize,
*outputlen + tmplen + 1);
if(ret != SASL_OK) return ret;
memcpy(*output + *outputlen, tmp, tmplen);
*(*output + *outputlen + tmplen) = '\0';
*outputlen+=tmplen;
}
}
return SASL_OK;
}
int _plug_parseuser(const sasl_utils_t *utils,
char **user, char **realm, const char *user_realm,
const char *serverFQDN, const char *input)
{
int ret;
#ifdef _SUN_SDK_
const char *r;
#else
char *r;
#endif
if(!user || !serverFQDN) {
PARAMERROR( utils );
return SASL_BADPARAM;
}
r = strchr(input, '@');
if (!r) {
if(user_realm && user_realm[0]) {
ret = _plug_strdup(utils, user_realm, realm, NULL);
} else {
ret = _plug_strdup(utils, serverFQDN, realm, NULL);
}
if (ret == SASL_OK) {
ret = _plug_strdup(utils, input, user, NULL);
}
} else {
r++;
ret = _plug_strdup(utils, r, realm, NULL);
#ifdef _SUN_SDK_
if (ret == SASL_OK) {
*user = utils->malloc(r - input);
if (*user) {
memcpy(*user, input, r - input - 1);
(*user)[r - input - 1] = '\0';
} else {
MEMERROR( utils );
ret = SASL_NOMEM;
}
}
#else
*--r = '\0';
*user = utils->malloc(r - input + 1);
if (*user) {
strncpy(*user, input, r - input +1);
} else {
MEMERROR( utils );
ret = SASL_NOMEM;
}
*r = '@';
#endif
}
return ret;
}
#ifdef _INTEGRATED_SOLARIS_
int
use_locale(const char *lang_list, int is_client)
{
const char *s;
const char *begin;
const char *end;
const char *i_default = "i-default";
const int i_default_len = 9;
if (lang_list == NULL)
return is_client;
begin = lang_list;
for (;;) {
while (isspace(*begin) || *begin == ',')
begin++;
if (*begin == '\0')
break;
for (end = begin; end[1] != ',' && end[1] != '\0'; end++) {}
for (s = end; isspace(*s); s--) {}
if (s == begin && *begin == '*')
return 1;
if (s - begin == (i_default_len - 1) &&
strncasecmp(begin, i_default, i_default_len) == 0)
return 0;
begin = end + 1;
}
return is_client;
}
typedef struct prompt_list {
char *prompt;
struct prompt_list *next;
} prompt_list;
const char *
convert_prompt(const sasl_utils_t *utils, void **h, const char *s)
{
sasl_getsimple_t *simple_cb;
void *simple_context;
const char *result = NULL;
const char *s_locale;
int ret;
char *buf;
const char *ret_buf;
prompt_list *list;
prompt_list *next;
if (utils == NULL || utils->conn == NULL)
return s;
if (s == NULL) {
for (list = (prompt_list *)*h; list != NULL; list = next) {
if (list->prompt)
utils->free(list->prompt);
next = list->next;
utils->free(list);
}
*h = NULL;
return NULL;
}
ret = utils->getcallback(utils->conn, SASL_CB_LANGUAGE, &simple_cb,
&simple_context);
if (ret == SASL_OK && simple_cb) {
ret = simple_cb(simple_context, SASL_CB_LANGUAGE, &result, NULL);
} else
ret = SASL_FAIL;
if (ret == SASL_OK && !use_locale(result, 1))
return s;
s_locale = dgettext(TEXT_DOMAIN, s);
if (s == s_locale) {
return s;
}
buf = local_to_utf(utils, s_locale);
if (buf != NULL) {
list = utils->malloc(sizeof (prompt_list));
if (list == NULL) {
utils->free(buf);
buf = NULL;
} else {
list->prompt = buf;
list->next = *h;
*h = list;
}
}
ret_buf = (buf == NULL) ? s : buf;
return ret_buf;
}
#include <iconv.h>
#include <langinfo.h>
char *
local_to_utf(const sasl_utils_t *utils, const char *s)
{
const char *code_set = nl_langinfo(CODESET);
iconv_t cd;
char *buf, *tmp;
size_t in_len;
size_t buf_size;
size_t ileft, oleft;
const char *inptr;
char *outptr;
size_t ret;
if (s == NULL)
return NULL;
if (code_set == NULL)
code_set = "646";
if (strcasecmp(code_set, "UTF-8") == 0) {
if (utils == NULL)
buf = strdup(s);
else {
if (_plug_strdup(utils, s, &buf, NULL) != SASL_OK)
buf = NULL;
}
return buf;
}
cd = iconv_open("UTF-8", code_set);
if (cd == (iconv_t)-1)
return NULL;
in_len = strlen(s);
buf_size = 4 * (in_len + 1);
if (utils == NULL)
buf = malloc(buf_size);
else
buf = utils->malloc(buf_size);
if (buf == NULL) {
(void) iconv_close(cd);
return NULL;
}
inptr = s;
ileft = in_len;
outptr = buf;
oleft = buf_size;
for (;;) {
ret = iconv(cd, &inptr, &ileft, &outptr, &oleft);
if (ret == (size_t)(-1)) {
if (errno == E2BIG) {
oleft += buf_size;
buf_size *= 2;
if (utils == NULL)
tmp = realloc(buf, buf_size);
else
tmp = utils->realloc(buf, buf_size);
if (tmp == NULL) {
oleft = (size_t)(-1);
break;
}
outptr = tmp + (outptr-buf);
buf = tmp;
continue;
}
oleft = (size_t)(-1);
break;
}
if (inptr == NULL)
break;
inptr = NULL;
ileft = 0;
}
if (oleft > 0) {
*outptr = '\0';
} else if (oleft != (size_t)(-1)) {
if (utils == NULL)
tmp = realloc(buf, buf_size + 1);
else
tmp = utils->realloc(buf, buf_size + 1);
if (tmp == NULL) {
oleft = (size_t)(-1);
} else {
buf = tmp;
buf[buf_size] = '\0';
}
}
if (oleft == (size_t)(-1)) {
if (utils == NULL)
free(buf);
else
utils->free(buf);
buf = NULL;
}
(void) iconv_close(cd);
return buf;
}
#endif