Path: blob/main/lib/libcasper/services/cap_dns/cap_dns.c
48260 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2012-2013 The FreeBSD Foundation4*5* This software was developed by Pawel Jakub Dawidek under sponsorship from6* the FreeBSD Foundation.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*/2930#include <sys/cdefs.h>31#include <sys/dnv.h>32#include <sys/nv.h>33#include <netinet/in.h>3435#include <assert.h>36#include <errno.h>37#include <netdb.h>38#include <stdlib.h>39#include <string.h>40#include <unistd.h>4142#include <libcasper.h>43#include <libcasper_service.h>4445#include "cap_dns.h"4647static struct hostent hent;4849static void50hostent_free(struct hostent *hp)51{52unsigned int ii;5354free(hp->h_name);55hp->h_name = NULL;56if (hp->h_aliases != NULL) {57for (ii = 0; hp->h_aliases[ii] != NULL; ii++)58free(hp->h_aliases[ii]);59free(hp->h_aliases);60hp->h_aliases = NULL;61}62if (hp->h_addr_list != NULL) {63for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)64free(hp->h_addr_list[ii]);65free(hp->h_addr_list);66hp->h_addr_list = NULL;67}68}6970static struct hostent *71hostent_unpack(const nvlist_t *nvl, struct hostent *hp)72{73unsigned int ii, nitems;74char nvlname[64];75int n;7677hostent_free(hp);7879hp->h_name = strdup(nvlist_get_string(nvl, "name"));80if (hp->h_name == NULL)81goto fail;82hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");83hp->h_length = (int)nvlist_get_number(nvl, "length");8485nitems = (unsigned int)nvlist_get_number(nvl, "naliases");86hp->h_aliases = calloc(nitems + 1, sizeof(hp->h_aliases[0]));87if (hp->h_aliases == NULL)88goto fail;89for (ii = 0; ii < nitems; ii++) {90n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);91assert(n > 0 && n < (int)sizeof(nvlname));92hp->h_aliases[ii] =93strdup(nvlist_get_string(nvl, nvlname));94if (hp->h_aliases[ii] == NULL)95goto fail;96}97hp->h_aliases[ii] = NULL;9899nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");100hp->h_addr_list = calloc(nitems + 1, sizeof(hp->h_addr_list[0]));101if (hp->h_addr_list == NULL)102goto fail;103for (ii = 0; ii < nitems; ii++) {104hp->h_addr_list[ii] = malloc(hp->h_length);105if (hp->h_addr_list[ii] == NULL)106goto fail;107n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);108assert(n > 0 && n < (int)sizeof(nvlname));109bcopy(nvlist_get_binary(nvl, nvlname, NULL),110hp->h_addr_list[ii], hp->h_length);111}112hp->h_addr_list[ii] = NULL;113114return (hp);115fail:116hostent_free(hp);117h_errno = NO_RECOVERY;118return (NULL);119}120121struct hostent *122cap_gethostbyname(cap_channel_t *chan, const char *name)123{124125return (cap_gethostbyname2(chan, name, AF_INET));126}127128struct hostent *129cap_gethostbyname2(cap_channel_t *chan, const char *name, int type)130{131struct hostent *hp;132nvlist_t *nvl;133134nvl = nvlist_create(0);135nvlist_add_string(nvl, "cmd", "gethostbyname");136nvlist_add_number(nvl, "family", (uint64_t)type);137nvlist_add_string(nvl, "name", name);138nvl = cap_xfer_nvlist(chan, nvl);139if (nvl == NULL) {140h_errno = NO_RECOVERY;141return (NULL);142}143if (nvlist_get_number(nvl, "error") != 0) {144h_errno = (int)nvlist_get_number(nvl, "error");145nvlist_destroy(nvl);146return (NULL);147}148149hp = hostent_unpack(nvl, &hent);150nvlist_destroy(nvl);151return (hp);152}153154struct hostent *155cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,156int type)157{158struct hostent *hp;159nvlist_t *nvl;160161nvl = nvlist_create(0);162nvlist_add_string(nvl, "cmd", "gethostbyaddr");163nvlist_add_binary(nvl, "addr", addr, (size_t)len);164nvlist_add_number(nvl, "family", (uint64_t)type);165nvl = cap_xfer_nvlist(chan, nvl);166if (nvl == NULL) {167h_errno = NO_RECOVERY;168return (NULL);169}170if (nvlist_get_number(nvl, "error") != 0) {171h_errno = (int)nvlist_get_number(nvl, "error");172nvlist_destroy(nvl);173return (NULL);174}175hp = hostent_unpack(nvl, &hent);176nvlist_destroy(nvl);177return (hp);178}179180static struct addrinfo *181addrinfo_unpack(const nvlist_t *nvl)182{183struct addrinfo *ai;184const void *addr;185size_t addrlen;186const char *canonname;187188addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);189ai = malloc(sizeof(*ai) + addrlen);190if (ai == NULL)191return (NULL);192ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");193ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");194ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");195ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");196ai->ai_addrlen = (socklen_t)addrlen;197canonname = dnvlist_get_string(nvl, "ai_canonname", NULL);198if (canonname != NULL) {199ai->ai_canonname = strdup(canonname);200if (ai->ai_canonname == NULL) {201free(ai);202return (NULL);203}204} else {205ai->ai_canonname = NULL;206}207ai->ai_addr = (void *)(ai + 1);208bcopy(addr, ai->ai_addr, addrlen);209ai->ai_next = NULL;210211return (ai);212}213214int215cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,216const struct addrinfo *hints, struct addrinfo **res)217{218struct addrinfo *firstai, *prevai, *curai;219unsigned int ii;220const nvlist_t *nvlai;221char nvlname[64];222nvlist_t *nvl;223int error, n;224225nvl = nvlist_create(0);226nvlist_add_string(nvl, "cmd", "getaddrinfo");227if (hostname != NULL)228nvlist_add_string(nvl, "hostname", hostname);229if (servname != NULL)230nvlist_add_string(nvl, "servname", servname);231if (hints != NULL) {232nvlist_add_number(nvl, "hints.ai_flags",233(uint64_t)hints->ai_flags);234nvlist_add_number(nvl, "hints.ai_family",235(uint64_t)hints->ai_family);236nvlist_add_number(nvl, "hints.ai_socktype",237(uint64_t)hints->ai_socktype);238nvlist_add_number(nvl, "hints.ai_protocol",239(uint64_t)hints->ai_protocol);240}241nvl = cap_xfer_nvlist(chan, nvl);242if (nvl == NULL)243return (EAI_MEMORY);244if (nvlist_get_number(nvl, "error") != 0) {245error = (int)nvlist_get_number(nvl, "error");246nvlist_destroy(nvl);247return (error);248}249250nvlai = NULL;251firstai = prevai = curai = NULL;252for (ii = 0; ; ii++) {253n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);254assert(n > 0 && n < (int)sizeof(nvlname));255if (!nvlist_exists_nvlist(nvl, nvlname))256break;257nvlai = nvlist_get_nvlist(nvl, nvlname);258curai = addrinfo_unpack(nvlai);259if (curai == NULL)260break;261if (prevai != NULL)262prevai->ai_next = curai;263else if (firstai == NULL)264firstai = curai;265prevai = curai;266}267nvlist_destroy(nvl);268if (curai == NULL && nvlai != NULL) {269if (firstai == NULL)270freeaddrinfo(firstai);271return (EAI_MEMORY);272}273274*res = firstai;275return (0);276}277278int279cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen,280char *host, size_t hostlen, char *serv, size_t servlen, int flags)281{282nvlist_t *nvl;283int error;284285nvl = nvlist_create(0);286nvlist_add_string(nvl, "cmd", "getnameinfo");287nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen);288nvlist_add_number(nvl, "servlen", (uint64_t)servlen);289nvlist_add_binary(nvl, "sa", sa, (size_t)salen);290nvlist_add_number(nvl, "flags", (uint64_t)flags);291nvl = cap_xfer_nvlist(chan, nvl);292if (nvl == NULL)293return (EAI_MEMORY);294if (nvlist_get_number(nvl, "error") != 0) {295error = (int)nvlist_get_number(nvl, "error");296nvlist_destroy(nvl);297return (error);298}299300if (host != NULL && nvlist_exists_string(nvl, "host"))301strlcpy(host, nvlist_get_string(nvl, "host"), hostlen);302if (serv != NULL && nvlist_exists_string(nvl, "serv"))303strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen);304nvlist_destroy(nvl);305return (0);306}307308static void309limit_remove(nvlist_t *limits, const char *prefix)310{311const char *name;312size_t prefixlen;313void *cookie;314315prefixlen = strlen(prefix);316again:317cookie = NULL;318while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {319if (strncmp(name, prefix, prefixlen) == 0) {320nvlist_free(limits, name);321goto again;322}323}324}325326int327cap_dns_type_limit(cap_channel_t *chan, const char * const *types,328size_t ntypes)329{330nvlist_t *limits;331unsigned int i;332char nvlname[64];333int n;334335if (cap_limit_get(chan, &limits) < 0)336return (-1);337if (limits == NULL)338limits = nvlist_create(0);339else340limit_remove(limits, "type");341for (i = 0; i < ntypes; i++) {342n = snprintf(nvlname, sizeof(nvlname), "type%u", i);343assert(n > 0 && n < (int)sizeof(nvlname));344nvlist_add_string(limits, nvlname, types[i]);345}346return (cap_limit_set(chan, limits));347}348349int350cap_dns_family_limit(cap_channel_t *chan, const int *families,351size_t nfamilies)352{353nvlist_t *limits;354unsigned int i;355char nvlname[64];356int n;357358if (cap_limit_get(chan, &limits) < 0)359return (-1);360if (limits == NULL)361limits = nvlist_create(0);362else363limit_remove(limits, "family");364for (i = 0; i < nfamilies; i++) {365n = snprintf(nvlname, sizeof(nvlname), "family%u", i);366assert(n > 0 && n < (int)sizeof(nvlname));367nvlist_add_number(limits, nvlname, (uint64_t)families[i]);368}369return (cap_limit_set(chan, limits));370}371372/*373* Service functions.374*/375static bool376dns_allowed_type(const nvlist_t *limits, const char *type)377{378const char *name;379bool notypes;380void *cookie;381382if (limits == NULL)383return (true);384385notypes = true;386cookie = NULL;387while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {388if (strncmp(name, "type", sizeof("type") - 1) != 0)389continue;390notypes = false;391if (strcmp(nvlist_get_string(limits, name), type) == 0)392return (true);393}394395/* If there are no types at all, allow any type. */396if (notypes)397return (true);398399return (false);400}401402static bool403dns_allowed_family(const nvlist_t *limits, int family)404{405const char *name;406bool nofamilies;407void *cookie;408409if (limits == NULL)410return (true);411412nofamilies = true;413cookie = NULL;414while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {415if (strncmp(name, "family", sizeof("family") - 1) != 0)416continue;417nofamilies = false;418if (family == AF_UNSPEC)419continue;420if (nvlist_get_number(limits, name) == (uint64_t)family)421return (true);422}423424/* If there are no families at all, allow any family. */425if (nofamilies)426return (true);427428return (false);429}430431static void432hostent_pack(const struct hostent *hp, nvlist_t *nvl)433{434unsigned int ii;435char nvlname[64];436int n;437438nvlist_add_string(nvl, "name", hp->h_name);439nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);440nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);441442if (hp->h_aliases == NULL) {443nvlist_add_number(nvl, "naliases", 0);444} else {445for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {446n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);447assert(n > 0 && n < (int)sizeof(nvlname));448nvlist_add_string(nvl, nvlname, hp->h_aliases[ii]);449}450nvlist_add_number(nvl, "naliases", (uint64_t)ii);451}452453if (hp->h_addr_list == NULL) {454nvlist_add_number(nvl, "naddrs", 0);455} else {456for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {457n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);458assert(n > 0 && n < (int)sizeof(nvlname));459nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii],460(size_t)hp->h_length);461}462nvlist_add_number(nvl, "naddrs", (uint64_t)ii);463}464}465466static int467dns_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,468nvlist_t *nvlout)469{470struct hostent *hp;471int family;472473if (!dns_allowed_type(limits, "NAME2ADDR") &&474!dns_allowed_type(limits, "NAME"))475return (NO_RECOVERY);476477family = (int)nvlist_get_number(nvlin, "family");478479if (!dns_allowed_family(limits, family))480return (NO_RECOVERY);481482hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);483if (hp == NULL)484return (h_errno);485hostent_pack(hp, nvlout);486return (0);487}488489static int490dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,491nvlist_t *nvlout)492{493struct hostent *hp;494const void *addr;495size_t addrsize;496int family;497498if (!dns_allowed_type(limits, "ADDR2NAME") &&499!dns_allowed_type(limits, "ADDR"))500return (NO_RECOVERY);501502family = (int)nvlist_get_number(nvlin, "family");503504if (!dns_allowed_family(limits, family))505return (NO_RECOVERY);506507addr = nvlist_get_binary(nvlin, "addr", &addrsize);508hp = gethostbyaddr(addr, (socklen_t)addrsize, family);509if (hp == NULL)510return (h_errno);511hostent_pack(hp, nvlout);512return (0);513}514515static int516dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)517{518struct sockaddr_storage sast;519const void *sabin;520char *host, *serv;521size_t sabinsize, hostlen, servlen;522socklen_t salen;523int error, flags;524525if (!dns_allowed_type(limits, "ADDR2NAME") &&526!dns_allowed_type(limits, "ADDR"))527return (NO_RECOVERY);528529error = 0;530host = serv = NULL;531memset(&sast, 0, sizeof(sast));532533hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");534servlen = (size_t)nvlist_get_number(nvlin, "servlen");535536if (hostlen > 0) {537host = calloc(1, hostlen);538if (host == NULL) {539error = EAI_MEMORY;540goto out;541}542}543if (servlen > 0) {544serv = calloc(1, servlen);545if (serv == NULL) {546error = EAI_MEMORY;547goto out;548}549}550551sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);552if (sabinsize > sizeof(sast)) {553error = EAI_FAIL;554goto out;555}556557memcpy(&sast, sabin, sabinsize);558salen = (socklen_t)sabinsize;559560if ((sast.ss_family != AF_INET ||561salen != sizeof(struct sockaddr_in)) &&562(sast.ss_family != AF_INET6 ||563salen != sizeof(struct sockaddr_in6))) {564error = EAI_FAIL;565goto out;566}567568if (!dns_allowed_family(limits, (int)sast.ss_family)) {569error = NO_RECOVERY;570goto out;571}572573flags = (int)nvlist_get_number(nvlin, "flags");574575error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,576serv, servlen, flags);577if (error != 0)578goto out;579580if (host != NULL)581nvlist_move_string(nvlout, "host", host);582if (serv != NULL)583nvlist_move_string(nvlout, "serv", serv);584out:585if (error != 0) {586free(host);587free(serv);588}589return (error);590}591592static nvlist_t *593addrinfo_pack(const struct addrinfo *ai)594{595nvlist_t *nvl;596597nvl = nvlist_create(0);598nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);599nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);600nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);601nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);602nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);603if (ai->ai_canonname != NULL)604nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);605606return (nvl);607}608609static int610dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)611{612struct addrinfo hints, *hintsp, *res, *cur;613const char *hostname, *servname;614char nvlname[64];615nvlist_t *elem;616unsigned int ii;617int error, family, n;618619if (!dns_allowed_type(limits, "NAME2ADDR") &&620!dns_allowed_type(limits, "NAME"))621return (NO_RECOVERY);622623hostname = dnvlist_get_string(nvlin, "hostname", NULL);624servname = dnvlist_get_string(nvlin, "servname", NULL);625if (nvlist_exists_number(nvlin, "hints.ai_flags")) {626hints.ai_flags = (int)nvlist_get_number(nvlin,627"hints.ai_flags");628hints.ai_family = (int)nvlist_get_number(nvlin,629"hints.ai_family");630hints.ai_socktype = (int)nvlist_get_number(nvlin,631"hints.ai_socktype");632hints.ai_protocol = (int)nvlist_get_number(nvlin,633"hints.ai_protocol");634hints.ai_addrlen = 0;635hints.ai_addr = NULL;636hints.ai_canonname = NULL;637hints.ai_next = NULL;638hintsp = &hints;639family = hints.ai_family;640} else {641hintsp = NULL;642family = AF_UNSPEC;643}644645if (!dns_allowed_family(limits, family))646return (NO_RECOVERY);647648error = getaddrinfo(hostname, servname, hintsp, &res);649if (error != 0)650goto out;651652for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {653elem = addrinfo_pack(cur);654n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);655assert(n > 0 && n < (int)sizeof(nvlname));656nvlist_move_nvlist(nvlout, nvlname, elem);657}658659freeaddrinfo(res);660error = 0;661out:662return (error);663}664665static bool666limit_has_entry(const nvlist_t *limits, const char *prefix)667{668const char *name;669size_t prefixlen;670void *cookie;671672if (limits == NULL)673return (false);674675prefixlen = strlen(prefix);676677cookie = NULL;678while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {679if (strncmp(name, prefix, prefixlen) == 0)680return (true);681}682683return (false);684}685686static int687dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)688{689const char *name;690void *cookie;691int nvtype;692bool hastype, hasfamily;693694hastype = false;695hasfamily = false;696697cookie = NULL;698while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {699if (nvtype == NV_TYPE_STRING) {700const char *type;701702if (strncmp(name, "type", sizeof("type") - 1) != 0)703return (EINVAL);704type = nvlist_get_string(newlimits, name);705if (strcmp(type, "ADDR2NAME") != 0 &&706strcmp(type, "NAME2ADDR") != 0 &&707strcmp(type, "ADDR") != 0 &&708strcmp(type, "NAME") != 0) {709return (EINVAL);710}711if (!dns_allowed_type(oldlimits, type))712return (ENOTCAPABLE);713hastype = true;714} else if (nvtype == NV_TYPE_NUMBER) {715int family;716717if (strncmp(name, "family", sizeof("family") - 1) != 0)718return (EINVAL);719family = (int)nvlist_get_number(newlimits, name);720if (!dns_allowed_family(oldlimits, family))721return (ENOTCAPABLE);722hasfamily = true;723} else {724return (EINVAL);725}726}727728/*729* If the new limit doesn't mention type or family we have to730* check if the current limit does have those. Missing type or731* family in the limit means that all types or families are732* allowed.733*/734if (!hastype) {735if (limit_has_entry(oldlimits, "type"))736return (ENOTCAPABLE);737}738if (!hasfamily) {739if (limit_has_entry(oldlimits, "family"))740return (ENOTCAPABLE);741}742743return (0);744}745746static int747dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,748nvlist_t *nvlout)749{750int error;751752if (strcmp(cmd, "gethostbyname") == 0)753error = dns_gethostbyname(limits, nvlin, nvlout);754else if (strcmp(cmd, "gethostbyaddr") == 0)755error = dns_gethostbyaddr(limits, nvlin, nvlout);756else if (strcmp(cmd, "getnameinfo") == 0)757error = dns_getnameinfo(limits, nvlin, nvlout);758else if (strcmp(cmd, "getaddrinfo") == 0)759error = dns_getaddrinfo(limits, nvlin, nvlout);760else761error = NO_RECOVERY;762763return (error);764}765766CREATE_SERVICE("system.dns", dns_limit, dns_command, 0);767768769