Path: blob/main/crypto/heimdal/lib/krb5/addr_families.c
34878 views
/*1* Copyright (c) 1997-2007 Kungliga Tekniska Högskolan2* (Royal Institute of Technology, Stockholm, Sweden).3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8*9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11*12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* 3. Neither the name of the Institute nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#include "krb5_locl.h"3435struct addr_operations {36int af;37krb5_address_type atype;38size_t max_sockaddr_size;39krb5_error_code (*sockaddr2addr)(const struct sockaddr *, krb5_address *);40krb5_error_code (*sockaddr2port)(const struct sockaddr *, int16_t *);41void (*addr2sockaddr)(const krb5_address *, struct sockaddr *,42krb5_socklen_t *sa_size, int port);43void (*h_addr2sockaddr)(const char *, struct sockaddr *, krb5_socklen_t *, int);44krb5_error_code (*h_addr2addr)(const char *, krb5_address *);45krb5_boolean (*uninteresting)(const struct sockaddr *);46krb5_boolean (*is_loopback)(const struct sockaddr *);47void (*anyaddr)(struct sockaddr *, krb5_socklen_t *, int);48int (*print_addr)(const krb5_address *, char *, size_t);49int (*parse_addr)(krb5_context, const char*, krb5_address *);50int (*order_addr)(krb5_context, const krb5_address*, const krb5_address*);51int (*free_addr)(krb5_context, krb5_address*);52int (*copy_addr)(krb5_context, const krb5_address*, krb5_address*);53int (*mask_boundary)(krb5_context, const krb5_address*, unsigned long,54krb5_address*, krb5_address*);55};5657/*58* AF_INET - aka IPv4 implementation59*/6061static krb5_error_code62ipv4_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)63{64const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;65unsigned char buf[4];6667a->addr_type = KRB5_ADDRESS_INET;68memcpy (buf, &sin4->sin_addr, 4);69return krb5_data_copy(&a->address, buf, 4);70}7172static krb5_error_code73ipv4_sockaddr2port (const struct sockaddr *sa, int16_t *port)74{75const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;7677*port = sin4->sin_port;78return 0;79}8081static void82ipv4_addr2sockaddr (const krb5_address *a,83struct sockaddr *sa,84krb5_socklen_t *sa_size,85int port)86{87struct sockaddr_in tmp;8889memset (&tmp, 0, sizeof(tmp));90tmp.sin_family = AF_INET;91memcpy (&tmp.sin_addr, a->address.data, 4);92tmp.sin_port = port;93memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));94*sa_size = sizeof(tmp);95}9697static void98ipv4_h_addr2sockaddr(const char *addr,99struct sockaddr *sa,100krb5_socklen_t *sa_size,101int port)102{103struct sockaddr_in tmp;104105memset (&tmp, 0, sizeof(tmp));106tmp.sin_family = AF_INET;107tmp.sin_port = port;108tmp.sin_addr = *((const struct in_addr *)addr);109memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));110*sa_size = sizeof(tmp);111}112113static krb5_error_code114ipv4_h_addr2addr (const char *addr,115krb5_address *a)116{117unsigned char buf[4];118119a->addr_type = KRB5_ADDRESS_INET;120memcpy(buf, addr, 4);121return krb5_data_copy(&a->address, buf, 4);122}123124/*125* Are there any addresses that should be considered `uninteresting'?126*/127128static krb5_boolean129ipv4_uninteresting (const struct sockaddr *sa)130{131const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;132133if (sin4->sin_addr.s_addr == INADDR_ANY)134return TRUE;135136return FALSE;137}138139static krb5_boolean140ipv4_is_loopback (const struct sockaddr *sa)141{142const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa;143144if ((ntohl(sin4->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET)145return TRUE;146147return FALSE;148}149150static void151ipv4_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)152{153struct sockaddr_in tmp;154155memset (&tmp, 0, sizeof(tmp));156tmp.sin_family = AF_INET;157tmp.sin_port = port;158tmp.sin_addr.s_addr = INADDR_ANY;159memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));160*sa_size = sizeof(tmp);161}162163static int164ipv4_print_addr (const krb5_address *addr, char *str, size_t len)165{166struct in_addr ia;167168memcpy (&ia, addr->address.data, 4);169170return snprintf (str, len, "IPv4:%s", inet_ntoa(ia));171}172173static int174ipv4_parse_addr (krb5_context context, const char *address, krb5_address *addr)175{176const char *p;177struct in_addr a;178179p = strchr(address, ':');180if(p) {181p++;182if(strncasecmp(address, "ip:", p - address) != 0 &&183strncasecmp(address, "ip4:", p - address) != 0 &&184strncasecmp(address, "ipv4:", p - address) != 0 &&185strncasecmp(address, "inet:", p - address) != 0)186return -1;187} else188p = address;189if(inet_aton(p, &a) == 0)190return -1;191addr->addr_type = KRB5_ADDRESS_INET;192if(krb5_data_alloc(&addr->address, 4) != 0)193return -1;194_krb5_put_int(addr->address.data, ntohl(a.s_addr), addr->address.length);195return 0;196}197198static int199ipv4_mask_boundary(krb5_context context, const krb5_address *inaddr,200unsigned long len, krb5_address *low, krb5_address *high)201{202unsigned long ia;203uint32_t l, h, m = 0xffffffff;204205if (len > 32) {206krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,207N_("IPv4 prefix too large (%ld)", "len"), len);208return KRB5_PROG_ATYPE_NOSUPP;209}210m = m << (32 - len);211212_krb5_get_int(inaddr->address.data, &ia, inaddr->address.length);213214l = ia & m;215h = l | ~m;216217low->addr_type = KRB5_ADDRESS_INET;218if(krb5_data_alloc(&low->address, 4) != 0)219return -1;220_krb5_put_int(low->address.data, l, low->address.length);221222high->addr_type = KRB5_ADDRESS_INET;223if(krb5_data_alloc(&high->address, 4) != 0) {224krb5_free_address(context, low);225return -1;226}227_krb5_put_int(high->address.data, h, high->address.length);228229return 0;230}231232233/*234* AF_INET6 - aka IPv6 implementation235*/236237#ifdef HAVE_IPV6238239static krb5_error_code240ipv6_sockaddr2addr (const struct sockaddr *sa, krb5_address *a)241{242const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;243244if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {245unsigned char buf[4];246247a->addr_type = KRB5_ADDRESS_INET;248#ifndef IN6_ADDR_V6_TO_V4249#ifdef IN6_EXTRACT_V4ADDR250#define IN6_ADDR_V6_TO_V4(x) (&IN6_EXTRACT_V4ADDR(x))251#else252#define IN6_ADDR_V6_TO_V4(x) ((const struct in_addr *)&(x)->s6_addr[12])253#endif254#endif255memcpy (buf, IN6_ADDR_V6_TO_V4(&sin6->sin6_addr), 4);256return krb5_data_copy(&a->address, buf, 4);257} else {258a->addr_type = KRB5_ADDRESS_INET6;259return krb5_data_copy(&a->address,260&sin6->sin6_addr,261sizeof(sin6->sin6_addr));262}263}264265static krb5_error_code266ipv6_sockaddr2port (const struct sockaddr *sa, int16_t *port)267{268const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;269270*port = sin6->sin6_port;271return 0;272}273274static void275ipv6_addr2sockaddr (const krb5_address *a,276struct sockaddr *sa,277krb5_socklen_t *sa_size,278int port)279{280struct sockaddr_in6 tmp;281282memset (&tmp, 0, sizeof(tmp));283tmp.sin6_family = AF_INET6;284memcpy (&tmp.sin6_addr, a->address.data, sizeof(tmp.sin6_addr));285tmp.sin6_port = port;286memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));287*sa_size = sizeof(tmp);288}289290static void291ipv6_h_addr2sockaddr(const char *addr,292struct sockaddr *sa,293krb5_socklen_t *sa_size,294int port)295{296struct sockaddr_in6 tmp;297298memset (&tmp, 0, sizeof(tmp));299tmp.sin6_family = AF_INET6;300tmp.sin6_port = port;301tmp.sin6_addr = *((const struct in6_addr *)addr);302memcpy(sa, &tmp, min(sizeof(tmp), *sa_size));303*sa_size = sizeof(tmp);304}305306static krb5_error_code307ipv6_h_addr2addr (const char *addr,308krb5_address *a)309{310a->addr_type = KRB5_ADDRESS_INET6;311return krb5_data_copy(&a->address, addr, sizeof(struct in6_addr));312}313314/*315*316*/317318static krb5_boolean319ipv6_uninteresting (const struct sockaddr *sa)320{321const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;322const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr;323324return IN6_IS_ADDR_LINKLOCAL(in6)325|| IN6_IS_ADDR_V4COMPAT(in6);326}327328static krb5_boolean329ipv6_is_loopback (const struct sockaddr *sa)330{331const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;332const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr;333334return (IN6_IS_ADDR_LOOPBACK(in6));335}336337static void338ipv6_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port)339{340struct sockaddr_in6 tmp;341342memset (&tmp, 0, sizeof(tmp));343tmp.sin6_family = AF_INET6;344tmp.sin6_port = port;345tmp.sin6_addr = in6addr_any;346*sa_size = sizeof(tmp);347}348349static int350ipv6_print_addr (const krb5_address *addr, char *str, size_t len)351{352char buf[128], buf2[3];353if(inet_ntop(AF_INET6, addr->address.data, buf, sizeof(buf)) == NULL)354{355/* XXX this is pretty ugly, but better than abort() */356size_t i;357unsigned char *p = addr->address.data;358buf[0] = '\0';359for(i = 0; i < addr->address.length; i++) {360snprintf(buf2, sizeof(buf2), "%02x", p[i]);361if(i > 0 && (i & 1) == 0)362strlcat(buf, ":", sizeof(buf));363strlcat(buf, buf2, sizeof(buf));364}365}366return snprintf(str, len, "IPv6:%s", buf);367}368369static int370ipv6_parse_addr (krb5_context context, const char *address, krb5_address *addr)371{372int ret;373struct in6_addr in6;374const char *p;375376p = strchr(address, ':');377if(p) {378p++;379if(strncasecmp(address, "ip6:", p - address) == 0 ||380strncasecmp(address, "ipv6:", p - address) == 0 ||381strncasecmp(address, "inet6:", p - address) == 0)382address = p;383}384385ret = inet_pton(AF_INET6, address, &in6.s6_addr);386if(ret == 1) {387addr->addr_type = KRB5_ADDRESS_INET6;388ret = krb5_data_alloc(&addr->address, sizeof(in6.s6_addr));389if (ret)390return -1;391memcpy(addr->address.data, in6.s6_addr, sizeof(in6.s6_addr));392return 0;393}394return -1;395}396397static int398ipv6_mask_boundary(krb5_context context, const krb5_address *inaddr,399unsigned long len, krb5_address *low, krb5_address *high)400{401struct in6_addr addr, laddr, haddr;402uint32_t m;403int i, sub_len;404405if (len > 128) {406krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,407N_("IPv6 prefix too large (%ld)", "length"), len);408return KRB5_PROG_ATYPE_NOSUPP;409}410411if (inaddr->address.length != sizeof(addr)) {412krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,413N_("IPv6 addr bad length", ""));414return KRB5_PROG_ATYPE_NOSUPP;415}416417memcpy(&addr, inaddr->address.data, inaddr->address.length);418419for (i = 0; i < 16; i++) {420sub_len = min(8, len);421422m = 0xff << (8 - sub_len);423424laddr.s6_addr[i] = addr.s6_addr[i] & m;425haddr.s6_addr[i] = (addr.s6_addr[i] & m) | ~m;426427if (len > 8)428len -= 8;429else430len = 0;431}432433low->addr_type = KRB5_ADDRESS_INET6;434if (krb5_data_alloc(&low->address, sizeof(laddr.s6_addr)) != 0)435return -1;436memcpy(low->address.data, laddr.s6_addr, sizeof(laddr.s6_addr));437438high->addr_type = KRB5_ADDRESS_INET6;439if (krb5_data_alloc(&high->address, sizeof(haddr.s6_addr)) != 0) {440krb5_free_address(context, low);441return -1;442}443memcpy(high->address.data, haddr.s6_addr, sizeof(haddr.s6_addr));444445return 0;446}447448#endif /* IPv6 */449450#ifndef HEIMDAL_SMALLER451452/*453* table454*/455456#define KRB5_ADDRESS_ARANGE (-100)457458struct arange {459krb5_address low;460krb5_address high;461};462463static int464arange_parse_addr (krb5_context context,465const char *address, krb5_address *addr)466{467char buf[1024], *p;468krb5_address low0, high0;469struct arange *a;470krb5_error_code ret;471472if(strncasecmp(address, "RANGE:", 6) != 0)473return -1;474475address += 6;476477p = strrchr(address, '/');478if (p) {479krb5_addresses addrmask;480char *q;481long num;482483if (strlcpy(buf, address, sizeof(buf)) > sizeof(buf))484return -1;485buf[p - address] = '\0';486ret = krb5_parse_address(context, buf, &addrmask);487if (ret)488return ret;489if(addrmask.len != 1) {490krb5_free_addresses(context, &addrmask);491return -1;492}493494address += p - address + 1;495496num = strtol(address, &q, 10);497if (q == address || *q != '\0' || num < 0) {498krb5_free_addresses(context, &addrmask);499return -1;500}501502ret = krb5_address_prefixlen_boundary(context, &addrmask.val[0], num,503&low0, &high0);504krb5_free_addresses(context, &addrmask);505if (ret)506return ret;507508} else {509krb5_addresses low, high;510511strsep_copy(&address, "-", buf, sizeof(buf));512ret = krb5_parse_address(context, buf, &low);513if(ret)514return ret;515if(low.len != 1) {516krb5_free_addresses(context, &low);517return -1;518}519520strsep_copy(&address, "-", buf, sizeof(buf));521ret = krb5_parse_address(context, buf, &high);522if(ret) {523krb5_free_addresses(context, &low);524return ret;525}526527if(high.len != 1 || high.val[0].addr_type != low.val[0].addr_type) {528krb5_free_addresses(context, &low);529krb5_free_addresses(context, &high);530return -1;531}532533ret = krb5_copy_address(context, &high.val[0], &high0);534if (ret == 0) {535ret = krb5_copy_address(context, &low.val[0], &low0);536if (ret)537krb5_free_address(context, &high0);538}539krb5_free_addresses(context, &low);540krb5_free_addresses(context, &high);541if (ret)542return ret;543}544545krb5_data_alloc(&addr->address, sizeof(*a));546addr->addr_type = KRB5_ADDRESS_ARANGE;547a = addr->address.data;548549if(krb5_address_order(context, &low0, &high0) < 0) {550a->low = low0;551a->high = high0;552} else {553a->low = high0;554a->high = low0;555}556return 0;557}558559static int560arange_free (krb5_context context, krb5_address *addr)561{562struct arange *a;563a = addr->address.data;564krb5_free_address(context, &a->low);565krb5_free_address(context, &a->high);566krb5_data_free(&addr->address);567return 0;568}569570571static int572arange_copy (krb5_context context, const krb5_address *inaddr,573krb5_address *outaddr)574{575krb5_error_code ret;576struct arange *i, *o;577578outaddr->addr_type = KRB5_ADDRESS_ARANGE;579ret = krb5_data_alloc(&outaddr->address, sizeof(*o));580if(ret)581return ret;582i = inaddr->address.data;583o = outaddr->address.data;584ret = krb5_copy_address(context, &i->low, &o->low);585if(ret) {586krb5_data_free(&outaddr->address);587return ret;588}589ret = krb5_copy_address(context, &i->high, &o->high);590if(ret) {591krb5_free_address(context, &o->low);592krb5_data_free(&outaddr->address);593return ret;594}595return 0;596}597598static int599arange_print_addr (const krb5_address *addr, char *str, size_t len)600{601struct arange *a;602krb5_error_code ret;603size_t l, size, ret_len;604605a = addr->address.data;606607l = strlcpy(str, "RANGE:", len);608ret_len = l;609if (l > len)610l = len;611size = l;612613ret = krb5_print_address (&a->low, str + size, len - size, &l);614if (ret)615return ret;616ret_len += l;617if (len - size > l)618size += l;619else620size = len;621622l = strlcat(str + size, "-", len - size);623ret_len += l;624if (len - size > l)625size += l;626else627size = len;628629ret = krb5_print_address (&a->high, str + size, len - size, &l);630if (ret)631return ret;632ret_len += l;633634return ret_len;635}636637static int638arange_order_addr(krb5_context context,639const krb5_address *addr1,640const krb5_address *addr2)641{642int tmp1, tmp2, sign;643struct arange *a;644const krb5_address *a2;645646if(addr1->addr_type == KRB5_ADDRESS_ARANGE) {647a = addr1->address.data;648a2 = addr2;649sign = 1;650} else if(addr2->addr_type == KRB5_ADDRESS_ARANGE) {651a = addr2->address.data;652a2 = addr1;653sign = -1;654} else {655abort();656UNREACHABLE(return 0);657}658659if(a2->addr_type == KRB5_ADDRESS_ARANGE) {660struct arange *b = a2->address.data;661tmp1 = krb5_address_order(context, &a->low, &b->low);662if(tmp1 != 0)663return sign * tmp1;664return sign * krb5_address_order(context, &a->high, &b->high);665} else if(a2->addr_type == a->low.addr_type) {666tmp1 = krb5_address_order(context, &a->low, a2);667if(tmp1 > 0)668return sign;669tmp2 = krb5_address_order(context, &a->high, a2);670if(tmp2 < 0)671return -sign;672return 0;673} else {674return sign * (addr1->addr_type - addr2->addr_type);675}676}677678#endif /* HEIMDAL_SMALLER */679680static int681addrport_print_addr (const krb5_address *addr, char *str, size_t len)682{683krb5_error_code ret;684krb5_address addr1, addr2;685uint16_t port = 0;686size_t ret_len = 0, l, size = 0;687krb5_storage *sp;688689sp = krb5_storage_from_data((krb5_data*)rk_UNCONST(&addr->address));690if (sp == NULL)691return ENOMEM;692693/* for totally obscure reasons, these are not in network byteorder */694krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);695696krb5_storage_seek(sp, 2, SEEK_CUR); /* skip first two bytes */697krb5_ret_address(sp, &addr1);698699krb5_storage_seek(sp, 2, SEEK_CUR); /* skip two bytes */700krb5_ret_address(sp, &addr2);701krb5_storage_free(sp);702if(addr2.addr_type == KRB5_ADDRESS_IPPORT && addr2.address.length == 2) {703unsigned long value;704_krb5_get_int(addr2.address.data, &value, 2);705port = value;706}707l = strlcpy(str, "ADDRPORT:", len);708ret_len += l;709if (len > l)710size += l;711else712size = len;713714ret = krb5_print_address(&addr1, str + size, len - size, &l);715if (ret)716return ret;717ret_len += l;718if (len - size > l)719size += l;720else721size = len;722723ret = snprintf(str + size, len - size, ",PORT=%u", port);724if (ret < 0)725return EINVAL;726ret_len += ret;727return ret_len;728}729730static struct addr_operations at[] = {731{732AF_INET, KRB5_ADDRESS_INET, sizeof(struct sockaddr_in),733ipv4_sockaddr2addr,734ipv4_sockaddr2port,735ipv4_addr2sockaddr,736ipv4_h_addr2sockaddr,737ipv4_h_addr2addr,738ipv4_uninteresting,739ipv4_is_loopback,740ipv4_anyaddr,741ipv4_print_addr,742ipv4_parse_addr,743NULL,744NULL,745NULL,746ipv4_mask_boundary747},748#ifdef HAVE_IPV6749{750AF_INET6, KRB5_ADDRESS_INET6, sizeof(struct sockaddr_in6),751ipv6_sockaddr2addr,752ipv6_sockaddr2port,753ipv6_addr2sockaddr,754ipv6_h_addr2sockaddr,755ipv6_h_addr2addr,756ipv6_uninteresting,757ipv6_is_loopback,758ipv6_anyaddr,759ipv6_print_addr,760ipv6_parse_addr,761NULL,762NULL,763NULL,764ipv6_mask_boundary765} ,766#endif767#ifndef HEIMDAL_SMALLER768/* fake address type */769{770KRB5_ADDRESS_ARANGE, KRB5_ADDRESS_ARANGE, sizeof(struct arange),771NULL,772NULL,773NULL,774NULL,775NULL,776NULL,777NULL,778NULL,779arange_print_addr,780arange_parse_addr,781arange_order_addr,782arange_free,783arange_copy,784NULL785},786#endif787{788KRB5_ADDRESS_ADDRPORT, KRB5_ADDRESS_ADDRPORT, 0,789NULL,790NULL,791NULL,792NULL,793NULL,794NULL,795NULL,796NULL,797addrport_print_addr,798NULL,799NULL,800NULL,801NULL802}803};804805static int num_addrs = sizeof(at) / sizeof(at[0]);806807static size_t max_sockaddr_size = 0;808809/*810* generic functions811*/812813static struct addr_operations *814find_af(int af)815{816struct addr_operations *a;817818for (a = at; a < at + num_addrs; ++a)819if (af == a->af)820return a;821return NULL;822}823824static struct addr_operations *825find_atype(krb5_address_type atype)826{827struct addr_operations *a;828829for (a = at; a < at + num_addrs; ++a)830if (atype == a->atype)831return a;832return NULL;833}834835/**836* krb5_sockaddr2address stores a address a "struct sockaddr" sa in837* the krb5_address addr.838*839* @param context a Keberos context840* @param sa a struct sockaddr to extract the address from841* @param addr an Kerberos 5 address to store the address in.842*843* @return Return an error code or 0.844*845* @ingroup krb5_address846*/847848KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL849krb5_sockaddr2address (krb5_context context,850const struct sockaddr *sa, krb5_address *addr)851{852struct addr_operations *a = find_af(sa->sa_family);853if (a == NULL) {854krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,855N_("Address family %d not supported", ""),856sa->sa_family);857return KRB5_PROG_ATYPE_NOSUPP;858}859return (*a->sockaddr2addr)(sa, addr);860}861862/**863* krb5_sockaddr2port extracts a port (if possible) from a "struct864* sockaddr.865*866* @param context a Keberos context867* @param sa a struct sockaddr to extract the port from868* @param port a pointer to an int16_t store the port in.869*870* @return Return an error code or 0. Will return871* KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.872*873* @ingroup krb5_address874*/875876KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL877krb5_sockaddr2port (krb5_context context,878const struct sockaddr *sa, int16_t *port)879{880struct addr_operations *a = find_af(sa->sa_family);881if (a == NULL) {882krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,883N_("Address family %d not supported", ""),884sa->sa_family);885return KRB5_PROG_ATYPE_NOSUPP;886}887return (*a->sockaddr2port)(sa, port);888}889890/**891* krb5_addr2sockaddr sets the "struct sockaddr sockaddr" from addr892* and port. The argument sa_size should initially contain the size of893* the sa and after the call, it will contain the actual length of the894* address. In case of the sa is too small to fit the whole address,895* the up to *sa_size will be stored, and then *sa_size will be set to896* the required length.897*898* @param context a Keberos context899* @param addr the address to copy the from900* @param sa the struct sockaddr that will be filled in901* @param sa_size pointer to length of sa, and after the call, it will902* contain the actual length of the address.903* @param port set port in sa.904*905* @return Return an error code or 0. Will return906* KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.907*908* @ingroup krb5_address909*/910911KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL912krb5_addr2sockaddr (krb5_context context,913const krb5_address *addr,914struct sockaddr *sa,915krb5_socklen_t *sa_size,916int port)917{918struct addr_operations *a = find_atype(addr->addr_type);919920if (a == NULL) {921krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,922N_("Address type %d not supported",923"krb5_address type"),924addr->addr_type);925return KRB5_PROG_ATYPE_NOSUPP;926}927if (a->addr2sockaddr == NULL) {928krb5_set_error_message (context,929KRB5_PROG_ATYPE_NOSUPP,930N_("Can't convert address type %d to sockaddr", ""),931addr->addr_type);932return KRB5_PROG_ATYPE_NOSUPP;933}934(*a->addr2sockaddr)(addr, sa, sa_size, port);935return 0;936}937938/**939* krb5_max_sockaddr_size returns the max size of the .Li struct940* sockaddr that the Kerberos library will return.941*942* @return Return an size_t of the maximum struct sockaddr.943*944* @ingroup krb5_address945*/946947KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL948krb5_max_sockaddr_size (void)949{950if (max_sockaddr_size == 0) {951struct addr_operations *a;952953for(a = at; a < at + num_addrs; ++a)954max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size);955}956return max_sockaddr_size;957}958959/**960* krb5_sockaddr_uninteresting returns TRUE for all .Fa sa that the961* kerberos library thinks are uninteresting. One example are link962* local addresses.963*964* @param sa pointer to struct sockaddr that might be interesting.965*966* @return Return a non zero for uninteresting addresses.967*968* @ingroup krb5_address969*/970971KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL972krb5_sockaddr_uninteresting(const struct sockaddr *sa)973{974struct addr_operations *a = find_af(sa->sa_family);975if (a == NULL || a->uninteresting == NULL)976return TRUE;977return (*a->uninteresting)(sa);978}979980KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL981krb5_sockaddr_is_loopback(const struct sockaddr *sa)982{983struct addr_operations *a = find_af(sa->sa_family);984if (a == NULL || a->is_loopback == NULL)985return TRUE;986return (*a->is_loopback)(sa);987}988989/**990* krb5_h_addr2sockaddr initializes a "struct sockaddr sa" from af and991* the "struct hostent" (see gethostbyname(3) ) h_addr_list992* component. The argument sa_size should initially contain the size993* of the sa, and after the call, it will contain the actual length of994* the address.995*996* @param context a Keberos context997* @param af addresses998* @param addr address999* @param sa returned struct sockaddr1000* @param sa_size size of sa1001* @param port port to set in sa.1002*1003* @return Return an error code or 0.1004*1005* @ingroup krb5_address1006*/10071008KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1009krb5_h_addr2sockaddr (krb5_context context,1010int af,1011const char *addr, struct sockaddr *sa,1012krb5_socklen_t *sa_size,1013int port)1014{1015struct addr_operations *a = find_af(af);1016if (a == NULL) {1017krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,1018"Address family %d not supported", af);1019return KRB5_PROG_ATYPE_NOSUPP;1020}1021(*a->h_addr2sockaddr)(addr, sa, sa_size, port);1022return 0;1023}10241025/**1026* krb5_h_addr2addr works like krb5_h_addr2sockaddr with the exception1027* that it operates on a krb5_address instead of a struct sockaddr.1028*1029* @param context a Keberos context1030* @param af address family1031* @param haddr host address from struct hostent.1032* @param addr returned krb5_address.1033*1034* @return Return an error code or 0.1035*1036* @ingroup krb5_address1037*/10381039KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1040krb5_h_addr2addr (krb5_context context,1041int af,1042const char *haddr, krb5_address *addr)1043{1044struct addr_operations *a = find_af(af);1045if (a == NULL) {1046krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,1047N_("Address family %d not supported", ""), af);1048return KRB5_PROG_ATYPE_NOSUPP;1049}1050return (*a->h_addr2addr)(haddr, addr);1051}10521053/**1054* krb5_anyaddr fills in a "struct sockaddr sa" that can be used to1055* bind(2) to. The argument sa_size should initially contain the size1056* of the sa, and after the call, it will contain the actual length1057* of the address.1058*1059* @param context a Keberos context1060* @param af address family1061* @param sa sockaddr1062* @param sa_size lenght of sa.1063* @param port for to fill into sa.1064*1065* @return Return an error code or 0.1066*1067* @ingroup krb5_address1068*/10691070KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1071krb5_anyaddr (krb5_context context,1072int af,1073struct sockaddr *sa,1074krb5_socklen_t *sa_size,1075int port)1076{1077struct addr_operations *a = find_af (af);10781079if (a == NULL) {1080krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,1081N_("Address family %d not supported", ""), af);1082return KRB5_PROG_ATYPE_NOSUPP;1083}10841085(*a->anyaddr)(sa, sa_size, port);1086return 0;1087}10881089/**1090* krb5_print_address prints the address in addr to the string string1091* that have the length len. If ret_len is not NULL, it will be filled1092* with the length of the string if size were unlimited (not including1093* the final NUL) .1094*1095* @param addr address to be printed1096* @param str pointer string to print the address into1097* @param len length that will fit into area pointed to by "str".1098* @param ret_len return length the str.1099*1100* @return Return an error code or 0.1101*1102* @ingroup krb5_address1103*/11041105KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1106krb5_print_address (const krb5_address *addr,1107char *str, size_t len, size_t *ret_len)1108{1109struct addr_operations *a = find_atype(addr->addr_type);1110int ret;11111112if (a == NULL || a->print_addr == NULL) {1113char *s;1114int l;1115size_t i;11161117s = str;1118l = snprintf(s, len, "TYPE_%d:", addr->addr_type);1119if (l < 0 || (size_t)l >= len)1120return EINVAL;1121s += l;1122len -= l;1123for(i = 0; i < addr->address.length; i++) {1124l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]);1125if (l < 0 || (size_t)l >= len)1126return EINVAL;1127len -= l;1128s += l;1129}1130if(ret_len != NULL)1131*ret_len = s - str;1132return 0;1133}1134ret = (*a->print_addr)(addr, str, len);1135if (ret < 0)1136return EINVAL;1137if(ret_len != NULL)1138*ret_len = ret;1139return 0;1140}11411142/**1143* krb5_parse_address returns the resolved hostname in string to the1144* krb5_addresses addresses .1145*1146* @param context a Keberos context1147* @param string1148* @param addresses1149*1150* @return Return an error code or 0.1151*1152* @ingroup krb5_address1153*/11541155KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1156krb5_parse_address(krb5_context context,1157const char *string,1158krb5_addresses *addresses)1159{1160int i, n;1161struct addrinfo *ai, *a;1162int error;1163int save_errno;11641165addresses->len = 0;1166addresses->val = NULL;11671168for(i = 0; i < num_addrs; i++) {1169if(at[i].parse_addr) {1170krb5_address addr;1171if((*at[i].parse_addr)(context, string, &addr) == 0) {1172ALLOC_SEQ(addresses, 1);1173if (addresses->val == NULL) {1174krb5_set_error_message(context, ENOMEM,1175N_("malloc: out of memory", ""));1176return ENOMEM;1177}1178addresses->val[0] = addr;1179return 0;1180}1181}1182}11831184error = getaddrinfo (string, NULL, NULL, &ai);1185if (error) {1186krb5_error_code ret2;1187save_errno = errno;1188ret2 = krb5_eai_to_heim_errno(error, save_errno);1189krb5_set_error_message (context, ret2, "%s: %s",1190string, gai_strerror(error));1191return ret2;1192}11931194n = 0;1195for (a = ai; a != NULL; a = a->ai_next)1196++n;11971198ALLOC_SEQ(addresses, n);1199if (addresses->val == NULL) {1200krb5_set_error_message(context, ENOMEM,1201N_("malloc: out of memory", ""));1202freeaddrinfo(ai);1203return ENOMEM;1204}12051206addresses->len = 0;1207for (a = ai, i = 0; a != NULL; a = a->ai_next) {1208if (krb5_sockaddr2address (context, ai->ai_addr, &addresses->val[i]))1209continue;1210if(krb5_address_search(context, &addresses->val[i], addresses)) {1211krb5_free_address(context, &addresses->val[i]);1212continue;1213}1214i++;1215addresses->len = i;1216}1217freeaddrinfo (ai);1218return 0;1219}12201221/**1222* krb5_address_order compares the addresses addr1 and addr2 so that1223* it can be used for sorting addresses. If the addresses are the same1224* address krb5_address_order will return 0. Behavies like memcmp(2).1225*1226* @param context a Keberos context1227* @param addr1 krb5_address to compare1228* @param addr2 krb5_address to compare1229*1230* @return < 0 if address addr1 in "less" then addr2. 0 if addr1 and1231* addr2 is the same address, > 0 if addr2 is "less" then addr1.1232*1233* @ingroup krb5_address1234*/12351236KRB5_LIB_FUNCTION int KRB5_LIB_CALL1237krb5_address_order(krb5_context context,1238const krb5_address *addr1,1239const krb5_address *addr2)1240{1241/* this sucks; what if both addresses have order functions, which1242should we call? this works for now, though */1243struct addr_operations *a;1244a = find_atype(addr1->addr_type);1245if(a == NULL) {1246krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,1247N_("Address family %d not supported", ""),1248addr1->addr_type);1249return KRB5_PROG_ATYPE_NOSUPP;1250}1251if(a->order_addr != NULL)1252return (*a->order_addr)(context, addr1, addr2);1253a = find_atype(addr2->addr_type);1254if(a == NULL) {1255krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP,1256N_("Address family %d not supported", ""),1257addr2->addr_type);1258return KRB5_PROG_ATYPE_NOSUPP;1259}1260if(a->order_addr != NULL)1261return (*a->order_addr)(context, addr1, addr2);12621263if(addr1->addr_type != addr2->addr_type)1264return addr1->addr_type - addr2->addr_type;1265if(addr1->address.length != addr2->address.length)1266return addr1->address.length - addr2->address.length;1267return memcmp (addr1->address.data,1268addr2->address.data,1269addr1->address.length);1270}12711272/**1273* krb5_address_compare compares the addresses addr1 and addr2.1274* Returns TRUE if the two addresses are the same.1275*1276* @param context a Keberos context1277* @param addr1 address to compare1278* @param addr2 address to compare1279*1280* @return Return an TRUE is the address are the same FALSE if not1281*1282* @ingroup krb5_address1283*/12841285KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL1286krb5_address_compare(krb5_context context,1287const krb5_address *addr1,1288const krb5_address *addr2)1289{1290return krb5_address_order (context, addr1, addr2) == 0;1291}12921293/**1294* krb5_address_search checks if the address addr is a member of the1295* address set list addrlist .1296*1297* @param context a Keberos context.1298* @param addr address to search for.1299* @param addrlist list of addresses to look in for addr.1300*1301* @return Return an error code or 0.1302*1303* @ingroup krb5_address1304*/13051306KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL1307krb5_address_search(krb5_context context,1308const krb5_address *addr,1309const krb5_addresses *addrlist)1310{1311size_t i;13121313for (i = 0; i < addrlist->len; ++i)1314if (krb5_address_compare (context, addr, &addrlist->val[i]))1315return TRUE;1316return FALSE;1317}13181319/**1320* krb5_free_address frees the data stored in the address that is1321* alloced with any of the krb5_address functions.1322*1323* @param context a Keberos context1324* @param address addresss to be freed.1325*1326* @return Return an error code or 0.1327*1328* @ingroup krb5_address1329*/13301331KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1332krb5_free_address(krb5_context context,1333krb5_address *address)1334{1335struct addr_operations *a = find_atype (address->addr_type);1336if(a != NULL && a->free_addr != NULL)1337return (*a->free_addr)(context, address);1338krb5_data_free (&address->address);1339memset(address, 0, sizeof(*address));1340return 0;1341}13421343/**1344* krb5_free_addresses frees the data stored in the address that is1345* alloced with any of the krb5_address functions.1346*1347* @param context a Keberos context1348* @param addresses addressses to be freed.1349*1350* @return Return an error code or 0.1351*1352* @ingroup krb5_address1353*/13541355KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1356krb5_free_addresses(krb5_context context,1357krb5_addresses *addresses)1358{1359size_t i;1360for(i = 0; i < addresses->len; i++)1361krb5_free_address(context, &addresses->val[i]);1362free(addresses->val);1363addresses->len = 0;1364addresses->val = NULL;1365return 0;1366}13671368/**1369* krb5_copy_address copies the content of address1370* inaddr to outaddr.1371*1372* @param context a Keberos context1373* @param inaddr pointer to source address1374* @param outaddr pointer to destination address1375*1376* @return Return an error code or 0.1377*1378* @ingroup krb5_address1379*/13801381KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1382krb5_copy_address(krb5_context context,1383const krb5_address *inaddr,1384krb5_address *outaddr)1385{1386struct addr_operations *a = find_af (inaddr->addr_type);1387if(a != NULL && a->copy_addr != NULL)1388return (*a->copy_addr)(context, inaddr, outaddr);1389return copy_HostAddress(inaddr, outaddr);1390}13911392/**1393* krb5_copy_addresses copies the content of addresses1394* inaddr to outaddr.1395*1396* @param context a Keberos context1397* @param inaddr pointer to source addresses1398* @param outaddr pointer to destination addresses1399*1400* @return Return an error code or 0.1401*1402* @ingroup krb5_address1403*/14041405KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1406krb5_copy_addresses(krb5_context context,1407const krb5_addresses *inaddr,1408krb5_addresses *outaddr)1409{1410size_t i;1411ALLOC_SEQ(outaddr, inaddr->len);1412if(inaddr->len > 0 && outaddr->val == NULL)1413return ENOMEM;1414for(i = 0; i < inaddr->len; i++)1415krb5_copy_address(context, &inaddr->val[i], &outaddr->val[i]);1416return 0;1417}14181419/**1420* krb5_append_addresses adds the set of addresses in source to1421* dest. While copying the addresses, duplicates are also sorted out.1422*1423* @param context a Keberos context1424* @param dest destination of copy operation1425* @param source adresses that are going to be added to dest1426*1427* @return Return an error code or 0.1428*1429* @ingroup krb5_address1430*/14311432KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1433krb5_append_addresses(krb5_context context,1434krb5_addresses *dest,1435const krb5_addresses *source)1436{1437krb5_address *tmp;1438krb5_error_code ret;1439size_t i;1440if(source->len > 0) {1441tmp = realloc(dest->val, (dest->len + source->len) * sizeof(*tmp));1442if(tmp == NULL) {1443krb5_set_error_message (context, ENOMEM,1444N_("malloc: out of memory", ""));1445return ENOMEM;1446}1447dest->val = tmp;1448for(i = 0; i < source->len; i++) {1449/* skip duplicates */1450if(krb5_address_search(context, &source->val[i], dest))1451continue;1452ret = krb5_copy_address(context,1453&source->val[i],1454&dest->val[dest->len]);1455if(ret)1456return ret;1457dest->len++;1458}1459}1460return 0;1461}14621463/**1464* Create an address of type KRB5_ADDRESS_ADDRPORT from (addr, port)1465*1466* @param context a Keberos context1467* @param res built address from addr/port1468* @param addr address to use1469* @param port port to use1470*1471* @return Return an error code or 0.1472*1473* @ingroup krb5_address1474*/14751476KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1477krb5_make_addrport (krb5_context context,1478krb5_address **res, const krb5_address *addr, int16_t port)1479{1480krb5_error_code ret;1481size_t len = addr->address.length + 2 + 4 * 4;1482u_char *p;14831484*res = malloc (sizeof(**res));1485if (*res == NULL) {1486krb5_set_error_message (context, ENOMEM,1487N_("malloc: out of memory", ""));1488return ENOMEM;1489}1490(*res)->addr_type = KRB5_ADDRESS_ADDRPORT;1491ret = krb5_data_alloc (&(*res)->address, len);1492if (ret) {1493krb5_set_error_message (context, ret,1494N_("malloc: out of memory", ""));1495free (*res);1496*res = NULL;1497return ret;1498}1499p = (*res)->address.data;1500*p++ = 0;1501*p++ = 0;1502*p++ = (addr->addr_type ) & 0xFF;1503*p++ = (addr->addr_type >> 8) & 0xFF;15041505*p++ = (addr->address.length ) & 0xFF;1506*p++ = (addr->address.length >> 8) & 0xFF;1507*p++ = (addr->address.length >> 16) & 0xFF;1508*p++ = (addr->address.length >> 24) & 0xFF;15091510memcpy (p, addr->address.data, addr->address.length);1511p += addr->address.length;15121513*p++ = 0;1514*p++ = 0;1515*p++ = (KRB5_ADDRESS_IPPORT ) & 0xFF;1516*p++ = (KRB5_ADDRESS_IPPORT >> 8) & 0xFF;15171518*p++ = (2 ) & 0xFF;1519*p++ = (2 >> 8) & 0xFF;1520*p++ = (2 >> 16) & 0xFF;1521*p++ = (2 >> 24) & 0xFF;15221523memcpy (p, &port, 2);15241525return 0;1526}15271528/**1529* Calculate the boundary addresses of `inaddr'/`prefixlen' and store1530* them in `low' and `high'.1531*1532* @param context a Keberos context1533* @param inaddr address in prefixlen that the bondery searched1534* @param prefixlen width of boundery1535* @param low lowest address1536* @param high highest address1537*1538* @return Return an error code or 0.1539*1540* @ingroup krb5_address1541*/15421543KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL1544krb5_address_prefixlen_boundary(krb5_context context,1545const krb5_address *inaddr,1546unsigned long prefixlen,1547krb5_address *low,1548krb5_address *high)1549{1550struct addr_operations *a = find_atype (inaddr->addr_type);1551if(a != NULL && a->mask_boundary != NULL)1552return (*a->mask_boundary)(context, inaddr, prefixlen, low, high);1553krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP,1554N_("Address family %d doesn't support "1555"address mask operation", ""),1556inaddr->addr_type);1557return KRB5_PROG_ATYPE_NOSUPP;1558}155915601561