155682Smarkm/* 2178825Sdfr * Copyright (c) 2001 - 2003 Kungliga Tekniska H�gskolan 355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden). 455682Smarkm * All rights reserved. 555682Smarkm * 655682Smarkm * Redistribution and use in source and binary forms, with or without 755682Smarkm * modification, are permitted provided that the following conditions 855682Smarkm * are met: 955682Smarkm * 1055682Smarkm * 1. Redistributions of source code must retain the above copyright 1155682Smarkm * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1455682Smarkm * notice, this list of conditions and the following disclaimer in the 1555682Smarkm * documentation and/or other materials provided with the distribution. 1655682Smarkm * 1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors 1855682Smarkm * may be used to endorse or promote products derived from this software 1955682Smarkm * without specific prior written permission. 2055682Smarkm * 2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2455682Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3155682Smarkm * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "krb5_locl.h" 3555682Smarkm#include <resolve.h> 36178825Sdfr#include "locate_plugin.h" 3755682Smarkm 38178825SdfrRCSID("$Id: krbhst.c 21457 2007-07-10 12:53:25Z lha $"); 3955682Smarkm 4090926Snectarstatic int 4190926Snectarstring_to_proto(const char *string) 4290926Snectar{ 4390926Snectar if(strcasecmp(string, "udp") == 0) 4490926Snectar return KRB5_KRBHST_UDP; 4590926Snectar else if(strcasecmp(string, "tcp") == 0) 4690926Snectar return KRB5_KRBHST_TCP; 4790926Snectar else if(strcasecmp(string, "http") == 0) 4890926Snectar return KRB5_KRBHST_HTTP; 4990926Snectar return -1; 5090926Snectar} 5190926Snectar 5255682Smarkm/* 5390926Snectar * set `res' and `count' to the result of looking up SRV RR in DNS for 5490926Snectar * `proto', `proto', `realm' using `dns_type'. 5590926Snectar * if `port' != 0, force that port number 5655682Smarkm */ 5755682Smarkm 5890926Snectarstatic krb5_error_code 5990926Snectarsrv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count, 6090926Snectar const char *realm, const char *dns_type, 6190926Snectar const char *proto, const char *service, int port) 6255682Smarkm{ 6390926Snectar char domain[1024]; 6490926Snectar struct dns_reply *r; 6590926Snectar struct resource_record *rr; 6690926Snectar int num_srv; 6790926Snectar int proto_num; 6890926Snectar int def_port; 6955682Smarkm 70178825Sdfr *res = NULL; 71178825Sdfr *count = 0; 72178825Sdfr 7390926Snectar proto_num = string_to_proto(proto); 7490926Snectar if(proto_num < 0) { 7590926Snectar krb5_set_error_string(context, "unknown protocol `%s'", proto); 7690926Snectar return EINVAL; 7790926Snectar } 7890926Snectar 7990926Snectar if(proto_num == KRB5_KRBHST_HTTP) 8090926Snectar def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80)); 8190926Snectar else if(port == 0) 8290926Snectar def_port = ntohs(krb5_getportbyname (context, service, proto, 88)); 8390926Snectar else 8490926Snectar def_port = port; 8590926Snectar 8690926Snectar snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm); 8790926Snectar 8890926Snectar r = dns_lookup(domain, dns_type); 89178825Sdfr if(r == NULL) 9090926Snectar return KRB5_KDC_UNREACH; 9190926Snectar 9290926Snectar for(num_srv = 0, rr = r->head; rr; rr = rr->next) 9390926Snectar if(rr->type == T_SRV) 9490926Snectar num_srv++; 9590926Snectar 9690926Snectar *res = malloc(num_srv * sizeof(**res)); 9790926Snectar if(*res == NULL) { 9890926Snectar dns_free_data(r); 9990926Snectar krb5_set_error_string(context, "malloc: out of memory"); 10055682Smarkm return ENOMEM; 10178527Sassar } 10290926Snectar 10390926Snectar dns_srv_order(r); 10490926Snectar 10590926Snectar for(num_srv = 0, rr = r->head; rr; rr = rr->next) 10690926Snectar if(rr->type == T_SRV) { 10790926Snectar krb5_krbhst_info *hi; 108120945Snectar size_t len = strlen(rr->u.srv->target); 109120945Snectar 110120945Snectar hi = calloc(1, sizeof(*hi) + len); 11190926Snectar if(hi == NULL) { 11290926Snectar dns_free_data(r); 11390926Snectar while(--num_srv >= 0) 11490926Snectar free((*res)[num_srv]); 11590926Snectar free(*res); 116178825Sdfr *res = NULL; 11790926Snectar return ENOMEM; 11890926Snectar } 11990926Snectar (*res)[num_srv++] = hi; 12090926Snectar 12190926Snectar hi->proto = proto_num; 12290926Snectar 12390926Snectar hi->def_port = def_port; 12490926Snectar if (port != 0) 12590926Snectar hi->port = port; 12690926Snectar else 12790926Snectar hi->port = rr->u.srv->port; 12890926Snectar 129120945Snectar strlcpy(hi->hostname, rr->u.srv->target, len + 1); 13078527Sassar } 13190926Snectar 13290926Snectar *count = num_srv; 13390926Snectar 13490926Snectar dns_free_data(r); 13555682Smarkm return 0; 13655682Smarkm} 13755682Smarkm 13890926Snectar 13990926Snectarstruct krb5_krbhst_data { 14090926Snectar char *realm; 14190926Snectar unsigned int flags; 14290926Snectar int def_port; 14390926Snectar int port; /* hardwired port number if != 0 */ 144178825Sdfr#define KD_CONFIG 1 145178825Sdfr#define KD_SRV_UDP 2 146178825Sdfr#define KD_SRV_TCP 4 147178825Sdfr#define KD_SRV_HTTP 8 148178825Sdfr#define KD_FALLBACK 16 149178825Sdfr#define KD_CONFIG_EXISTS 32 150178825Sdfr#define KD_LARGE_MSG 64 151178825Sdfr#define KD_PLUGIN 128 15290926Snectar krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *, 15390926Snectar krb5_krbhst_info**); 15490926Snectar 15590926Snectar unsigned int fallback_count; 15690926Snectar 15790926Snectar struct krb5_krbhst_info *hosts, **index, **end; 15890926Snectar}; 15990926Snectar 16090926Snectarstatic krb5_boolean 16190926Snectarkrbhst_empty(const struct krb5_krbhst_data *kd) 16290926Snectar{ 16390926Snectar return kd->index == &kd->hosts; 16490926Snectar} 16590926Snectar 16672445Sassar/* 167178825Sdfr * Return the default protocol for the `kd' (either TCP or UDP) 168178825Sdfr */ 169178825Sdfr 170178825Sdfrstatic int 171178825Sdfrkrbhst_get_default_proto(struct krb5_krbhst_data *kd) 172178825Sdfr{ 173178825Sdfr if (kd->flags & KD_LARGE_MSG) 174178825Sdfr return KRB5_KRBHST_TCP; 175178825Sdfr return KRB5_KRBHST_UDP; 176178825Sdfr} 177178825Sdfr 178178825Sdfr 179178825Sdfr/* 18090926Snectar * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port' 18190926Snectar * and forcing it to `port' if port != 0 18272445Sassar */ 18372445Sassar 18490926Snectarstatic struct krb5_krbhst_info* 185178825Sdfrparse_hostspec(krb5_context context, struct krb5_krbhst_data *kd, 186178825Sdfr const char *spec, int def_port, int port) 18790926Snectar{ 18890926Snectar const char *p = spec; 18990926Snectar struct krb5_krbhst_info *hi; 19090926Snectar 19190926Snectar hi = calloc(1, sizeof(*hi) + strlen(spec)); 19290926Snectar if(hi == NULL) 19390926Snectar return NULL; 19490926Snectar 195178825Sdfr hi->proto = krbhst_get_default_proto(kd); 19690926Snectar 19790926Snectar if(strncmp(p, "http://", 7) == 0){ 19890926Snectar hi->proto = KRB5_KRBHST_HTTP; 19990926Snectar p += 7; 20090926Snectar } else if(strncmp(p, "http/", 5) == 0) { 20190926Snectar hi->proto = KRB5_KRBHST_HTTP; 20290926Snectar p += 5; 20390926Snectar def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80)); 20490926Snectar }else if(strncmp(p, "tcp/", 4) == 0){ 20590926Snectar hi->proto = KRB5_KRBHST_TCP; 20690926Snectar p += 4; 20790926Snectar } else if(strncmp(p, "udp/", 4) == 0) { 20890926Snectar p += 4; 20990926Snectar } 21090926Snectar 21190926Snectar if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) { 21290926Snectar free(hi); 21390926Snectar return NULL; 21490926Snectar } 21590926Snectar /* get rid of trailing /, and convert to lower case */ 21690926Snectar hi->hostname[strcspn(hi->hostname, "/")] = '\0'; 21790926Snectar strlwr(hi->hostname); 21890926Snectar 21990926Snectar hi->port = hi->def_port = def_port; 22090926Snectar if(p != NULL) { 22190926Snectar char *end; 22290926Snectar hi->port = strtol(p, &end, 0); 22390926Snectar if(end == p) { 22490926Snectar free(hi); 22590926Snectar return NULL; 22690926Snectar } 22790926Snectar } 22890926Snectar if (port) 22990926Snectar hi->port = port; 23090926Snectar return hi; 23190926Snectar} 23290926Snectar 233178825Sdfrvoid 234178825Sdfr_krb5_free_krbhst_info(krb5_krbhst_info *hi) 23590926Snectar{ 23690926Snectar if (hi->ai != NULL) 23790926Snectar freeaddrinfo(hi->ai); 23890926Snectar free(hi); 23990926Snectar} 24090926Snectar 241178825Sdfrkrb5_error_code 242178825Sdfr_krb5_krbhost_info_move(krb5_context context, 243178825Sdfr krb5_krbhst_info *from, 244178825Sdfr krb5_krbhst_info **to) 245178825Sdfr{ 246178825Sdfr size_t hostnamelen = strlen(from->hostname); 247178825Sdfr /* trailing NUL is included in structure */ 248178825Sdfr *to = calloc(1, sizeof(**to) + hostnamelen); 249178825Sdfr if(*to == NULL) { 250178825Sdfr krb5_set_error_string(context, "malloc - out of memory"); 251178825Sdfr return ENOMEM; 252178825Sdfr } 253178825Sdfr 254178825Sdfr (*to)->proto = from->proto; 255178825Sdfr (*to)->port = from->port; 256178825Sdfr (*to)->def_port = from->def_port; 257178825Sdfr (*to)->ai = from->ai; 258178825Sdfr from->ai = NULL; 259178825Sdfr (*to)->next = NULL; 260178825Sdfr memcpy((*to)->hostname, from->hostname, hostnamelen + 1); 261178825Sdfr return 0; 262178825Sdfr} 263178825Sdfr 264178825Sdfr 26590926Snectarstatic void 26690926Snectarappend_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host) 26790926Snectar{ 26890926Snectar struct krb5_krbhst_info *h; 26990926Snectar 27090926Snectar for(h = kd->hosts; h; h = h->next) 27190926Snectar if(h->proto == host->proto && 27290926Snectar h->port == host->port && 27390926Snectar strcmp(h->hostname, host->hostname) == 0) { 274178825Sdfr _krb5_free_krbhst_info(host); 27590926Snectar return; 27690926Snectar } 27790926Snectar *kd->end = host; 27890926Snectar kd->end = &host->next; 27990926Snectar} 28090926Snectar 28155682Smarkmstatic krb5_error_code 28290926Snectarappend_host_string(krb5_context context, struct krb5_krbhst_data *kd, 28390926Snectar const char *host, int def_port, int port) 28455682Smarkm{ 28590926Snectar struct krb5_krbhst_info *hi; 28655682Smarkm 287178825Sdfr hi = parse_hostspec(context, kd, host, def_port, port); 28890926Snectar if(hi == NULL) 28990926Snectar return ENOMEM; 29055682Smarkm 29190926Snectar append_host_hostinfo(kd, hi); 29290926Snectar return 0; 29390926Snectar} 29490926Snectar 29590926Snectar/* 29690926Snectar * return a readable representation of `host' in `hostname, hostlen' 29790926Snectar */ 29890926Snectar 299178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 30090926Snectarkrb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host, 30190926Snectar char *hostname, size_t hostlen) 30290926Snectar{ 30390926Snectar const char *proto = ""; 30490926Snectar char portstr[7] = ""; 30590926Snectar if(host->proto == KRB5_KRBHST_TCP) 30690926Snectar proto = "tcp/"; 30790926Snectar else if(host->proto == KRB5_KRBHST_HTTP) 30890926Snectar proto = "http://"; 30990926Snectar if(host->port != host->def_port) 31090926Snectar snprintf(portstr, sizeof(portstr), ":%d", host->port); 31190926Snectar snprintf(hostname, hostlen, "%s%s%s", proto, host->hostname, portstr); 31290926Snectar return 0; 31390926Snectar} 31490926Snectar 31590926Snectar/* 31690926Snectar * create a getaddrinfo `hints' based on `proto' 31790926Snectar */ 31890926Snectar 31990926Snectarstatic void 32090926Snectarmake_hints(struct addrinfo *hints, int proto) 32190926Snectar{ 32290926Snectar memset(hints, 0, sizeof(*hints)); 32390926Snectar hints->ai_family = AF_UNSPEC; 32490926Snectar switch(proto) { 32590926Snectar case KRB5_KRBHST_UDP : 32690926Snectar hints->ai_socktype = SOCK_DGRAM; 32790926Snectar break; 32890926Snectar case KRB5_KRBHST_HTTP : 32990926Snectar case KRB5_KRBHST_TCP : 33090926Snectar hints->ai_socktype = SOCK_STREAM; 33190926Snectar break; 33255682Smarkm } 33390926Snectar} 33455682Smarkm 33590926Snectar/* 33690926Snectar * return an `struct addrinfo *' in `ai' corresponding to the information 33790926Snectar * in `host'. free:ing is handled by krb5_krbhst_free. 33890926Snectar */ 33955682Smarkm 340178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 34190926Snectarkrb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host, 34290926Snectar struct addrinfo **ai) 34390926Snectar{ 34490926Snectar struct addrinfo hints; 34590926Snectar char portstr[NI_MAXSERV]; 34690926Snectar int ret; 34790926Snectar 34890926Snectar if (host->ai == NULL) { 34990926Snectar make_hints(&hints, host->proto); 35090926Snectar snprintf (portstr, sizeof(portstr), "%d", host->port); 35190926Snectar ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai); 35290926Snectar if (ret) 35390926Snectar return krb5_eai_to_heim_errno(ret, errno); 35455682Smarkm } 35590926Snectar *ai = host->ai; 35655682Smarkm return 0; 35755682Smarkm} 35855682Smarkm 35990926Snectarstatic krb5_boolean 36090926Snectarget_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host) 36190926Snectar{ 36290926Snectar struct krb5_krbhst_info *hi = *kd->index; 36390926Snectar if(hi != NULL) { 36490926Snectar *host = hi; 36590926Snectar kd->index = &(*kd->index)->next; 36690926Snectar return TRUE; 36790926Snectar } 36890926Snectar return FALSE; 36990926Snectar} 37090926Snectar 37190926Snectarstatic void 37290926Snectarsrv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, 373178825Sdfr const char *proto, const char *service) 37490926Snectar{ 37590926Snectar krb5_krbhst_info **res; 37690926Snectar int count, i; 37790926Snectar 378178825Sdfr if (srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service, 379178825Sdfr kd->port)) 380178825Sdfr return; 38190926Snectar for(i = 0; i < count; i++) 38290926Snectar append_host_hostinfo(kd, res[i]); 38390926Snectar free(res); 38490926Snectar} 38590926Snectar 38655682Smarkm/* 38790926Snectar * read the configuration for `conf_string', defaulting to kd->def_port and 38890926Snectar * forcing it to `kd->port' if kd->port != 0 38955682Smarkm */ 39055682Smarkm 39190926Snectarstatic void 39290926Snectarconfig_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, 39390926Snectar const char *conf_string) 39490926Snectar{ 39590926Snectar int i; 39690926Snectar 39790926Snectar char **hostlist; 39890926Snectar hostlist = krb5_config_get_strings(context, NULL, 39990926Snectar "realms", kd->realm, conf_string, NULL); 40090926Snectar 40190926Snectar if(hostlist == NULL) 40290926Snectar return; 40390926Snectar kd->flags |= KD_CONFIG_EXISTS; 40490926Snectar for(i = 0; hostlist && hostlist[i] != NULL; i++) 40590926Snectar append_host_string(context, kd, hostlist[i], kd->def_port, kd->port); 40690926Snectar 40790926Snectar krb5_config_free_strings(hostlist); 40890926Snectar} 40990926Snectar 41090926Snectar/* 41190926Snectar * as a fallback, look for `serv_string.kd->realm' (typically 41290926Snectar * kerberos.REALM, kerberos-1.REALM, ... 41390926Snectar * `port' is the default port for the service, and `proto' the 41490926Snectar * protocol 41590926Snectar */ 41690926Snectar 41755682Smarkmstatic krb5_error_code 41890926Snectarfallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, 41990926Snectar const char *serv_string, int port, int proto) 42055682Smarkm{ 42190926Snectar char *host; 42290926Snectar int ret; 42390926Snectar struct addrinfo *ai; 42490926Snectar struct addrinfo hints; 42590926Snectar char portstr[NI_MAXSERV]; 42690926Snectar 427178825Sdfr /* 428178825Sdfr * Don't try forever in case the DNS server keep returning us 429178825Sdfr * entries (like wildcard entries or the .nu TLD) 430178825Sdfr */ 431178825Sdfr if(kd->fallback_count >= 5) { 432178825Sdfr kd->flags |= KD_FALLBACK; 433178825Sdfr return 0; 434178825Sdfr } 435178825Sdfr 43690926Snectar if(kd->fallback_count == 0) 43790926Snectar asprintf(&host, "%s.%s.", serv_string, kd->realm); 43890926Snectar else 43990926Snectar asprintf(&host, "%s-%d.%s.", 44090926Snectar serv_string, kd->fallback_count, kd->realm); 44190926Snectar 44290926Snectar if (host == NULL) 44390926Snectar return ENOMEM; 44490926Snectar 44590926Snectar make_hints(&hints, proto); 44690926Snectar snprintf(portstr, sizeof(portstr), "%d", port); 44790926Snectar ret = getaddrinfo(host, portstr, &hints, &ai); 44890926Snectar if (ret) { 44990926Snectar /* no more hosts, so we're done here */ 45090926Snectar free(host); 45190926Snectar kd->flags |= KD_FALLBACK; 45290926Snectar } else { 45390926Snectar struct krb5_krbhst_info *hi; 45490926Snectar size_t hostlen = strlen(host); 45590926Snectar 45690926Snectar hi = calloc(1, sizeof(*hi) + hostlen); 45790926Snectar if(hi == NULL) { 45890926Snectar free(host); 45990926Snectar return ENOMEM; 46090926Snectar } 46190926Snectar 46290926Snectar hi->proto = proto; 46390926Snectar hi->port = hi->def_port = port; 46490926Snectar hi->ai = ai; 465178825Sdfr memmove(hi->hostname, host, hostlen); 466178825Sdfr hi->hostname[hostlen] = '\0'; 46790926Snectar free(host); 46890926Snectar append_host_hostinfo(kd, hi); 46990926Snectar kd->fallback_count++; 47090926Snectar } 47190926Snectar return 0; 47290926Snectar} 47390926Snectar 474178825Sdfr/* 475178825Sdfr * Fetch hosts from plugin 476178825Sdfr */ 477178825Sdfr 478178825Sdfrstatic krb5_error_code 479178825Sdfradd_locate(void *ctx, int type, struct sockaddr *addr) 480178825Sdfr{ 481178825Sdfr struct krb5_krbhst_info *hi; 482178825Sdfr struct krb5_krbhst_data *kd = ctx; 483178825Sdfr char host[NI_MAXHOST], port[NI_MAXSERV]; 484178825Sdfr struct addrinfo hints, *ai; 485178825Sdfr socklen_t socklen; 486178825Sdfr size_t hostlen; 487178825Sdfr int ret; 488178825Sdfr 489178825Sdfr socklen = socket_sockaddr_size(addr); 490178825Sdfr 491178825Sdfr ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port), 492178825Sdfr NI_NUMERICHOST|NI_NUMERICSERV); 493178825Sdfr if (ret != 0) 494178825Sdfr return 0; 495178825Sdfr 496178825Sdfr make_hints(&hints, krbhst_get_default_proto(kd)); 497178825Sdfr ret = getaddrinfo(host, port, &hints, &ai); 498178825Sdfr if (ret) 499178825Sdfr return 0; 500178825Sdfr 501178825Sdfr hostlen = strlen(host); 502178825Sdfr 503178825Sdfr hi = calloc(1, sizeof(*hi) + hostlen); 504178825Sdfr if(hi == NULL) 505178825Sdfr return ENOMEM; 506178825Sdfr 507178825Sdfr hi->proto = krbhst_get_default_proto(kd); 508178825Sdfr hi->port = hi->def_port = socket_get_port(addr); 509178825Sdfr hi->ai = ai; 510178825Sdfr memmove(hi->hostname, host, hostlen); 511178825Sdfr hi->hostname[hostlen] = '\0'; 512178825Sdfr append_host_hostinfo(kd, hi); 513178825Sdfr 514178825Sdfr return 0; 515178825Sdfr} 516178825Sdfr 517178825Sdfrstatic void 518178825Sdfrplugin_get_hosts(krb5_context context, 519178825Sdfr struct krb5_krbhst_data *kd, 520178825Sdfr enum locate_service_type type) 521178825Sdfr{ 522178825Sdfr struct krb5_plugin *list = NULL, *e; 523178825Sdfr krb5_error_code ret; 524178825Sdfr 525178825Sdfr ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "resolve", &list); 526178825Sdfr if(ret != 0 || list == NULL) 527178825Sdfr return; 528178825Sdfr 529178825Sdfr kd->flags |= KD_CONFIG_EXISTS; 530178825Sdfr 531178825Sdfr for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) { 532178825Sdfr krb5plugin_service_locate_ftable *service; 533178825Sdfr void *ctx; 534178825Sdfr 535178825Sdfr service = _krb5_plugin_get_symbol(e); 536178825Sdfr if (service->minor_version != 0) 537178825Sdfr continue; 538178825Sdfr 539178825Sdfr (*service->init)(context, &ctx); 540178825Sdfr ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd); 541178825Sdfr (*service->fini)(ctx); 542178825Sdfr if (ret) { 543178825Sdfr krb5_set_error_string(context, "Plugin failed to lookup"); 544178825Sdfr break; 545178825Sdfr } 546178825Sdfr } 547178825Sdfr _krb5_plugin_free(list); 548178825Sdfr} 549178825Sdfr 550178825Sdfr/* 551178825Sdfr * 552178825Sdfr */ 553178825Sdfr 55490926Snectarstatic krb5_error_code 55590926Snectarkdc_get_next(krb5_context context, 55690926Snectar struct krb5_krbhst_data *kd, 55790926Snectar krb5_krbhst_info **host) 55890926Snectar{ 55955682Smarkm krb5_error_code ret; 56055682Smarkm 561178825Sdfr if ((kd->flags & KD_PLUGIN) == 0) { 562178825Sdfr plugin_get_hosts(context, kd, locate_service_kdc); 563178825Sdfr kd->flags |= KD_PLUGIN; 564178825Sdfr if(get_next(kd, host)) 565178825Sdfr return 0; 566178825Sdfr } 567178825Sdfr 56890926Snectar if((kd->flags & KD_CONFIG) == 0) { 56990926Snectar config_get_hosts(context, kd, "kdc"); 57090926Snectar kd->flags |= KD_CONFIG; 57190926Snectar if(get_next(kd, host)) 57290926Snectar return 0; 57390926Snectar } 57455682Smarkm 57590926Snectar if (kd->flags & KD_CONFIG_EXISTS) 57690926Snectar return KRB5_KDC_UNREACH; /* XXX */ 57790926Snectar 57890926Snectar if(context->srv_lookup) { 579178825Sdfr if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) { 58090926Snectar srv_get_hosts(context, kd, "udp", "kerberos"); 58190926Snectar kd->flags |= KD_SRV_UDP; 58290926Snectar if(get_next(kd, host)) 58390926Snectar return 0; 58455682Smarkm } 58590926Snectar 58690926Snectar if((kd->flags & KD_SRV_TCP) == 0) { 58790926Snectar srv_get_hosts(context, kd, "tcp", "kerberos"); 58890926Snectar kd->flags |= KD_SRV_TCP; 58990926Snectar if(get_next(kd, host)) 59090926Snectar return 0; 59190926Snectar } 59290926Snectar if((kd->flags & KD_SRV_HTTP) == 0) { 59390926Snectar srv_get_hosts(context, kd, "http", "kerberos"); 59490926Snectar kd->flags |= KD_SRV_HTTP; 59590926Snectar if(get_next(kd, host)) 59690926Snectar return 0; 59790926Snectar } 59855682Smarkm } 59955682Smarkm 60090926Snectar while((kd->flags & KD_FALLBACK) == 0) { 60190926Snectar ret = fallback_get_hosts(context, kd, "kerberos", 602178825Sdfr kd->def_port, 603178825Sdfr krbhst_get_default_proto(kd)); 60490926Snectar if(ret) 60555682Smarkm return ret; 60690926Snectar if(get_next(kd, host)) 60790926Snectar return 0; 60890926Snectar } 60990926Snectar 61090926Snectar return KRB5_KDC_UNREACH; /* XXX */ 61190926Snectar} 61290926Snectar 61390926Snectarstatic krb5_error_code 61490926Snectaradmin_get_next(krb5_context context, 61590926Snectar struct krb5_krbhst_data *kd, 61690926Snectar krb5_krbhst_info **host) 61790926Snectar{ 61890926Snectar krb5_error_code ret; 61990926Snectar 620178825Sdfr if ((kd->flags & KD_PLUGIN) == 0) { 621178825Sdfr plugin_get_hosts(context, kd, locate_service_kadmin); 622178825Sdfr kd->flags |= KD_PLUGIN; 623178825Sdfr if(get_next(kd, host)) 624178825Sdfr return 0; 625178825Sdfr } 626178825Sdfr 62790926Snectar if((kd->flags & KD_CONFIG) == 0) { 62890926Snectar config_get_hosts(context, kd, "admin_server"); 62990926Snectar kd->flags |= KD_CONFIG; 63090926Snectar if(get_next(kd, host)) 63190926Snectar return 0; 63290926Snectar } 63390926Snectar 63490926Snectar if (kd->flags & KD_CONFIG_EXISTS) 63590926Snectar return KRB5_KDC_UNREACH; /* XXX */ 63690926Snectar 63790926Snectar if(context->srv_lookup) { 63890926Snectar if((kd->flags & KD_SRV_TCP) == 0) { 63990926Snectar srv_get_hosts(context, kd, "tcp", "kerberos-adm"); 64090926Snectar kd->flags |= KD_SRV_TCP; 64190926Snectar if(get_next(kd, host)) 64290926Snectar return 0; 64355682Smarkm } 64455682Smarkm } 64590926Snectar 64690926Snectar if (krbhst_empty(kd) 64790926Snectar && (kd->flags & KD_FALLBACK) == 0) { 64890926Snectar ret = fallback_get_hosts(context, kd, "kerberos", 649178825Sdfr kd->def_port, 650178825Sdfr krbhst_get_default_proto(kd)); 65190926Snectar if(ret) 65290926Snectar return ret; 65390926Snectar kd->flags |= KD_FALLBACK; 65490926Snectar if(get_next(kd, host)) 65590926Snectar return 0; 65690926Snectar } 65790926Snectar 65890926Snectar return KRB5_KDC_UNREACH; /* XXX */ 65990926Snectar} 66090926Snectar 66190926Snectarstatic krb5_error_code 66290926Snectarkpasswd_get_next(krb5_context context, 66390926Snectar struct krb5_krbhst_data *kd, 66490926Snectar krb5_krbhst_info **host) 66590926Snectar{ 666102644Snectar krb5_error_code ret; 667102644Snectar 668178825Sdfr if ((kd->flags & KD_PLUGIN) == 0) { 669178825Sdfr plugin_get_hosts(context, kd, locate_service_kpasswd); 670178825Sdfr kd->flags |= KD_PLUGIN; 671178825Sdfr if(get_next(kd, host)) 672178825Sdfr return 0; 673178825Sdfr } 674178825Sdfr 67590926Snectar if((kd->flags & KD_CONFIG) == 0) { 67690926Snectar config_get_hosts(context, kd, "kpasswd_server"); 677178825Sdfr kd->flags |= KD_CONFIG; 67890926Snectar if(get_next(kd, host)) 67990926Snectar return 0; 68090926Snectar } 68190926Snectar 68290926Snectar if (kd->flags & KD_CONFIG_EXISTS) 68390926Snectar return KRB5_KDC_UNREACH; /* XXX */ 68490926Snectar 68590926Snectar if(context->srv_lookup) { 68690926Snectar if((kd->flags & KD_SRV_UDP) == 0) { 68790926Snectar srv_get_hosts(context, kd, "udp", "kpasswd"); 68890926Snectar kd->flags |= KD_SRV_UDP; 68990926Snectar if(get_next(kd, host)) 69090926Snectar return 0; 69190926Snectar } 692178825Sdfr if((kd->flags & KD_SRV_TCP) == 0) { 693178825Sdfr srv_get_hosts(context, kd, "tcp", "kpasswd"); 694178825Sdfr kd->flags |= KD_SRV_TCP; 695178825Sdfr if(get_next(kd, host)) 696178825Sdfr return 0; 697178825Sdfr } 69890926Snectar } 69990926Snectar 70090926Snectar /* no matches -> try admin */ 70190926Snectar 70290926Snectar if (krbhst_empty(kd)) { 70390926Snectar kd->flags = 0; 70490926Snectar kd->port = kd->def_port; 70590926Snectar kd->get_next = admin_get_next; 706102644Snectar ret = (*kd->get_next)(context, kd, host); 707102644Snectar if (ret == 0) 708178825Sdfr (*host)->proto = krbhst_get_default_proto(kd); 709102644Snectar return ret; 71090926Snectar } 71190926Snectar 71290926Snectar return KRB5_KDC_UNREACH; /* XXX */ 71390926Snectar} 71490926Snectar 71590926Snectarstatic krb5_error_code 71690926Snectarkrb524_get_next(krb5_context context, 71790926Snectar struct krb5_krbhst_data *kd, 71890926Snectar krb5_krbhst_info **host) 71990926Snectar{ 720178825Sdfr if ((kd->flags & KD_PLUGIN) == 0) { 721178825Sdfr plugin_get_hosts(context, kd, locate_service_krb524); 722178825Sdfr kd->flags |= KD_PLUGIN; 723178825Sdfr if(get_next(kd, host)) 724178825Sdfr return 0; 725178825Sdfr } 726178825Sdfr 72790926Snectar if((kd->flags & KD_CONFIG) == 0) { 72890926Snectar config_get_hosts(context, kd, "krb524_server"); 72990926Snectar if(get_next(kd, host)) 73090926Snectar return 0; 73190926Snectar kd->flags |= KD_CONFIG; 73290926Snectar } 73390926Snectar 73490926Snectar if (kd->flags & KD_CONFIG_EXISTS) 73590926Snectar return KRB5_KDC_UNREACH; /* XXX */ 73690926Snectar 73790926Snectar if(context->srv_lookup) { 73890926Snectar if((kd->flags & KD_SRV_UDP) == 0) { 73990926Snectar srv_get_hosts(context, kd, "udp", "krb524"); 74090926Snectar kd->flags |= KD_SRV_UDP; 74190926Snectar if(get_next(kd, host)) 74290926Snectar return 0; 74390926Snectar } 74490926Snectar 74590926Snectar if((kd->flags & KD_SRV_TCP) == 0) { 74690926Snectar srv_get_hosts(context, kd, "tcp", "krb524"); 74790926Snectar kd->flags |= KD_SRV_TCP; 74890926Snectar if(get_next(kd, host)) 74990926Snectar return 0; 75090926Snectar } 75190926Snectar } 75290926Snectar 75390926Snectar /* no matches -> try kdc */ 75490926Snectar 75590926Snectar if (krbhst_empty(kd)) { 75690926Snectar kd->flags = 0; 75790926Snectar kd->port = kd->def_port; 75890926Snectar kd->get_next = kdc_get_next; 75990926Snectar return (*kd->get_next)(context, kd, host); 76090926Snectar } 76190926Snectar 76290926Snectar return KRB5_KDC_UNREACH; /* XXX */ 76390926Snectar} 76490926Snectar 76590926Snectarstatic struct krb5_krbhst_data* 76690926Snectarcommon_init(krb5_context context, 767178825Sdfr const char *realm, 768178825Sdfr int flags) 76990926Snectar{ 77090926Snectar struct krb5_krbhst_data *kd; 77190926Snectar 77290926Snectar if((kd = calloc(1, sizeof(*kd))) == NULL) 77390926Snectar return NULL; 77490926Snectar 77590926Snectar if((kd->realm = strdup(realm)) == NULL) { 77690926Snectar free(kd); 77790926Snectar return NULL; 77890926Snectar } 77990926Snectar 780178825Sdfr /* For 'realms' without a . do not even think of going to DNS */ 781178825Sdfr if (!strchr(realm, '.')) 782178825Sdfr kd->flags |= KD_CONFIG_EXISTS; 783178825Sdfr 784178825Sdfr if (flags & KRB5_KRBHST_FLAGS_LARGE_MSG) 785178825Sdfr kd->flags |= KD_LARGE_MSG; 78690926Snectar kd->end = kd->index = &kd->hosts; 78790926Snectar return kd; 78890926Snectar} 78990926Snectar 79090926Snectar/* 79190926Snectar * initialize `handle' to look for hosts of type `type' in realm `realm' 79290926Snectar */ 79390926Snectar 794178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 79590926Snectarkrb5_krbhst_init(krb5_context context, 79690926Snectar const char *realm, 79790926Snectar unsigned int type, 79890926Snectar krb5_krbhst_handle *handle) 79990926Snectar{ 800178825Sdfr return krb5_krbhst_init_flags(context, realm, type, 0, handle); 801178825Sdfr} 802178825Sdfr 803178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 804178825Sdfrkrb5_krbhst_init_flags(krb5_context context, 805178825Sdfr const char *realm, 806178825Sdfr unsigned int type, 807178825Sdfr int flags, 808178825Sdfr krb5_krbhst_handle *handle) 809178825Sdfr{ 81090926Snectar struct krb5_krbhst_data *kd; 811178825Sdfr krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *, 812178825Sdfr krb5_krbhst_info **); 81390926Snectar int def_port; 81490926Snectar 81590926Snectar switch(type) { 81690926Snectar case KRB5_KRBHST_KDC: 817178825Sdfr next = kdc_get_next; 81890926Snectar def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88)); 81990926Snectar break; 82090926Snectar case KRB5_KRBHST_ADMIN: 821178825Sdfr next = admin_get_next; 82290926Snectar def_port = ntohs(krb5_getportbyname (context, "kerberos-adm", 82390926Snectar "tcp", 749)); 82490926Snectar break; 82590926Snectar case KRB5_KRBHST_CHANGEPW: 826178825Sdfr next = kpasswd_get_next; 82790926Snectar def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp", 82890926Snectar KPASSWD_PORT)); 82990926Snectar break; 83090926Snectar case KRB5_KRBHST_KRB524: 831178825Sdfr next = krb524_get_next; 83290926Snectar def_port = ntohs(krb5_getportbyname (context, "krb524", "udp", 4444)); 83390926Snectar break; 83490926Snectar default: 83590926Snectar krb5_set_error_string(context, "unknown krbhst type (%u)", type); 83690926Snectar return ENOTTY; 83790926Snectar } 838178825Sdfr if((kd = common_init(context, realm, flags)) == NULL) 83990926Snectar return ENOMEM; 840178825Sdfr kd->get_next = next; 84190926Snectar kd->def_port = def_port; 84290926Snectar *handle = kd; 84355682Smarkm return 0; 84455682Smarkm} 84555682Smarkm 84672445Sassar/* 84790926Snectar * return the next host information from `handle' in `host' 84872445Sassar */ 84972445Sassar 850178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 85190926Snectarkrb5_krbhst_next(krb5_context context, 85290926Snectar krb5_krbhst_handle handle, 85390926Snectar krb5_krbhst_info **host) 85490926Snectar{ 85590926Snectar if(get_next(handle, host)) 85690926Snectar return 0; 85790926Snectar 85890926Snectar return (*handle->get_next)(context, handle, host); 85990926Snectar} 86090926Snectar 86190926Snectar/* 86290926Snectar * return the next host information from `handle' as a host name 86390926Snectar * in `hostname' (or length `hostlen) 86490926Snectar */ 86590926Snectar 866178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 86790926Snectarkrb5_krbhst_next_as_string(krb5_context context, 86890926Snectar krb5_krbhst_handle handle, 86990926Snectar char *hostname, 87090926Snectar size_t hostlen) 87190926Snectar{ 87290926Snectar krb5_error_code ret; 87390926Snectar krb5_krbhst_info *host; 87490926Snectar ret = krb5_krbhst_next(context, handle, &host); 87590926Snectar if(ret) 87690926Snectar return ret; 87790926Snectar return krb5_krbhst_format_string(context, host, hostname, hostlen); 87890926Snectar} 87990926Snectar 88090926Snectar 881178825Sdfrvoid KRB5_LIB_FUNCTION 88290926Snectarkrb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle) 88390926Snectar{ 88490926Snectar handle->index = &handle->hosts; 88590926Snectar} 88690926Snectar 887178825Sdfrvoid KRB5_LIB_FUNCTION 88890926Snectarkrb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle) 88990926Snectar{ 89090926Snectar krb5_krbhst_info *h, *next; 89190926Snectar 89290926Snectar if (handle == NULL) 89390926Snectar return; 89490926Snectar 89590926Snectar for (h = handle->hosts; h != NULL; h = next) { 89690926Snectar next = h->next; 897178825Sdfr _krb5_free_krbhst_info(h); 89890926Snectar } 89990926Snectar 90090926Snectar free(handle->realm); 90190926Snectar free(handle); 90290926Snectar} 90390926Snectar 90490926Snectar/* backwards compatibility ahead */ 90590926Snectar 90690926Snectarstatic krb5_error_code 90790926Snectargethostlist(krb5_context context, const char *realm, 90890926Snectar unsigned int type, char ***hostlist) 90990926Snectar{ 91090926Snectar krb5_error_code ret; 91190926Snectar int nhost = 0; 91290926Snectar krb5_krbhst_handle handle; 91390926Snectar char host[MAXHOSTNAMELEN]; 91490926Snectar krb5_krbhst_info *hostinfo; 91590926Snectar 91690926Snectar ret = krb5_krbhst_init(context, realm, type, &handle); 91790926Snectar if (ret) 91890926Snectar return ret; 91990926Snectar 92090926Snectar while(krb5_krbhst_next(context, handle, &hostinfo) == 0) 92190926Snectar nhost++; 922178825Sdfr if(nhost == 0) { 923178825Sdfr krb5_set_error_string(context, "No KDC found for realm %s", realm); 92490926Snectar return KRB5_KDC_UNREACH; 925178825Sdfr } 92690926Snectar *hostlist = calloc(nhost + 1, sizeof(**hostlist)); 92790926Snectar if(*hostlist == NULL) { 92890926Snectar krb5_krbhst_free(context, handle); 92990926Snectar return ENOMEM; 93090926Snectar } 93190926Snectar 93290926Snectar krb5_krbhst_reset(context, handle); 93390926Snectar nhost = 0; 93490926Snectar while(krb5_krbhst_next_as_string(context, handle, 93590926Snectar host, sizeof(host)) == 0) { 93690926Snectar if(((*hostlist)[nhost++] = strdup(host)) == NULL) { 93790926Snectar krb5_free_krbhst(context, *hostlist); 93890926Snectar krb5_krbhst_free(context, handle); 93990926Snectar return ENOMEM; 94090926Snectar } 94190926Snectar } 94290926Snectar (*hostlist)[nhost++] = NULL; 94390926Snectar krb5_krbhst_free(context, handle); 94490926Snectar return 0; 94590926Snectar} 94690926Snectar 94790926Snectar/* 94890926Snectar * return an malloced list of kadmin-hosts for `realm' in `hostlist' 94990926Snectar */ 95090926Snectar 951178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 95255682Smarkmkrb5_get_krb_admin_hst (krb5_context context, 95355682Smarkm const krb5_realm *realm, 95455682Smarkm char ***hostlist) 95555682Smarkm{ 95690926Snectar return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist); 95755682Smarkm} 95855682Smarkm 95972445Sassar/* 96090926Snectar * return an malloced list of changepw-hosts for `realm' in `hostlist' 96172445Sassar */ 96272445Sassar 963178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 96455682Smarkmkrb5_get_krb_changepw_hst (krb5_context context, 96555682Smarkm const krb5_realm *realm, 96655682Smarkm char ***hostlist) 96755682Smarkm{ 96890926Snectar return gethostlist(context, *realm, KRB5_KRBHST_CHANGEPW, hostlist); 96990926Snectar} 97072445Sassar 97190926Snectar/* 97290926Snectar * return an malloced list of 524-hosts for `realm' in `hostlist' 97390926Snectar */ 97490926Snectar 975178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 97690926Snectarkrb5_get_krb524hst (krb5_context context, 97790926Snectar const krb5_realm *realm, 97890926Snectar char ***hostlist) 97990926Snectar{ 98090926Snectar return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist); 98155682Smarkm} 98255682Smarkm 98390926Snectar 98472445Sassar/* 98590926Snectar * return an malloced list of KDC's for `realm' in `hostlist' 98672445Sassar */ 98772445Sassar 988178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 98955682Smarkmkrb5_get_krbhst (krb5_context context, 99055682Smarkm const krb5_realm *realm, 99155682Smarkm char ***hostlist) 99255682Smarkm{ 99390926Snectar return gethostlist(context, *realm, KRB5_KRBHST_KDC, hostlist); 99455682Smarkm} 99555682Smarkm 99672445Sassar/* 99790926Snectar * free all the memory allocated in `hostlist' 99872445Sassar */ 99972445Sassar 1000178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION 100155682Smarkmkrb5_free_krbhst (krb5_context context, 100255682Smarkm char **hostlist) 100355682Smarkm{ 100455682Smarkm char **p; 100555682Smarkm 100655682Smarkm for (p = hostlist; *p; ++p) 100755682Smarkm free (*p); 100855682Smarkm free (hostlist); 100955682Smarkm return 0; 101055682Smarkm} 1011