/* * 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; }