#include "k5-int.h"
#include "os-proto.h"
#include <ctype.h>
#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include "fake-addrinfo.h"
#ifdef KRB5_DNS_LOOKUP
#include "dnsglue.h"
krb5_error_code
krb5_try_realm_txt_rr(const char *prefix, const char *name, char **realm)
{
krb5_error_code retval = KRB5_ERR_HOST_REALM_UNKNOWN;
const unsigned char *p, *base;
char host[MAXDNAME], *h;
int ret, rdlen, len;
struct krb5int_dns_state *ds = NULL;
if (name == NULL || name[0] == '\0') {
if (strlen (prefix) >= sizeof(host)-1)
return KRB5_ERR_HOST_REALM_UNKNOWN;
strcpy(host,prefix);
} else {
if ( strlen(prefix) + strlen(name) + 3 > MAXDNAME )
return KRB5_ERR_HOST_REALM_UNKNOWN;
sprintf(host,"%s.%s", prefix, name);
h = host + strlen (host);
if ((h > host) && (h[-1] != '.') && ((h - host + 1) < sizeof(host)))
strcpy (h, ".");
}
ret = krb5int_dns_init(&ds, host, C_IN, T_TXT);
if (ret < 0)
goto errout;
ret = krb5int_dns_nextans(ds, &base, &rdlen);
if (ret < 0 || base == NULL)
goto errout;
p = base;
if (!INCR_OK(base, rdlen, p, 1))
goto errout;
len = *p++;
*realm = malloc((size_t)len + 1);
if (*realm == NULL) {
retval = ENOMEM;
goto errout;
}
strncpy(*realm, (const char *)p, (size_t)len);
(*realm)[len] = '\0';
if ( (*realm)[len-1] == '.' )
(*realm)[len-1] = '\0';
retval = 0;
errout:
if (ds != NULL) {
krb5int_dns_fini(ds);
ds = NULL;
}
return retval;
}
#else
#ifndef MAXDNAME
#define MAXDNAME (16 * MAXHOSTNAMELEN)
#endif
#endif
krb5_error_code krb5int_translate_gai_error (int);
static krb5_error_code
krb5int_get_fq_hostname (char *buf, size_t bufsize, const char *name)
{
struct addrinfo *ai, hints;
int err;
memset (&hints, 0, sizeof (hints));
hints.ai_flags = AI_CANONNAME;
err = getaddrinfo (name, 0, &hints, &ai);
if (err)
return krb5int_translate_gai_error (err);
if (ai->ai_canonname == 0)
return KRB5_EAI_FAIL;
strncpy (buf, ai->ai_canonname, bufsize);
buf[bufsize-1] = 0;
freeaddrinfo (ai);
return 0;
}
krb5_error_code
krb5int_get_fq_local_hostname (char *buf, size_t bufsiz)
{
buf[0] = 0;
if (gethostname (buf, bufsiz) == -1)
return SOCKET_ERRNO;
buf[bufsiz - 1] = 0;
return krb5int_get_fq_hostname (buf, bufsiz, buf);
}
krb5_error_code KRB5_CALLCONV
krb5_get_host_realm(krb5_context context, const char *host, char ***realmsp)
{
char **retrealms;
char *realm, *cp, *temp_realm;
krb5_error_code retval;
char local_host[MAXDNAME+1];
#ifdef DEBUG_REFERRALS
printf("get_host_realm(host:%s) called\n",host);
#endif
krb5int_clean_hostname(context, host, local_host, sizeof local_host);
cp = local_host;
#ifdef DEBUG_REFERRALS
printf(" local_host: %s\n",local_host);
#endif
realm = (char *)NULL;
temp_realm = 0;
while (cp) {
#ifdef DEBUG_REFERRALS
printf(" trying to look up %s in the domain_realm map\n",cp);
#endif
retval = profile_get_string(context->profile, "domain_realm", cp,
0, (char *)NULL, &temp_realm);
if (retval)
return retval;
if (temp_realm != (char *)NULL)
break;
if (*cp == '.') {
cp++;
} else {
cp = strchr(cp, '.');
}
}
#ifdef DEBUG_REFERRALS
printf(" done searching the domain_realm map\n");
#endif
if (temp_realm) {
#ifdef DEBUG_REFERRALS
printf(" temp_realm is %s\n",temp_realm);
#endif
realm = malloc(strlen(temp_realm) + 1);
if (!realm) {
profile_release_string(temp_realm);
return ENOMEM;
}
strcpy(realm, temp_realm);
profile_release_string(temp_realm);
}
if (realm == (char *)NULL) {
if (!(cp = (char *)malloc(strlen(KRB5_REFERRAL_REALM)+1)))
return ENOMEM;
strcpy(cp, KRB5_REFERRAL_REALM);
realm = cp;
}
if (!(retrealms = (char **)calloc(2, sizeof(*retrealms)))) {
if (realm != (char *)NULL)
free(realm);
return ENOMEM;
}
retrealms[0] = realm;
retrealms[1] = 0;
*realmsp = retrealms;
return 0;
}
#if defined(_WIN32) && !defined(__CYGWIN32__)
# ifndef EAFNOSUPPORT
# define EAFNOSUPPORT WSAEAFNOSUPPORT
# endif
#endif
krb5_error_code
krb5int_translate_gai_error (int num)
{
switch (num) {
#ifdef EAI_ADDRFAMILY
case EAI_ADDRFAMILY:
return EAFNOSUPPORT;
#endif
case EAI_AGAIN:
return EAGAIN;
case EAI_BADFLAGS:
return EINVAL;
case EAI_FAIL:
return KRB5_EAI_FAIL;
case EAI_FAMILY:
return EAFNOSUPPORT;
case EAI_MEMORY:
return ENOMEM;
#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
case EAI_NODATA:
return KRB5_EAI_NODATA;
#endif
case EAI_NONAME:
return KRB5_EAI_NONAME;
#if defined(EAI_OVERFLOW)
case EAI_OVERFLOW:
return EINVAL;
#endif
case EAI_SERVICE:
return KRB5_EAI_SERVICE;
case EAI_SOCKTYPE:
return EINVAL;
#ifdef EAI_SYSTEM
case EAI_SYSTEM:
return errno;
#endif
}
return -1;
}
krb5_error_code KRB5_CALLCONV
krb5_get_fallback_host_realm(krb5_context context, krb5_data *hdata, char ***realmsp)
{
char **retrealms;
char *realm = (char *)NULL, *cp;
krb5_error_code retval;
char local_host[MAXDNAME+1], host[MAXDNAME+1];
memcpy(host, hdata->data, hdata->length);
host[hdata->length]=0;
#ifdef DEBUG_REFERRALS
printf("get_fallback_host_realm(host >%s<) called\n",host);
#endif
krb5int_clean_hostname(context, host, local_host, sizeof local_host);
#ifdef DEBUG_REFERRALS
printf(" local_host: %s\n",local_host);
#endif
#ifdef KRB5_DNS_LOOKUP
if (_krb5_use_dns_realm(context)) {
cp = local_host;
do {
retval = krb5_try_realm_txt_rr("_kerberos", cp, &realm);
cp = strchr(cp,'.');
if (cp)
cp++;
} while (retval && cp && cp[0]);
} else
#endif
{
(void) krb5int_fqdn_get_realm(context, local_host,
&realm);
#ifdef DEBUG_REFERRALS
printf(" done finding DNS-based default realm: >%s<\n",realm);
#endif
}
if (realm == (char *)NULL) {
retval = krb5_get_default_realm(context, &realm);
if (retval) {
return retval;
}
}
if (!(retrealms = (char **)calloc(2, sizeof(*retrealms)))) {
if (realm != (char *)NULL)
free(realm);
return ENOMEM;
}
retrealms[0] = realm;
retrealms[1] = 0;
*realmsp = retrealms;
return 0;
}
krb5_error_code KRB5_CALLCONV
krb5int_clean_hostname(krb5_context context, const char *host, char *local_host, size_t lhsize)
{
char *cp;
krb5_error_code retval;
int l;
local_host[0]=0;
#ifdef DEBUG_REFERRALS
printf("krb5int_clean_hostname called: host<%s>, local_host<%s>, size %d\n",host,local_host,lhsize);
#endif
if (host) {
if (strspn(host, "01234567890.") == strlen(host)) {
int ndots = 0;
const char *p;
for (p = host; *p; p++)
if (*p == '.')
ndots++;
if (ndots == 3)
return KRB5_ERR_NUMERIC_REALM;
}
if (strchr(host, ':'))
return KRB5_ERR_NUMERIC_REALM;
strncpy(local_host, host, lhsize);
local_host[lhsize - 1] = '\0';
} else {
retval = krb5int_get_fq_local_hostname (local_host, lhsize);
if (retval)
return retval;
}
for (cp = local_host; *cp; cp++) {
if (isupper((unsigned char) (*cp)))
*cp = tolower((unsigned char) *cp);
}
l = strlen(local_host);
if (l && local_host[l-1] == '.')
local_host[l-1] = 0;
#ifdef DEBUG_REFERRALS
printf("krb5int_clean_hostname ending: host<%s>, local_host<%s>, size %d\n",host,local_host,lhsize);
#endif
return 0;
}
krb5_error_code
krb5int_domain_get_realm(krb5_context context, const char *domain, char **realm) {
krb5_error_code retval;
struct addrlist addrlist = ADDRLIST_INIT;
krb5_data drealm;
char *cp = NULL;
char *fqdn = NULL;
*realm = NULL;
memset(&drealm, 0, sizeof (drealm));
if (!(fqdn = malloc(strlen(domain) + 1))) {
return (ENOMEM);
}
strlcpy(fqdn, domain, strlen(domain) + 1);
for (cp = fqdn; *cp; cp++)
if (islower((int)(*cp)))
*cp = toupper((int)*cp);
cp = fqdn;
while (strchr(cp, '.') != NULL) {
drealm.length = strlen(cp);
drealm.data = cp;
retval = krb5_locate_kdc(context, &drealm, &addrlist, 0, SOCK_DGRAM, 0);
krb5int_free_addrlist(&addrlist);
if (!retval) {
if (!(*realm = malloc(strlen(cp) + 1))) {
free(fqdn);
return (ENOMEM);
}
strlcpy(*realm, cp, strlen(cp) + 1);
break;
}
cp = strchr(cp, '.');
cp++;
}
free(fqdn);
return (0);
}
krb5_error_code
krb5int_fqdn_get_realm(krb5_context context, const char *fqdn, char **realm) {
char *domain = strchr(fqdn, '.');
if (domain) {
domain++;
return (krb5int_domain_get_realm(context, domain, realm));
} else {
return (-1);
}
}