freebsd-dev/crypto/kerberosIV/lib/krb/get_host.c
2000-01-09 08:31:47 +00:00

388 lines
9.0 KiB
C

/*
* Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "krb_locl.h"
RCSID("$Id: get_host.c,v 1.48 1999/12/02 16:58:41 joda Exp $");
static struct host_list {
struct krb_host *this;
struct host_list *next;
} *hosts;
static int krb_port = 0;
static void
free_hosts(struct host_list *h)
{
struct host_list *t;
while(h){
if(h->this->realm)
free(h->this->realm);
if(h->this->host)
free(h->this->host);
t = h;
h = h->next;
free(t);
}
}
static int
parse_address(char *address, enum krb_host_proto *proto,
char **host, int *port)
{
char *p, *q;
int default_port = krb_port;
*proto = PROTO_UDP;
if(strncmp(address, "http://", 7) == 0){
p = address + 7;
*proto = PROTO_HTTP;
default_port = 80;
}else{
p = strchr(address, '/');
if(p){
char prot[32];
strlcpy (prot, address,
min(p - address + 1, sizeof(prot)));
if(strcasecmp(prot, "udp") == 0)
*proto = PROTO_UDP;
else if(strcasecmp(prot, "tcp") == 0)
*proto = PROTO_TCP;
else if(strcasecmp(prot, "http") == 0) {
*proto = PROTO_HTTP;
default_port = 80;
} else
krb_warning("Unknown protocol `%s', Using default `udp'.\n",
prot);
p++;
}else
p = address;
}
q = strchr(p, ':');
if(q) {
*host = malloc(q - p + 1);
if (*host == NULL)
return -1;
strlcpy (*host, p, q - p + 1);
q++;
{
struct servent *sp = getservbyname(q, NULL);
if(sp)
*port = ntohs(sp->s_port);
else
if(sscanf(q, "%d", port) != 1){
krb_warning("Bad port specification `%s', using port %d.",
q, krb_port);
*port = krb_port;
}
}
} else {
*port = default_port;
q = strchr(p, '/');
if (q) {
*host = malloc(q - p + 1);
if (*host == NULL)
return -1;
strlcpy (*host, p, q - p + 1);
} else {
*host = strdup(p);
if(*host == NULL)
return -1;
}
}
return 0;
}
static int
add_host(const char *realm, char *address, int admin, int validate)
{
struct krb_host *host;
struct host_list *p, **last = &hosts;
host = (struct krb_host*)malloc(sizeof(struct krb_host));
if (host == NULL)
return 1;
if(parse_address(address, &host->proto, &host->host, &host->port) < 0) {
free(host);
return 1;
}
if (validate) {
if (krb_dns_debug)
krb_warning("Getting host entry for %s...", host->host);
if (gethostbyname(host->host) == NULL) {
if (krb_dns_debug)
krb_warning("Didn't get it.\n");
free(host->host);
free(host);
return 1;
}
else if (krb_dns_debug)
krb_warning("Got it.\n");
}
host->admin = admin;
for(p = hosts; p; p = p->next){
if(strcmp(realm, p->this->realm) == 0 &&
strcmp(host->host, p->this->host) == 0 &&
host->proto == p->this->proto &&
host->port == p->this->port){
free(host->host);
free(host);
return 1;
}
last = &p->next;
}
host->realm = strdup(realm);
if (host->realm == NULL) {
free(host->host);
free(host);
return 1;
}
p = (struct host_list*)malloc(sizeof(struct host_list));
if (p == NULL) {
free(host->realm);
free(host->host);
free(host);
return 1;
}
p->this = host;
p->next = NULL;
*last = p;
return 0;
}
static int
read_file(const char *filename, const char *r)
{
char line[1024];
int nhosts = 0;
FILE *f = fopen(filename, "r");
if(f == NULL)
return -1;
while(fgets(line, sizeof(line), f) != NULL) {
char *realm, *address, *admin;
char *save;
realm = strtok_r (line, " \t\n\r", &save);
if (realm == NULL)
continue;
if (strcmp(realm, r))
continue;
address = strtok_r (NULL, " \t\n\r", &save);
if (address == NULL)
continue;
admin = strtok_r (NULL, " \t\n\r", &save);
if (add_host(realm,
address,
admin != NULL && strcasecmp(admin, "admin") == 0,
0) == 0)
++nhosts;
}
fclose(f);
return nhosts;
}
#if 0
static int
read_cellservdb (const char *filename, const char *realm)
{
char line[1024];
FILE *f = fopen (filename, "r");
int nhosts = 0;
if (f == NULL)
return -1;
while (fgets (line, sizeof(line), f) != NULL) {
if (line[0] == '>'
&& strncasecmp (line + 1, realm, strlen(realm)) == 0) {
while (fgets (line, sizeof(line), f) != NULL && *line != '>') {
char *hash;
if (line [strlen(line) - 1] == '\n')
line [strlen(line) - 1] = '\0';
hash = strchr (line, '#');
if (hash != NULL
&& add_host (realm, hash + 1, 0, 0) == 0)
++nhosts;
}
break;
}
}
fclose (f);
return nhosts;
}
#endif
static int
init_hosts(char *realm)
{
int i, j, ret = 0;
char file[MaxPathLen];
/*
* proto should really be NULL, but there are libraries out there
* that don't like that so we use "udp" instead.
*/
krb_port = ntohs(k_getportbyname (KRB_SERVICE, "udp", htons(KRB_PORT)));
for(i = 0; krb_get_krbconf(i, file, sizeof(file)) == 0; i++) {
j = read_file(file, realm);
if (j > 0) ret += j;
}
return ret;
}
static void
srv_find_realm(char *realm, char *proto, char *service)
{
char *domain;
struct dns_reply *r;
struct resource_record *rr;
roken_mconcat(&domain, 1024, service, ".", proto, ".", realm, ".", NULL);
if(domain == NULL)
return;
r = dns_lookup(domain, "srv");
if(r == NULL)
r = dns_lookup(domain, "txt");
if(r == NULL){
free(domain);
return;
}
for(rr = r->head; rr; rr = rr->next){
if(rr->type == T_SRV){
char buf[1024];
if (snprintf (buf,
sizeof(buf),
"%s/%s:%u",
proto,
rr->u.srv->target,
rr->u.srv->port) < sizeof(buf))
add_host(realm, buf, 0, 0);
}else if(rr->type == T_TXT)
add_host(realm, rr->u.txt, 0, 0);
}
dns_free_data(r);
free(domain);
}
struct krb_host*
krb_get_host(int nth, const char *realm, int admin)
{
struct host_list *p;
static char orealm[REALM_SZ];
if(orealm[0] == 0 || strcmp(realm, orealm)){
/* quick optimization */
if(realm && realm[0]){
strlcpy (orealm, realm, sizeof(orealm));
}else{
int ret = krb_get_lrealm(orealm, 1);
if(ret != KSUCCESS)
return NULL;
}
if(hosts){
free_hosts(hosts);
hosts = NULL;
}
if (init_hosts(orealm) < nth) {
srv_find_realm(orealm, "udp", KRB_SERVICE);
srv_find_realm(orealm, "tcp", KRB_SERVICE);
srv_find_realm(orealm, "http", KRB_SERVICE);
{
char *host;
int i = 0;
asprintf(&host, "kerberos.%s.", orealm);
if (host == NULL) {
free_hosts(hosts);
hosts = NULL;
return NULL;
}
add_host(orealm, host, 1, 1);
do {
i++;
free(host);
asprintf(&host, "kerberos-%d.%s.", i, orealm);
} while(host != NULL
&& i < 100000
&& add_host(orealm, host, 0, 1) == 0);
free(host);
}
}
#if 0
read_cellservdb ("/usr/vice/etc/CellServDB", orealm);
read_cellservdb ("/usr/arla/etc/CellServDB", orealm);
#endif
}
for(p = hosts; p; p = p->next){
if(strcmp(orealm, p->this->realm) == 0 &&
(!admin || p->this->admin)) {
if(nth == 1)
return p->this;
else
nth--;
}
}
return NULL;
}
int
krb_get_krbhst(char *host, char *realm, int nth)
{
struct krb_host *p = krb_get_host(nth, realm, 0);
if(p == NULL)
return KFAILURE;
strlcpy (host, p->host, MaxHostNameLen);
return KSUCCESS;
}
int
krb_get_admhst(char *host, char *realm, int nth)
{
struct krb_host *p = krb_get_host(nth, realm, 1);
if(p == NULL)
return KFAILURE;
strlcpy (host, p->host, MaxHostNameLen);
return KSUCCESS;
}