diff --git a/include/xlocale.h b/include/xlocale.h index f39000d6b373..cc2fc0839593 100644 --- a/include/xlocale.h +++ b/include/xlocale.h @@ -1,85 +1,84 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2011, 2012 The FreeBSD Foundation - * All rights reserved. * * This software was developed by David Chisnall under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _XLOCALE_H_ #define _XLOCALE_H_ #include __BEGIN_DECLS #include #ifdef _STRING_H_ #include #endif #ifdef _INTTYPES_H_ #include #endif #ifdef _MONETARY_H_ #include #endif #ifdef _STDLIB_H_ #include #endif #ifdef _TIME_H_ #include #endif #ifdef _LANGINFO_H_ #include #endif #ifdef _CTYPE_H_ #include #endif #ifdef _WCTYPE_H_ #define _XLOCALE_WCTYPES 1 #include #endif #ifdef _STDIO_H_ #include #endif #ifdef _WCHAR_H_ #include #endif struct lconv *localeconv_l(locale_t); __END_DECLS #endif diff --git a/lib/libcasper/services/cap_dns/cap_dns.c b/lib/libcasper/services/cap_dns/cap_dns.c index 32cbf973ad45..8681f0baef40 100644 --- a/lib/libcasper/services/cap_dns/cap_dns.c +++ b/lib/libcasper/services/cap_dns/cap_dns.c @@ -1,768 +1,767 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2012-2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "cap_dns.h" static struct hostent hent; static void hostent_free(struct hostent *hp) { unsigned int ii; free(hp->h_name); hp->h_name = NULL; if (hp->h_aliases != NULL) { for (ii = 0; hp->h_aliases[ii] != NULL; ii++) free(hp->h_aliases[ii]); free(hp->h_aliases); hp->h_aliases = NULL; } if (hp->h_addr_list != NULL) { for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) free(hp->h_addr_list[ii]); free(hp->h_addr_list); hp->h_addr_list = NULL; } } static struct hostent * hostent_unpack(const nvlist_t *nvl, struct hostent *hp) { unsigned int ii, nitems; char nvlname[64]; int n; hostent_free(hp); hp->h_name = strdup(nvlist_get_string(nvl, "name")); if (hp->h_name == NULL) goto fail; hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype"); hp->h_length = (int)nvlist_get_number(nvl, "length"); nitems = (unsigned int)nvlist_get_number(nvl, "naliases"); hp->h_aliases = calloc(nitems + 1, sizeof(hp->h_aliases[0])); if (hp->h_aliases == NULL) goto fail; for (ii = 0; ii < nitems; ii++) { n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); hp->h_aliases[ii] = strdup(nvlist_get_string(nvl, nvlname)); if (hp->h_aliases[ii] == NULL) goto fail; } hp->h_aliases[ii] = NULL; nitems = (unsigned int)nvlist_get_number(nvl, "naddrs"); hp->h_addr_list = calloc(nitems + 1, sizeof(hp->h_addr_list[0])); if (hp->h_addr_list == NULL) goto fail; for (ii = 0; ii < nitems; ii++) { hp->h_addr_list[ii] = malloc(hp->h_length); if (hp->h_addr_list[ii] == NULL) goto fail; n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); bcopy(nvlist_get_binary(nvl, nvlname, NULL), hp->h_addr_list[ii], hp->h_length); } hp->h_addr_list[ii] = NULL; return (hp); fail: hostent_free(hp); h_errno = NO_RECOVERY; return (NULL); } struct hostent * cap_gethostbyname(cap_channel_t *chan, const char *name) { return (cap_gethostbyname2(chan, name, AF_INET)); } struct hostent * cap_gethostbyname2(cap_channel_t *chan, const char *name, int type) { struct hostent *hp; nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "gethostbyname"); nvlist_add_number(nvl, "family", (uint64_t)type); nvlist_add_string(nvl, "name", name); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { h_errno = NO_RECOVERY; return (NULL); } if (nvlist_get_number(nvl, "error") != 0) { h_errno = (int)nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (NULL); } hp = hostent_unpack(nvl, &hent); nvlist_destroy(nvl); return (hp); } struct hostent * cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len, int type) { struct hostent *hp; nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "gethostbyaddr"); nvlist_add_binary(nvl, "addr", addr, (size_t)len); nvlist_add_number(nvl, "family", (uint64_t)type); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { h_errno = NO_RECOVERY; return (NULL); } if (nvlist_get_number(nvl, "error") != 0) { h_errno = (int)nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (NULL); } hp = hostent_unpack(nvl, &hent); nvlist_destroy(nvl); return (hp); } static struct addrinfo * addrinfo_unpack(const nvlist_t *nvl) { struct addrinfo *ai; const void *addr; size_t addrlen; const char *canonname; addr = nvlist_get_binary(nvl, "ai_addr", &addrlen); ai = malloc(sizeof(*ai) + addrlen); if (ai == NULL) return (NULL); ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags"); ai->ai_family = (int)nvlist_get_number(nvl, "ai_family"); ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype"); ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol"); ai->ai_addrlen = (socklen_t)addrlen; canonname = dnvlist_get_string(nvl, "ai_canonname", NULL); if (canonname != NULL) { ai->ai_canonname = strdup(canonname); if (ai->ai_canonname == NULL) { free(ai); return (NULL); } } else { ai->ai_canonname = NULL; } ai->ai_addr = (void *)(ai + 1); bcopy(addr, ai->ai_addr, addrlen); ai->ai_next = NULL; return (ai); } int cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { struct addrinfo *firstai, *prevai, *curai; unsigned int ii; const nvlist_t *nvlai; char nvlname[64]; nvlist_t *nvl; int error, n; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "getaddrinfo"); if (hostname != NULL) nvlist_add_string(nvl, "hostname", hostname); if (servname != NULL) nvlist_add_string(nvl, "servname", servname); if (hints != NULL) { nvlist_add_number(nvl, "hints.ai_flags", (uint64_t)hints->ai_flags); nvlist_add_number(nvl, "hints.ai_family", (uint64_t)hints->ai_family); nvlist_add_number(nvl, "hints.ai_socktype", (uint64_t)hints->ai_socktype); nvlist_add_number(nvl, "hints.ai_protocol", (uint64_t)hints->ai_protocol); } nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (EAI_MEMORY); if (nvlist_get_number(nvl, "error") != 0) { error = (int)nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (error); } nvlai = NULL; firstai = prevai = curai = NULL; for (ii = 0; ; ii++) { n = snprintf(nvlname, sizeof(nvlname), "res%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); if (!nvlist_exists_nvlist(nvl, nvlname)) break; nvlai = nvlist_get_nvlist(nvl, nvlname); curai = addrinfo_unpack(nvlai); if (curai == NULL) break; if (prevai != NULL) prevai->ai_next = curai; else if (firstai == NULL) firstai = curai; prevai = curai; } nvlist_destroy(nvl); if (curai == NULL && nvlai != NULL) { if (firstai == NULL) freeaddrinfo(firstai); return (EAI_MEMORY); } *res = firstai; return (0); } int cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { nvlist_t *nvl; int error; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "getnameinfo"); nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen); nvlist_add_number(nvl, "servlen", (uint64_t)servlen); nvlist_add_binary(nvl, "sa", sa, (size_t)salen); nvlist_add_number(nvl, "flags", (uint64_t)flags); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (EAI_MEMORY); if (nvlist_get_number(nvl, "error") != 0) { error = (int)nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (error); } if (host != NULL && nvlist_exists_string(nvl, "host")) strlcpy(host, nvlist_get_string(nvl, "host"), hostlen); if (serv != NULL && nvlist_exists_string(nvl, "serv")) strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen); nvlist_destroy(nvl); return (0); } static void limit_remove(nvlist_t *limits, const char *prefix) { const char *name; size_t prefixlen; void *cookie; prefixlen = strlen(prefix); again: cookie = NULL; while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) { if (strncmp(name, prefix, prefixlen) == 0) { nvlist_free(limits, name); goto again; } } } int cap_dns_type_limit(cap_channel_t *chan, const char * const *types, size_t ntypes) { nvlist_t *limits; unsigned int i; char nvlname[64]; int n; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) limits = nvlist_create(0); else limit_remove(limits, "type"); for (i = 0; i < ntypes; i++) { n = snprintf(nvlname, sizeof(nvlname), "type%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_string(limits, nvlname, types[i]); } return (cap_limit_set(chan, limits)); } int cap_dns_family_limit(cap_channel_t *chan, const int *families, size_t nfamilies) { nvlist_t *limits; unsigned int i; char nvlname[64]; int n; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) limits = nvlist_create(0); else limit_remove(limits, "family"); for (i = 0; i < nfamilies; i++) { n = snprintf(nvlname, sizeof(nvlname), "family%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_number(limits, nvlname, (uint64_t)families[i]); } return (cap_limit_set(chan, limits)); } /* * Service functions. */ static bool dns_allowed_type(const nvlist_t *limits, const char *type) { const char *name; bool notypes; void *cookie; if (limits == NULL) return (true); notypes = true; cookie = NULL; while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) { if (strncmp(name, "type", sizeof("type") - 1) != 0) continue; notypes = false; if (strcmp(nvlist_get_string(limits, name), type) == 0) return (true); } /* If there are no types at all, allow any type. */ if (notypes) return (true); return (false); } static bool dns_allowed_family(const nvlist_t *limits, int family) { const char *name; bool nofamilies; void *cookie; if (limits == NULL) return (true); nofamilies = true; cookie = NULL; while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) { if (strncmp(name, "family", sizeof("family") - 1) != 0) continue; nofamilies = false; if (family == AF_UNSPEC) continue; if (nvlist_get_number(limits, name) == (uint64_t)family) return (true); } /* If there are no families at all, allow any family. */ if (nofamilies) return (true); return (false); } static void hostent_pack(const struct hostent *hp, nvlist_t *nvl) { unsigned int ii; char nvlname[64]; int n; nvlist_add_string(nvl, "name", hp->h_name); nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype); nvlist_add_number(nvl, "length", (uint64_t)hp->h_length); if (hp->h_aliases == NULL) { nvlist_add_number(nvl, "naliases", 0); } else { for (ii = 0; hp->h_aliases[ii] != NULL; ii++) { n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_string(nvl, nvlname, hp->h_aliases[ii]); } nvlist_add_number(nvl, "naliases", (uint64_t)ii); } if (hp->h_addr_list == NULL) { nvlist_add_number(nvl, "naddrs", 0); } else { for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) { n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii], (size_t)hp->h_length); } nvlist_add_number(nvl, "naddrs", (uint64_t)ii); } } static int dns_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) { struct hostent *hp; int family; if (!dns_allowed_type(limits, "NAME2ADDR") && !dns_allowed_type(limits, "NAME")) return (NO_RECOVERY); family = (int)nvlist_get_number(nvlin, "family"); if (!dns_allowed_family(limits, family)) return (NO_RECOVERY); hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family); if (hp == NULL) return (h_errno); hostent_pack(hp, nvlout); return (0); } static int dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) { struct hostent *hp; const void *addr; size_t addrsize; int family; if (!dns_allowed_type(limits, "ADDR2NAME") && !dns_allowed_type(limits, "ADDR")) return (NO_RECOVERY); family = (int)nvlist_get_number(nvlin, "family"); if (!dns_allowed_family(limits, family)) return (NO_RECOVERY); addr = nvlist_get_binary(nvlin, "addr", &addrsize); hp = gethostbyaddr(addr, (socklen_t)addrsize, family); if (hp == NULL) return (h_errno); hostent_pack(hp, nvlout); return (0); } static int dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) { struct sockaddr_storage sast; const void *sabin; char *host, *serv; size_t sabinsize, hostlen, servlen; socklen_t salen; int error, flags; if (!dns_allowed_type(limits, "ADDR2NAME") && !dns_allowed_type(limits, "ADDR")) return (NO_RECOVERY); error = 0; host = serv = NULL; memset(&sast, 0, sizeof(sast)); hostlen = (size_t)nvlist_get_number(nvlin, "hostlen"); servlen = (size_t)nvlist_get_number(nvlin, "servlen"); if (hostlen > 0) { host = calloc(1, hostlen); if (host == NULL) { error = EAI_MEMORY; goto out; } } if (servlen > 0) { serv = calloc(1, servlen); if (serv == NULL) { error = EAI_MEMORY; goto out; } } sabin = nvlist_get_binary(nvlin, "sa", &sabinsize); if (sabinsize > sizeof(sast)) { error = EAI_FAIL; goto out; } memcpy(&sast, sabin, sabinsize); salen = (socklen_t)sabinsize; if ((sast.ss_family != AF_INET || salen != sizeof(struct sockaddr_in)) && (sast.ss_family != AF_INET6 || salen != sizeof(struct sockaddr_in6))) { error = EAI_FAIL; goto out; } if (!dns_allowed_family(limits, (int)sast.ss_family)) { error = NO_RECOVERY; goto out; } flags = (int)nvlist_get_number(nvlin, "flags"); error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen, serv, servlen, flags); if (error != 0) goto out; if (host != NULL) nvlist_move_string(nvlout, "host", host); if (serv != NULL) nvlist_move_string(nvlout, "serv", serv); out: if (error != 0) { free(host); free(serv); } return (error); } static nvlist_t * addrinfo_pack(const struct addrinfo *ai) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags); nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family); nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype); nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol); nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen); if (ai->ai_canonname != NULL) nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname); return (nvl); } static int dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) { struct addrinfo hints, *hintsp, *res, *cur; const char *hostname, *servname; char nvlname[64]; nvlist_t *elem; unsigned int ii; int error, family, n; if (!dns_allowed_type(limits, "NAME2ADDR") && !dns_allowed_type(limits, "NAME")) return (NO_RECOVERY); hostname = dnvlist_get_string(nvlin, "hostname", NULL); servname = dnvlist_get_string(nvlin, "servname", NULL); if (nvlist_exists_number(nvlin, "hints.ai_flags")) { hints.ai_flags = (int)nvlist_get_number(nvlin, "hints.ai_flags"); hints.ai_family = (int)nvlist_get_number(nvlin, "hints.ai_family"); hints.ai_socktype = (int)nvlist_get_number(nvlin, "hints.ai_socktype"); hints.ai_protocol = (int)nvlist_get_number(nvlin, "hints.ai_protocol"); hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; hintsp = &hints; family = hints.ai_family; } else { hintsp = NULL; family = AF_UNSPEC; } if (!dns_allowed_family(limits, family)) return (NO_RECOVERY); error = getaddrinfo(hostname, servname, hintsp, &res); if (error != 0) goto out; for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) { elem = addrinfo_pack(cur); n = snprintf(nvlname, sizeof(nvlname), "res%u", ii); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_move_nvlist(nvlout, nvlname, elem); } freeaddrinfo(res); error = 0; out: return (error); } static bool limit_has_entry(const nvlist_t *limits, const char *prefix) { const char *name; size_t prefixlen; void *cookie; if (limits == NULL) return (false); prefixlen = strlen(prefix); cookie = NULL; while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) { if (strncmp(name, prefix, prefixlen) == 0) return (true); } return (false); } static int dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) { const char *name; void *cookie; int nvtype; bool hastype, hasfamily; hastype = false; hasfamily = false; cookie = NULL; while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) { if (nvtype == NV_TYPE_STRING) { const char *type; if (strncmp(name, "type", sizeof("type") - 1) != 0) return (EINVAL); type = nvlist_get_string(newlimits, name); if (strcmp(type, "ADDR2NAME") != 0 && strcmp(type, "NAME2ADDR") != 0 && strcmp(type, "ADDR") != 0 && strcmp(type, "NAME") != 0) { return (EINVAL); } if (!dns_allowed_type(oldlimits, type)) return (ENOTCAPABLE); hastype = true; } else if (nvtype == NV_TYPE_NUMBER) { int family; if (strncmp(name, "family", sizeof("family") - 1) != 0) return (EINVAL); family = (int)nvlist_get_number(newlimits, name); if (!dns_allowed_family(oldlimits, family)) return (ENOTCAPABLE); hasfamily = true; } else { return (EINVAL); } } /* * If the new limit doesn't mention type or family we have to * check if the current limit does have those. Missing type or * family in the limit means that all types or families are * allowed. */ if (!hastype) { if (limit_has_entry(oldlimits, "type")) return (ENOTCAPABLE); } if (!hasfamily) { if (limit_has_entry(oldlimits, "family")) return (ENOTCAPABLE); } return (0); } static int dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout) { int error; if (strcmp(cmd, "gethostbyname") == 0) error = dns_gethostbyname(limits, nvlin, nvlout); else if (strcmp(cmd, "gethostbyaddr") == 0) error = dns_gethostbyaddr(limits, nvlin, nvlout); else if (strcmp(cmd, "getnameinfo") == 0) error = dns_getnameinfo(limits, nvlin, nvlout); else if (strcmp(cmd, "getaddrinfo") == 0) error = dns_getaddrinfo(limits, nvlin, nvlout); else error = NO_RECOVERY; return (error); } CREATE_SERVICE("system.dns", dns_limit, dns_command, 0); diff --git a/lib/libcasper/services/cap_dns/cap_dns.h b/lib/libcasper/services/cap_dns/cap_dns.h index 4bf8e3648a2d..556cac1158d2 100644 --- a/lib/libcasper/services/cap_dns/cap_dns.h +++ b/lib/libcasper/services/cap_dns/cap_dns.h @@ -1,132 +1,131 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _CAP_DNS_H_ #define _CAP_DNS_H_ #ifdef HAVE_CASPER #define WITH_CASPER #endif #include #include /* socklen_t */ /* * Pull these in if we're just inlining calls to the underlying * libc functions. */ #ifndef WITH_CASPER #include #include #endif /* WITH_CASPER */ struct addrinfo; struct hostent; #ifdef WITH_CASPER __BEGIN_DECLS struct hostent *cap_gethostbyname(cap_channel_t *chan, const char *name); struct hostent *cap_gethostbyname2(cap_channel_t *chan, const char *name, int type); struct hostent *cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len, int type); int cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res); int cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags); int cap_dns_type_limit(cap_channel_t *chan, const char * const *types, size_t ntypes); int cap_dns_family_limit(cap_channel_t *chan, const int *families, size_t nfamilies); __END_DECLS #else static inline struct hostent * cap_gethostbyname(cap_channel_t *chan __unused, const char *name) { return (gethostbyname(name)); } static inline struct hostent * cap_gethostbyname2(cap_channel_t *chan __unused, const char *name, int type) { return (gethostbyname2(name, type)); } static inline struct hostent * cap_gethostbyaddr(cap_channel_t *chan __unused, const void *addr, socklen_t len, int type) { return (gethostbyaddr(addr, len, type)); } static inline int cap_getaddrinfo(cap_channel_t *chan __unused, const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { return (getaddrinfo(hostname, servname, hints, res)); } static inline int cap_getnameinfo(cap_channel_t *chan __unused, const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { return (getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)); } static inline int cap_dns_type_limit(cap_channel_t *chan __unused, const char * const *types __unused, size_t ntypes __unused) { return (0); } static inline int cap_dns_family_limit(cap_channel_t *chan __unused, const int *families __unused, size_t nfamilies __unused) { return (0); } #endif /* WITH_CASPER */ #endif /* !_CAP_DNS_H_ */ diff --git a/lib/libcasper/services/cap_dns/tests/dns_test.c b/lib/libcasper/services/cap_dns/tests/dns_test.c index 2b50ab446dd1..76534bbbebad 100644 --- a/lib/libcasper/services/cap_dns/tests/dns_test.c +++ b/lib/libcasper/services/cap_dns/tests/dns_test.c @@ -1,717 +1,716 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GETHOSTBYNAME 0x01 #define GETHOSTBYNAME2_AF_INET 0x02 #define GETHOSTBYNAME2_AF_INET6 0x04 #define GETHOSTBYADDR_AF_INET 0x08 #define GETHOSTBYADDR_AF_INET6 0x10 #define GETADDRINFO_AF_UNSPEC 0x20 #define GETADDRINFO_AF_INET 0x40 #define GETADDRINFO_AF_INET6 0x80 static bool addrinfo_compare(struct addrinfo *ai0, struct addrinfo *ai1) { struct addrinfo *at0, *at1; if (ai0 == NULL && ai1 == NULL) return (true); if (ai0 == NULL || ai1 == NULL) return (false); at0 = ai0; at1 = ai1; while (true) { if ((at0->ai_flags == at1->ai_flags) && (at0->ai_family == at1->ai_family) && (at0->ai_socktype == at1->ai_socktype) && (at0->ai_protocol == at1->ai_protocol) && (at0->ai_addrlen == at1->ai_addrlen) && (memcmp(at0->ai_addr, at1->ai_addr, at0->ai_addrlen) == 0)) { if (at0->ai_canonname != NULL && at1->ai_canonname != NULL) { if (strcmp(at0->ai_canonname, at1->ai_canonname) != 0) { return (false); } } if (at0->ai_canonname == NULL && at1->ai_canonname != NULL) { return (false); } if (at0->ai_canonname != NULL && at1->ai_canonname == NULL) { return (false); } if (at0->ai_next == NULL && at1->ai_next == NULL) return (true); if (at0->ai_next == NULL || at1->ai_next == NULL) return (false); at0 = at0->ai_next; at1 = at1->ai_next; } else { return (false); } } /* NOTREACHED */ fprintf(stderr, "Dead code reached in addrinfo_compare()\n"); exit(1); } static bool hostent_aliases_compare(char **aliases0, char **aliases1) { int i0, i1; if (aliases0 == NULL && aliases1 == NULL) return (true); if (aliases0 == NULL || aliases1 == NULL) return (false); for (i0 = 0; aliases0[i0] != NULL; i0++) { for (i1 = 0; aliases1[i1] != NULL; i1++) { if (strcmp(aliases0[i0], aliases1[i1]) == 0) break; } if (aliases1[i1] == NULL) return (false); } return (true); } static bool hostent_addr_list_compare(char **addr_list0, char **addr_list1, int length) { int i0, i1; if (addr_list0 == NULL && addr_list1 == NULL) return (true); if (addr_list0 == NULL || addr_list1 == NULL) return (false); for (i0 = 0; addr_list0[i0] != NULL; i0++) { for (i1 = 0; addr_list1[i1] != NULL; i1++) { if (memcmp(addr_list0[i0], addr_list1[i1], length) == 0) break; } if (addr_list1[i1] == NULL) return (false); } return (true); } static bool hostent_compare(const struct hostent *hp0, const struct hostent *hp1) { if (hp0 == NULL && hp1 != NULL) return (true); if (hp0 == NULL || hp1 == NULL) return (false); if (hp0->h_name != NULL || hp1->h_name != NULL) { if (hp0->h_name == NULL || hp1->h_name == NULL) return (false); if (strcmp(hp0->h_name, hp1->h_name) != 0) return (false); } if (!hostent_aliases_compare(hp0->h_aliases, hp1->h_aliases)) return (false); if (!hostent_aliases_compare(hp1->h_aliases, hp0->h_aliases)) return (false); if (hp0->h_addrtype != hp1->h_addrtype) return (false); if (hp0->h_length != hp1->h_length) return (false); if (!hostent_addr_list_compare(hp0->h_addr_list, hp1->h_addr_list, hp0->h_length)) { return (false); } if (!hostent_addr_list_compare(hp1->h_addr_list, hp0->h_addr_list, hp0->h_length)) { return (false); } return (true); } static void runtest(cap_channel_t *capdns, unsigned int expected) { unsigned int result, failure; struct addrinfo *ais, *aic, hints, *hintsp; struct hostent *hps, *hpc; struct in_addr ip4; struct in6_addr ip6; int caperr, syserr; failure = result = 0; hps = gethostbyname("example.com"); if (hps == NULL) { failure |= GETHOSTBYNAME; fprintf(stderr, "Unable to resolve %s IPv4.\n", "example.com"); } else { hpc = cap_gethostbyname(capdns, "example.com"); if (hostent_compare(hps, hpc)) result |= GETHOSTBYNAME; } hps = gethostbyname2("example.com", AF_INET); if (hps == NULL) { failure |= GETHOSTBYNAME2_AF_INET; fprintf(stderr, "Unable to resolve %s IPv4.\n", "example.com"); } else { hpc = cap_gethostbyname2(capdns, "example.com", AF_INET); if (hostent_compare(hps, hpc)) result |= GETHOSTBYNAME2_AF_INET; } hps = gethostbyname2("example.com", AF_INET6); if (hps == NULL) { failure |= GETHOSTBYNAME2_AF_INET6; fprintf(stderr, "Unable to resolve %s IPv6.\n", "example.com"); } else { hpc = cap_gethostbyname2(capdns, "example.com", AF_INET6); if (hostent_compare(hps, hpc)) result |= GETHOSTBYNAME2_AF_INET6; } hints.ai_flags = 0; hints.ai_family = AF_UNSPEC; hints.ai_socktype = 0; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; hintsp = &hints; syserr = getaddrinfo("freebsd.org", "25", hintsp, &ais); if (syserr != 0) { failure |= GETADDRINFO_AF_UNSPEC; fprintf(stderr, "Unable to issue [system] getaddrinfo() for AF_UNSPEC: %s\n", gai_strerror(syserr)); } else { caperr = cap_getaddrinfo(capdns, "freebsd.org", "25", hintsp, &aic); if (caperr == 0) { if (addrinfo_compare(ais, aic)) result |= GETADDRINFO_AF_UNSPEC; freeaddrinfo(ais); freeaddrinfo(aic); } } hints.ai_family = AF_INET; syserr = getaddrinfo("freebsd.org", "25", hintsp, &ais); if (syserr != 0) { failure |= GETADDRINFO_AF_INET; fprintf(stderr, "Unable to issue [system] getaddrinfo() for AF_UNSPEC: %s\n", gai_strerror(syserr)); } else { caperr = cap_getaddrinfo(capdns, "freebsd.org", "25", hintsp, &aic); if (caperr == 0) { if (addrinfo_compare(ais, aic)) result |= GETADDRINFO_AF_INET; freeaddrinfo(ais); freeaddrinfo(aic); } } hints.ai_family = AF_INET6; syserr = getaddrinfo("freebsd.org", "25", hintsp, &ais); if (syserr != 0) { failure |= GETADDRINFO_AF_INET6; fprintf(stderr, "Unable to issue [system] getaddrinfo() for AF_UNSPEC: %s\n", gai_strerror(syserr)); } else { caperr = cap_getaddrinfo(capdns, "freebsd.org", "25", hintsp, &aic); if (caperr == 0) { if (addrinfo_compare(ais, aic)) result |= GETADDRINFO_AF_INET6; freeaddrinfo(ais); freeaddrinfo(aic); } } /* XXX: hardcoded addresses for "google-public-dns-a.google.com". */ #define GOOGLE_DNS_IPV4 "8.8.8.8" #define GOOGLE_DNS_IPV6 "2001:4860:4860::8888" inet_pton(AF_INET, GOOGLE_DNS_IPV4, &ip4); hps = gethostbyaddr(&ip4, sizeof(ip4), AF_INET); if (hps == NULL) { failure |= GETHOSTBYADDR_AF_INET; fprintf(stderr, "Unable to resolve %s.\n", GOOGLE_DNS_IPV4); } else { hpc = cap_gethostbyaddr(capdns, &ip4, sizeof(ip4), AF_INET); if (hostent_compare(hps, hpc)) result |= GETHOSTBYADDR_AF_INET; } inet_pton(AF_INET6, GOOGLE_DNS_IPV6, &ip6); hps = gethostbyaddr(&ip6, sizeof(ip6), AF_INET6); if (hps == NULL) { failure |= GETHOSTBYADDR_AF_INET6; fprintf(stderr, "Unable to resolve %s.\n", GOOGLE_DNS_IPV6); } else { hpc = cap_gethostbyaddr(capdns, &ip6, sizeof(ip6), AF_INET6); if (hostent_compare(hps, hpc)) { caperr = h_errno; result |= GETHOSTBYADDR_AF_INET6; } } /* * If we had any failures, make sure that all lookups failed. If some * succeeded and some failed, there's a problem with the test or the DNS * and we should not fail silently. */ if (failure != 0) { ATF_REQUIRE_MSG(failure == (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYNAME2_AF_INET6 | GETADDRINFO_AF_UNSPEC | GETADDRINFO_AF_INET | GETADDRINFO_AF_INET6 | GETHOSTBYADDR_AF_INET | GETHOSTBYADDR_AF_INET6), "expected all tests to fail, got 0x%x", failure); atf_tc_skip( "no name lookups succeeded, tests require Internet access"); } ATF_REQUIRE_MSG(result == expected, "expected 0x%x, got 0x%x", expected, result); } static cap_channel_t * cap_dns_init(void) { cap_channel_t *capcas, *capdns; capcas = cap_init(); ATF_REQUIRE(capcas != NULL); capdns = cap_service_open(capcas, "system.dns"); ATF_REQUIRE(capdns != NULL); cap_close(capcas); return (capdns); } ATF_TC(dns_no_limits); ATF_TC_HEAD(dns_no_limits, tc) { } ATF_TC_BODY(dns_no_limits, tc) { cap_channel_t *capdns; capdns = cap_dns_init(); runtest(capdns, (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYNAME2_AF_INET6 | GETHOSTBYADDR_AF_INET | GETHOSTBYADDR_AF_INET6 | GETADDRINFO_AF_UNSPEC | GETADDRINFO_AF_INET | GETADDRINFO_AF_INET6)); cap_close(capdns); } ATF_TC(dns_all_limits); ATF_TC_HEAD(dns_all_limits, tc) { } ATF_TC_BODY(dns_all_limits, tc) { cap_channel_t *capdns; const char *types[2]; int families[2]; capdns = cap_dns_init(); types[0] = "NAME2ADDR"; types[1] = "ADDR2NAME"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET; families[1] = AF_INET6; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0); ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, NULL, 0) == -1); ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, NULL, 0) == -1); runtest(capdns, (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYNAME2_AF_INET6 | GETHOSTBYADDR_AF_INET | GETHOSTBYADDR_AF_INET6 | GETADDRINFO_AF_INET | GETADDRINFO_AF_INET6)); cap_close(capdns); } ATF_TC(dns_name_limit); ATF_TC_HEAD(dns_name_limit, tc) { } ATF_TC_BODY(dns_name_limit, tc) { cap_channel_t *capdns; const char *types[2]; int families[2]; capdns = cap_dns_init(); types[0] = "NAME2ADDR"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "ADDR2NAME"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 2) == -1); types[0] = "ADDR2NAME"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 1) == -1); families[0] = AF_INET; families[1] = AF_INET6; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0); runtest(capdns, (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYNAME2_AF_INET6 | GETADDRINFO_AF_INET | GETADDRINFO_AF_INET6)); cap_close(capdns); } ATF_TC(dns_addr_limit); ATF_TC_HEAD(dns_addr_limit, tc) { } ATF_TC_BODY(dns_addr_limit, tc) { cap_channel_t *capdns; const char *types[2]; int families[2]; capdns = cap_dns_init(); types[0] = "ADDR2NAME"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "NAME2ADDR"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 2) == -1); types[0] = "NAME2ADDR"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 1) == -1); families[0] = AF_INET; families[1] = AF_INET6; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0); runtest(capdns, (GETHOSTBYADDR_AF_INET | GETHOSTBYADDR_AF_INET6)); cap_close(capdns); } ATF_TC(dns_inet_limit); ATF_TC_HEAD(dns_inet_limit, tc) { } ATF_TC_BODY(dns_inet_limit, tc) { cap_channel_t *capdns; const char *types[2]; int families[2]; capdns = cap_dns_init(); types[0] = "NAME2ADDR"; types[1] = "ADDR2NAME"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET6; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 2) == -1); families[0] = AF_INET6; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 1) == -1); runtest(capdns, (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETHOSTBYADDR_AF_INET | GETADDRINFO_AF_INET)); cap_close(capdns); } ATF_TC(dns_inet6_limit); ATF_TC_HEAD(dns_inet6_limit, tc) { } ATF_TC_BODY(dns_inet6_limit, tc) { cap_channel_t *capdns; const char *types[2]; int families[2]; capdns = cap_dns_init(); types[0] = "NAME2ADDR"; types[1] = "ADDR2NAME"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET6; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 2) == -1); families[0] = AF_INET; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 1) == -1); runtest(capdns, (GETHOSTBYNAME2_AF_INET6 | GETHOSTBYADDR_AF_INET6 | GETADDRINFO_AF_INET6)); cap_close(capdns); } ATF_TC(dns_name_inet_limit); ATF_TC_HEAD(dns_name_inet_limit, tc) { } ATF_TC_BODY(dns_name_inet_limit, tc) { cap_channel_t *capdns; const char *types[2]; int families[2]; capdns = cap_dns_init(); types[0] = "NAME2ADDR"; types[1] = "ADDR2NAME"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET; families[1] = AF_INET6; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0); types[0] = "NAME2ADDR"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "ADDR2NAME"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 2) == -1); types[0] = "ADDR2NAME"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 1) == -1); families[0] = AF_INET; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET6; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 2) == -1); families[0] = AF_INET6; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 1) == -1); runtest(capdns, (GETHOSTBYNAME | GETHOSTBYNAME2_AF_INET | GETADDRINFO_AF_INET)); cap_close(capdns); } ATF_TC(dns_name_inet6_limit); ATF_TC_HEAD(dns_name_inet6_limit, tc) { } ATF_TC_BODY(dns_name_inet6_limit, tc) { cap_channel_t *capdns; const char *types[2]; int families[2]; capdns = cap_dns_init(); types[0] = "NAME2ADDR"; types[1] = "ADDR2NAME"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET6; families[1] = AF_INET; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0); types[0] = "NAME2ADDR"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "ADDR2NAME"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 2) == -1); types[0] = "ADDR2NAME"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 1) == -1); families[0] = AF_INET6; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 2) == -1); families[0] = AF_INET; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 1) == -1); runtest(capdns, (GETHOSTBYNAME2_AF_INET6 | GETADDRINFO_AF_INET6)); cap_close(capdns); } ATF_TC(dns_addr_inet_limit); ATF_TC_HEAD(dns_addr_inet_limit, tc) { } ATF_TC_BODY(dns_addr_inet_limit, tc) { cap_channel_t *capdns; const char *types[2]; int families[2]; capdns = cap_dns_init(); types[0] = "NAME2ADDR"; types[1] = "ADDR2NAME"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET; families[1] = AF_INET6; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0); types[0] = "ADDR2NAME"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "NAME2ADDR"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 2) == -1); types[0] = "NAME2ADDR"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 1) == -1); families[0] = AF_INET; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET6; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 2) == -1); families[0] = AF_INET6; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 1) == -1); runtest(capdns, GETHOSTBYADDR_AF_INET); cap_close(capdns); } ATF_TC(dns_addr_inet6_limit); ATF_TC_HEAD(dns_addr_inet6_limit, tc) { } ATF_TC_BODY(dns_addr_inet6_limit, tc) { cap_channel_t *capdns; const char *types[2]; int families[2]; capdns = cap_dns_init(); types[0] = "NAME2ADDR"; types[1] = "ADDR2NAME"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 2) == 0); families[0] = AF_INET6; families[1] = AF_INET; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 2) == 0); types[0] = "ADDR2NAME"; ATF_REQUIRE(cap_dns_type_limit(capdns, types, 1) == 0); types[1] = "NAME2ADDR"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 2) == -1); types[0] = "NAME2ADDR"; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_type_limit(capdns, types, 1) == -1); families[0] = AF_INET6; ATF_REQUIRE(cap_dns_family_limit(capdns, families, 1) == 0); families[1] = AF_INET; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 2) == -1); families[0] = AF_INET; ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_dns_family_limit(capdns, families, 1) == -1); runtest(capdns, GETHOSTBYADDR_AF_INET6); cap_close(capdns); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, dns_no_limits); ATF_TP_ADD_TC(tp, dns_all_limits); ATF_TP_ADD_TC(tp, dns_name_limit); ATF_TP_ADD_TC(tp, dns_addr_limit); ATF_TP_ADD_TC(tp, dns_inet_limit); ATF_TP_ADD_TC(tp, dns_inet6_limit); ATF_TP_ADD_TC(tp, dns_name_inet_limit); ATF_TP_ADD_TC(tp, dns_name_inet6_limit); ATF_TP_ADD_TC(tp, dns_addr_inet_limit); ATF_TP_ADD_TC(tp, dns_addr_inet6_limit); return atf_no_error(); } diff --git a/lib/libcasper/services/cap_grp/cap_grp.c b/lib/libcasper/services/cap_grp/cap_grp.c index 6a5ad84fcd22..025ce00adf56 100644 --- a/lib/libcasper/services/cap_grp/cap_grp.c +++ b/lib/libcasper/services/cap_grp/cap_grp.c @@ -1,789 +1,788 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include "cap_grp.h" static struct group ggrp; static char *gbuffer; static size_t gbufsize; static int group_resize(void) { char *buf; if (gbufsize == 0) gbufsize = 1024; else gbufsize *= 2; buf = gbuffer; gbuffer = realloc(buf, gbufsize); if (gbuffer == NULL) { free(buf); gbufsize = 0; return (ENOMEM); } memset(gbuffer, 0, gbufsize); return (0); } static int group_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp, char **bufferp, size_t *bufsizep) { const char *str; size_t len; str = nvlist_get_string(nvl, fieldname); len = strlcpy(*bufferp, str, *bufsizep); if (len >= *bufsizep) return (ERANGE); *fieldp = *bufferp; *bufferp += len + 1; *bufsizep -= len + 1; return (0); } static int group_unpack_members(const nvlist_t *nvl, char ***fieldp, char **bufferp, size_t *bufsizep) { const char *mem; char **outstrs, *str, nvlname[64]; size_t nmem, datasize, strsize; unsigned int ii; int n; if (!nvlist_exists_number(nvl, "gr_nmem")) { datasize = _ALIGNBYTES + sizeof(char *); if (datasize >= *bufsizep) return (ERANGE); outstrs = (char **)_ALIGN(*bufferp); outstrs[0] = NULL; *fieldp = outstrs; *bufferp += datasize; *bufsizep -= datasize; return (0); } nmem = (size_t)nvlist_get_number(nvl, "gr_nmem"); datasize = _ALIGNBYTES + sizeof(char *) * (nmem + 1); for (ii = 0; ii < nmem; ii++) { n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii); assert(n > 0 && n < (int)sizeof(nvlname)); mem = dnvlist_get_string(nvl, nvlname, NULL); if (mem == NULL) return (EINVAL); datasize += strlen(mem) + 1; } if (datasize >= *bufsizep) return (ERANGE); outstrs = (char **)_ALIGN(*bufferp); str = (char *)outstrs + sizeof(char *) * (nmem + 1); for (ii = 0; ii < nmem; ii++) { n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ii); assert(n > 0 && n < (int)sizeof(nvlname)); mem = nvlist_get_string(nvl, nvlname); strsize = strlen(mem) + 1; memcpy(str, mem, strsize); outstrs[ii] = str; str += strsize; } assert(ii == nmem); outstrs[ii] = NULL; *fieldp = outstrs; *bufferp += datasize; *bufsizep -= datasize; return (0); } static int group_unpack(const nvlist_t *nvl, struct group *grp, char *buffer, size_t bufsize) { int error; if (!nvlist_exists_string(nvl, "gr_name")) return (EINVAL); explicit_bzero(grp, sizeof(*grp)); error = group_unpack_string(nvl, "gr_name", &grp->gr_name, &buffer, &bufsize); if (error != 0) return (error); error = group_unpack_string(nvl, "gr_passwd", &grp->gr_passwd, &buffer, &bufsize); if (error != 0) return (error); grp->gr_gid = (gid_t)nvlist_get_number(nvl, "gr_gid"); error = group_unpack_members(nvl, &grp->gr_mem, &buffer, &bufsize); if (error != 0) return (error); return (0); } static int cap_getgrcommon_r(cap_channel_t *chan, const char *cmd, const char *name, gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result) { nvlist_t *nvl; bool getgr_r; int error; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", cmd); if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0) { /* Add nothing. */ } else if (strcmp(cmd, "getgrnam") == 0 || strcmp(cmd, "getgrnam_r") == 0) { nvlist_add_string(nvl, "name", name); } else if (strcmp(cmd, "getgrgid") == 0 || strcmp(cmd, "getgrgid_r") == 0) { nvlist_add_number(nvl, "gid", (uint64_t)gid); } else { abort(); } nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { assert(errno != 0); *result = NULL; return (errno); } error = (int)nvlist_get_number(nvl, "error"); if (error != 0) { nvlist_destroy(nvl); *result = NULL; return (error); } if (!nvlist_exists_string(nvl, "gr_name")) { /* Not found. */ nvlist_destroy(nvl); *result = NULL; return (0); } getgr_r = (strcmp(cmd, "getgrent_r") == 0 || strcmp(cmd, "getgrnam_r") == 0 || strcmp(cmd, "getgrgid_r") == 0); for (;;) { error = group_unpack(nvl, grp, buffer, bufsize); if (getgr_r || error != ERANGE) break; assert(buffer == gbuffer); assert(bufsize == gbufsize); error = group_resize(); if (error != 0) break; /* Update pointers after resize. */ buffer = gbuffer; bufsize = gbufsize; } nvlist_destroy(nvl); if (error == 0) *result = grp; else *result = NULL; return (error); } static struct group * cap_getgrcommon(cap_channel_t *chan, const char *cmd, const char *name, gid_t gid) { struct group *result; int error, serrno; serrno = errno; error = cap_getgrcommon_r(chan, cmd, name, gid, &ggrp, gbuffer, gbufsize, &result); if (error != 0) { errno = error; return (NULL); } errno = serrno; return (result); } struct group * cap_getgrent(cap_channel_t *chan) { return (cap_getgrcommon(chan, "getgrent", NULL, 0)); } struct group * cap_getgrnam(cap_channel_t *chan, const char *name) { return (cap_getgrcommon(chan, "getgrnam", name, 0)); } struct group * cap_getgrgid(cap_channel_t *chan, gid_t gid) { return (cap_getgrcommon(chan, "getgrgid", NULL, gid)); } int cap_getgrent_r(cap_channel_t *chan, struct group *grp, char *buffer, size_t bufsize, struct group **result) { return (cap_getgrcommon_r(chan, "getgrent_r", NULL, 0, grp, buffer, bufsize, result)); } int cap_getgrnam_r(cap_channel_t *chan, const char *name, struct group *grp, char *buffer, size_t bufsize, struct group **result) { return (cap_getgrcommon_r(chan, "getgrnam_r", name, 0, grp, buffer, bufsize, result)); } int cap_getgrgid_r(cap_channel_t *chan, gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result) { return (cap_getgrcommon_r(chan, "getgrgid_r", NULL, gid, grp, buffer, bufsize, result)); } int cap_setgroupent(cap_channel_t *chan, int stayopen) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "setgroupent"); nvlist_add_bool(nvl, "stayopen", stayopen != 0); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (0); if (nvlist_get_number(nvl, "error") != 0) { errno = nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (0); } nvlist_destroy(nvl); return (1); } int cap_setgrent(cap_channel_t *chan) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "setgrent"); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (0); if (nvlist_get_number(nvl, "error") != 0) { errno = nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (0); } nvlist_destroy(nvl); return (1); } void cap_endgrent(cap_channel_t *chan) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "endgrent"); /* Ignore any errors, we have no way to report them. */ nvlist_destroy(cap_xfer_nvlist(chan, nvl)); } int cap_grp_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds) { nvlist_t *limits, *nvl; unsigned int i; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "cmds")) nvlist_free_nvlist(limits, "cmds"); } nvl = nvlist_create(0); for (i = 0; i < ncmds; i++) nvlist_add_null(nvl, cmds[i]); nvlist_move_nvlist(limits, "cmds", nvl); return (cap_limit_set(chan, limits)); } int cap_grp_limit_fields(cap_channel_t *chan, const char * const *fields, size_t nfields) { nvlist_t *limits, *nvl; unsigned int i; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "fields")) nvlist_free_nvlist(limits, "fields"); } nvl = nvlist_create(0); for (i = 0; i < nfields; i++) nvlist_add_null(nvl, fields[i]); nvlist_move_nvlist(limits, "fields", nvl); return (cap_limit_set(chan, limits)); } int cap_grp_limit_groups(cap_channel_t *chan, const char * const *names, size_t nnames, const gid_t *gids, size_t ngids) { nvlist_t *limits, *groups; unsigned int i; char nvlname[64]; int n; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "groups")) nvlist_free_nvlist(limits, "groups"); } groups = nvlist_create(0); for (i = 0; i < ngids; i++) { n = snprintf(nvlname, sizeof(nvlname), "gid%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_number(groups, nvlname, (uint64_t)gids[i]); } for (i = 0; i < nnames; i++) { n = snprintf(nvlname, sizeof(nvlname), "gid%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_string(groups, nvlname, names[i]); } nvlist_move_nvlist(limits, "groups", groups); return (cap_limit_set(chan, limits)); } /* * Service functions. */ static bool grp_allowed_cmd(const nvlist_t *limits, const char *cmd) { if (limits == NULL) return (true); /* * If no limit was set on allowed commands, then all commands * are allowed. */ if (!nvlist_exists_nvlist(limits, "cmds")) return (true); limits = nvlist_get_nvlist(limits, "cmds"); return (nvlist_exists_null(limits, cmd)); } static int grp_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits) { const char *name; void *cookie; int type; cookie = NULL; while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { if (type != NV_TYPE_NULL) return (EINVAL); if (!grp_allowed_cmd(oldlimits, name)) return (ENOTCAPABLE); } return (0); } static bool grp_allowed_group(const nvlist_t *limits, const char *gname, gid_t gid) { const char *name; void *cookie; int type; if (limits == NULL) return (true); /* * If no limit was set on allowed groups, then all groups are allowed. */ if (!nvlist_exists_nvlist(limits, "groups")) return (true); limits = nvlist_get_nvlist(limits, "groups"); cookie = NULL; while ((name = nvlist_next(limits, &type, &cookie)) != NULL) { switch (type) { case NV_TYPE_NUMBER: if (gid != (gid_t)-1 && nvlist_get_number(limits, name) == (uint64_t)gid) { return (true); } break; case NV_TYPE_STRING: if (gname != NULL && strcmp(nvlist_get_string(limits, name), gname) == 0) { return (true); } break; default: abort(); } } return (false); } static int grp_allowed_groups(const nvlist_t *oldlimits, const nvlist_t *newlimits) { const char *name, *gname; void *cookie; gid_t gid; int type; cookie = NULL; while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { switch (type) { case NV_TYPE_NUMBER: gid = (gid_t)nvlist_get_number(newlimits, name); gname = NULL; break; case NV_TYPE_STRING: gid = (gid_t)-1; gname = nvlist_get_string(newlimits, name); break; default: return (EINVAL); } if (!grp_allowed_group(oldlimits, gname, gid)) return (ENOTCAPABLE); } return (0); } static bool grp_allowed_field(const nvlist_t *limits, const char *field) { if (limits == NULL) return (true); /* * If no limit was set on allowed fields, then all fields are allowed. */ if (!nvlist_exists_nvlist(limits, "fields")) return (true); limits = nvlist_get_nvlist(limits, "fields"); return (nvlist_exists_null(limits, field)); } static int grp_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits) { const char *name; void *cookie; int type; cookie = NULL; while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { if (type != NV_TYPE_NULL) return (EINVAL); if (!grp_allowed_field(oldlimits, name)) return (ENOTCAPABLE); } return (0); } static bool grp_pack(const nvlist_t *limits, const struct group *grp, nvlist_t *nvl) { char nvlname[64]; int n; if (grp == NULL) return (true); /* * If either name or GID is allowed, we allow it. */ if (!grp_allowed_group(limits, grp->gr_name, grp->gr_gid)) return (false); if (grp_allowed_field(limits, "gr_name")) nvlist_add_string(nvl, "gr_name", grp->gr_name); else nvlist_add_string(nvl, "gr_name", ""); if (grp_allowed_field(limits, "gr_passwd")) nvlist_add_string(nvl, "gr_passwd", grp->gr_passwd); else nvlist_add_string(nvl, "gr_passwd", ""); if (grp_allowed_field(limits, "gr_gid")) nvlist_add_number(nvl, "gr_gid", (uint64_t)grp->gr_gid); else nvlist_add_number(nvl, "gr_gid", (uint64_t)-1); if (grp_allowed_field(limits, "gr_mem") && grp->gr_mem[0] != NULL) { unsigned int ngroups; for (ngroups = 0; grp->gr_mem[ngroups] != NULL; ngroups++) { n = snprintf(nvlname, sizeof(nvlname), "gr_mem[%u]", ngroups); assert(n > 0 && n < (ssize_t)sizeof(nvlname)); nvlist_add_string(nvl, nvlname, grp->gr_mem[ngroups]); } nvlist_add_number(nvl, "gr_nmem", (uint64_t)ngroups); } return (true); } static int grp_getgrent(const nvlist_t *limits, const nvlist_t *nvlin __unused, nvlist_t *nvlout) { struct group *grp; for (;;) { errno = 0; grp = getgrent(); if (errno != 0) return (errno); if (grp_pack(limits, grp, nvlout)) return (0); } /* NOTREACHED */ } static int grp_getgrnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) { struct group *grp; const char *name; if (!nvlist_exists_string(nvlin, "name")) return (EINVAL); name = nvlist_get_string(nvlin, "name"); assert(name != NULL); errno = 0; grp = getgrnam(name); if (errno != 0) return (errno); (void)grp_pack(limits, grp, nvlout); return (0); } static int grp_getgrgid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) { struct group *grp; gid_t gid; if (!nvlist_exists_number(nvlin, "gid")) return (EINVAL); gid = (gid_t)nvlist_get_number(nvlin, "gid"); errno = 0; grp = getgrgid(gid); if (errno != 0) return (errno); (void)grp_pack(limits, grp, nvlout); return (0); } static int grp_setgroupent(const nvlist_t *limits __unused, const nvlist_t *nvlin, nvlist_t *nvlout __unused) { int stayopen; if (!nvlist_exists_bool(nvlin, "stayopen")) return (EINVAL); stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0; return (setgroupent(stayopen) == 0 ? EFAULT : 0); } static int grp_setgrent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, nvlist_t *nvlout __unused) { setgrent(); return (0); } static int grp_endgrent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, nvlist_t *nvlout __unused) { endgrent(); return (0); } static int grp_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) { const nvlist_t *limits; const char *name; void *cookie; int error, type; if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") && !nvlist_exists_nvlist(newlimits, "cmds")) { return (ENOTCAPABLE); } if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") && !nvlist_exists_nvlist(newlimits, "fields")) { return (ENOTCAPABLE); } if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "groups") && !nvlist_exists_nvlist(newlimits, "groups")) { return (ENOTCAPABLE); } cookie = NULL; while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { if (type != NV_TYPE_NVLIST) return (EINVAL); limits = nvlist_get_nvlist(newlimits, name); if (strcmp(name, "cmds") == 0) error = grp_allowed_cmds(oldlimits, limits); else if (strcmp(name, "fields") == 0) error = grp_allowed_fields(oldlimits, limits); else if (strcmp(name, "groups") == 0) error = grp_allowed_groups(oldlimits, limits); else error = EINVAL; if (error != 0) return (error); } return (0); } static int grp_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout) { int error; if (!grp_allowed_cmd(limits, cmd)) return (ENOTCAPABLE); if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0) error = grp_getgrent(limits, nvlin, nvlout); else if (strcmp(cmd, "getgrnam") == 0 || strcmp(cmd, "getgrnam_r") == 0) error = grp_getgrnam(limits, nvlin, nvlout); else if (strcmp(cmd, "getgrgid") == 0 || strcmp(cmd, "getgrgid_r") == 0) error = grp_getgrgid(limits, nvlin, nvlout); else if (strcmp(cmd, "setgroupent") == 0) error = grp_setgroupent(limits, nvlin, nvlout); else if (strcmp(cmd, "setgrent") == 0) error = grp_setgrent(limits, nvlin, nvlout); else if (strcmp(cmd, "endgrent") == 0) error = grp_endgrent(limits, nvlin, nvlout); else error = EINVAL; return (error); } CREATE_SERVICE("system.grp", grp_limit, grp_command, 0); diff --git a/lib/libcasper/services/cap_grp/cap_grp.h b/lib/libcasper/services/cap_grp/cap_grp.h index a478a91cf760..9be2e7a78dd5 100644 --- a/lib/libcasper/services/cap_grp/cap_grp.h +++ b/lib/libcasper/services/cap_grp/cap_grp.h @@ -1,94 +1,93 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _CAP_GRP_H_ #define _CAP_GRP_H_ #ifdef HAVE_CASPER #define WITH_CASPER #endif #include #ifdef WITH_CASPER __BEGIN_DECLS struct group *cap_getgrent(cap_channel_t *chan); struct group *cap_getgrnam(cap_channel_t *chan, const char *name); struct group *cap_getgrgid(cap_channel_t *chan, gid_t gid); int cap_getgrent_r(cap_channel_t *chan, struct group *grp, char *buffer, size_t bufsize, struct group **result); int cap_getgrnam_r(cap_channel_t *chan, const char *name, struct group *grp, char *buffer, size_t bufsize, struct group **result); int cap_getgrgid_r(cap_channel_t *chan, gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result); int cap_setgroupent(cap_channel_t *chan, int stayopen); int cap_setgrent(cap_channel_t *chan); void cap_endgrent(cap_channel_t *chan); int cap_grp_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds); int cap_grp_limit_fields(cap_channel_t *chan, const char * const *fields, size_t nfields); int cap_grp_limit_groups(cap_channel_t *chan, const char * const *names, size_t nnames, const gid_t *gids, size_t ngids); __END_DECLS #else #define cap_getgrent(chan) getgrent() #define cap_getgrnam(chan, name) getgrnam(name) #define cap_getgrgid(chan, gid) getgrgid(gid) #define cap_setgroupent(chan, stayopen) etgroupent(stayopen) #define endgrent(chan) endgrent() static inline int cap_setgrent(cap_channel_t *chan __unused) { setgrent(); return(0); } #define cap_getgrent_r(chan, grp, buffer, bufsize, result) \ getgrent_r(grp, buffer, bufsize, result) #define cap_getgrnam_r(chan, name, grp, buffer, bufsize, result) \ getgrnam_r(name, grp, buffer, bufsize, result) #define cap_getgrgid_r(chan, gid, grp, buffer, bufsize, result) \ getgrgid_r(gid, grp, buffer, bufsize, result) #define cap_grp_limit_cmds(chan, cmds, ncmds) (0) #define cap_grp_limit_fields(chan, fields, nfields) (0) #define cap_grp_limit_groups(chan, names, nnames, gids, ngids) (0) #endif #endif /* !_CAP_GRP_H_ */ diff --git a/lib/libcasper/services/cap_grp/tests/grp_test.c b/lib/libcasper/services/cap_grp/tests/grp_test.c index 5441c8e9bdca..d7e9cf12954a 100644 --- a/lib/libcasper/services/cap_grp/tests/grp_test.c +++ b/lib/libcasper/services/cap_grp/tests/grp_test.c @@ -1,1557 +1,1556 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok %d %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok %d %s:%u\n", ntest, __FILE__, __LINE__); \ fflush(stdout); \ ntest++; \ } while (0) #define CHECKX(expr) do { \ if ((expr)) { \ printf("ok %d %s:%u\n", ntest, __FILE__, __LINE__); \ } else { \ printf("not ok %d %s:%u\n", ntest, __FILE__, __LINE__); \ exit(1); \ } \ fflush(stdout); \ ntest++; \ } while (0) #define GID_WHEEL 0 #define GID_OPERATOR 5 #define GETGRENT0 0x0001 #define GETGRENT1 0x0002 #define GETGRENT2 0x0004 #define GETGRENT (GETGRENT0 | GETGRENT1 | GETGRENT2) #define GETGRENT_R0 0x0008 #define GETGRENT_R1 0x0010 #define GETGRENT_R2 0x0020 #define GETGRENT_R (GETGRENT_R0 | GETGRENT_R1 | GETGRENT_R2) #define GETGRNAM 0x0040 #define GETGRNAM_R 0x0080 #define GETGRGID 0x0100 #define GETGRGID_R 0x0200 #define SETGRENT 0x0400 static bool group_mem_compare(char **mem0, char **mem1) { int i0, i1; if (mem0 == NULL && mem1 == NULL) return (true); if (mem0 == NULL || mem1 == NULL) return (false); for (i0 = 0; mem0[i0] != NULL; i0++) { for (i1 = 0; mem1[i1] != NULL; i1++) { if (strcmp(mem0[i0], mem1[i1]) == 0) break; } if (mem1[i1] == NULL) return (false); } return (true); } static bool group_compare(const struct group *grp0, const struct group *grp1) { if (grp0 == NULL && grp1 == NULL) return (true); if (grp0 == NULL || grp1 == NULL) return (false); if (strcmp(grp0->gr_name, grp1->gr_name) != 0) return (false); if (grp0->gr_passwd != NULL || grp1->gr_passwd != NULL) { if (grp0->gr_passwd == NULL || grp1->gr_passwd == NULL) return (false); if (strcmp(grp0->gr_passwd, grp1->gr_passwd) != 0) return (false); } if (grp0->gr_gid != grp1->gr_gid) return (false); if (!group_mem_compare(grp0->gr_mem, grp1->gr_mem)) return (false); return (true); } static unsigned int runtest_cmds(cap_channel_t *capgrp) { char bufs[1024], bufc[1024]; unsigned int result; struct group *grps, *grpc; struct group sts, stc; result = 0; (void)setgrent(); if (cap_setgrent(capgrp) == 1) result |= SETGRENT; grps = getgrent(); grpc = cap_getgrent(capgrp); if (group_compare(grps, grpc)) { result |= GETGRENT0; grps = getgrent(); grpc = cap_getgrent(capgrp); if (group_compare(grps, grpc)) result |= GETGRENT1; } getgrent_r(&sts, bufs, sizeof(bufs), &grps); cap_getgrent_r(capgrp, &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) { result |= GETGRENT_R0; getgrent_r(&sts, bufs, sizeof(bufs), &grps); cap_getgrent_r(capgrp, &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) result |= GETGRENT_R1; } (void)setgrent(); if (cap_setgrent(capgrp) == 1) result |= SETGRENT; getgrent_r(&sts, bufs, sizeof(bufs), &grps); cap_getgrent_r(capgrp, &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) result |= GETGRENT_R2; grps = getgrent(); grpc = cap_getgrent(capgrp); if (group_compare(grps, grpc)) result |= GETGRENT2; grps = getgrnam("wheel"); grpc = cap_getgrnam(capgrp, "wheel"); if (group_compare(grps, grpc)) { grps = getgrnam("operator"); grpc = cap_getgrnam(capgrp, "operator"); if (group_compare(grps, grpc)) result |= GETGRNAM; } getgrnam_r("wheel", &sts, bufs, sizeof(bufs), &grps); cap_getgrnam_r(capgrp, "wheel", &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) { getgrnam_r("operator", &sts, bufs, sizeof(bufs), &grps); cap_getgrnam_r(capgrp, "operator", &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) result |= GETGRNAM_R; } grps = getgrgid(GID_WHEEL); grpc = cap_getgrgid(capgrp, GID_WHEEL); if (group_compare(grps, grpc)) { grps = getgrgid(GID_OPERATOR); grpc = cap_getgrgid(capgrp, GID_OPERATOR); if (group_compare(grps, grpc)) result |= GETGRGID; } getgrgid_r(GID_WHEEL, &sts, bufs, sizeof(bufs), &grps); cap_getgrgid_r(capgrp, GID_WHEEL, &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) { getgrgid_r(GID_OPERATOR, &sts, bufs, sizeof(bufs), &grps); cap_getgrgid_r(capgrp, GID_OPERATOR, &stc, bufc, sizeof(bufc), &grpc); if (group_compare(grps, grpc)) result |= GETGRGID_R; } return (result); } static void test_cmds(cap_channel_t *origcapgrp) { cap_channel_t *capgrp; const char *cmds[7], *fields[4], *names[5]; gid_t gids[5]; fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; names[0] = "wheel"; names[1] = "daemon"; names[2] = "kmem"; names[3] = "sys"; names[4] = "operator"; gids[0] = 0; gids[1] = 1; gids[2] = 2; gids[3] = 3; gids[4] = 5; /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == 0); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == 0); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: setgrent * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cap_setgrent(capgrp); cmds[0] = "getgrent"; cmds[1] = "getgrent_r"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "setgrent"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (GETGRENT0 | GETGRENT1 | GETGRENT_R0 | GETGRENT_R1 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: setgrent * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cap_setgrent(capgrp); cmds[0] = "getgrent"; cmds[1] = "getgrent_r"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "setgrent"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (GETGRENT0 | GETGRENT1 | GETGRENT_R0 | GETGRENT_R1 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrent * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent_r"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrent"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT_R2 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrent * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent_r"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrent"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT_R2 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrent_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrent_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT0 | GETGRENT1 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrnam, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrent_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrnam"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrent_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT0 | GETGRENT1 | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrnam * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrnam"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam_r, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrnam * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam_r"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrnam"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM_R | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrnam_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrnam_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, * getgrgid, getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrnam_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrgid"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrnam_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRGID | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrgid * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrgid"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid_r * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrgid * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrgid"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID_R)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: wheel, daemon, kmem, sys, operator * gids: * Disallow: * cmds: getgrgid_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID)); cap_close(capgrp); /* * Allow: * cmds: setgrent, getgrent, getgrent_r, getgrnam, getgrnam_r, * getgrgid * fields: gr_name, gr_passwd, gr_gid, gr_mem * groups: * names: * gids: 0, 1, 2, 3, 5 * Disallow: * cmds: getgrgid_r * fields: * groups: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 6) == 0); cmds[0] = "setgrent"; cmds[1] = "getgrent"; cmds[2] = "getgrent_r"; cmds[3] = "getgrnam"; cmds[4] = "getgrnam_r"; cmds[5] = "getgrgid"; cmds[6] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getgrgid_r"; CHECK(cap_grp_limit_cmds(capgrp, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 5) == 0); CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID)); cap_close(capgrp); } #define GR_NAME 0x01 #define GR_PASSWD 0x02 #define GR_GID 0x04 #define GR_MEM 0x08 static unsigned int group_fields(const struct group *grp) { unsigned int result; result = 0; if (grp->gr_name != NULL && grp->gr_name[0] != '\0') result |= GR_NAME; if (grp->gr_passwd != NULL && grp->gr_passwd[0] != '\0') result |= GR_PASSWD; if (grp->gr_gid != (gid_t)-1) result |= GR_GID; if (grp->gr_mem != NULL && grp->gr_mem[0] != NULL) result |= GR_MEM; return (result); } static bool runtest_fields(cap_channel_t *capgrp, unsigned int expected) { char buf[1024]; struct group *grp; struct group st; (void)cap_setgrent(capgrp); grp = cap_getgrent(capgrp); if (group_fields(grp) != expected) return (false); (void)cap_setgrent(capgrp); cap_getgrent_r(capgrp, &st, buf, sizeof(buf), &grp); if (group_fields(grp) != expected) return (false); grp = cap_getgrnam(capgrp, "wheel"); if (group_fields(grp) != expected) return (false); cap_getgrnam_r(capgrp, "wheel", &st, buf, sizeof(buf), &grp); if (group_fields(grp) != expected) return (false); grp = cap_getgrgid(capgrp, GID_WHEEL); if (group_fields(grp) != expected) return (false); cap_getgrgid_r(capgrp, GID_WHEEL, &st, buf, sizeof(buf), &grp); if (group_fields(grp) != expected) return (false); return (true); } static void test_fields(cap_channel_t *origcapgrp) { cap_channel_t *capgrp; const char *fields[4]; /* No limits. */ CHECK(runtest_fields(origcapgrp, GR_NAME | GR_PASSWD | GR_GID | GR_MEM)); /* * Allow: * fields: gr_name, gr_passwd, gr_gid, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == 0); CHECK(runtest_fields(capgrp, GR_NAME | GR_PASSWD | GR_GID | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_passwd, gr_gid, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_passwd"; fields[1] = "gr_gid"; fields[2] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 3) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_PASSWD | GR_GID | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_gid, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_gid"; fields[2] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 3) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_passwd"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_GID | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_passwd, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 3) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_PASSWD | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_passwd, gr_gid */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 3) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_PASSWD | GR_GID)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_passwd */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_passwd"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_PASSWD)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_gid */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_GID)); cap_close(capgrp); /* * Allow: * fields: gr_name, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_name"; fields[1] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_passwd"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_NAME | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_passwd, gr_gid */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_passwd"; fields[1] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_PASSWD | GR_GID)); cap_close(capgrp); /* * Allow: * fields: gr_passwd, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_passwd"; fields[1] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_gid"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_PASSWD | GR_MEM)); cap_close(capgrp); /* * Allow: * fields: gr_gid, gr_mem */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); fields[0] = "gr_gid"; fields[1] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 2) == 0); fields[0] = "gr_name"; fields[1] = "gr_passwd"; fields[2] = "gr_gid"; fields[3] = "gr_mem"; CHECK(cap_grp_limit_fields(capgrp, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "gr_passwd"; CHECK(cap_grp_limit_fields(capgrp, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(capgrp, GR_GID | GR_MEM)); cap_close(capgrp); } static bool runtest_groups(cap_channel_t *capgrp, const char **names, const gid_t *gids, size_t ngroups) { char buf[1024]; struct group *grp; struct group st; unsigned int i, got; (void)cap_setgrent(capgrp); got = 0; for (;;) { grp = cap_getgrent(capgrp); if (grp == NULL) break; got++; for (i = 0; i < ngroups; i++) { if (strcmp(names[i], grp->gr_name) == 0 && gids[i] == grp->gr_gid) { break; } } if (i == ngroups) return (false); } if (got != ngroups) return (false); (void)cap_setgrent(capgrp); got = 0; for (;;) { cap_getgrent_r(capgrp, &st, buf, sizeof(buf), &grp); if (grp == NULL) break; got++; for (i = 0; i < ngroups; i++) { if (strcmp(names[i], grp->gr_name) == 0 && gids[i] == grp->gr_gid) { break; } } if (i == ngroups) return (false); } if (got != ngroups) return (false); for (i = 0; i < ngroups; i++) { grp = cap_getgrnam(capgrp, names[i]); if (grp == NULL) return (false); } for (i = 0; i < ngroups; i++) { cap_getgrnam_r(capgrp, names[i], &st, buf, sizeof(buf), &grp); if (grp == NULL) return (false); } for (i = 0; i < ngroups; i++) { grp = cap_getgrgid(capgrp, gids[i]); if (grp == NULL) return (false); } for (i = 0; i < ngroups; i++) { cap_getgrgid_r(capgrp, gids[i], &st, buf, sizeof(buf), &grp); if (grp == NULL) return (false); } return (true); } static void test_groups(cap_channel_t *origcapgrp) { cap_channel_t *capgrp; const char *names[5]; gid_t gids[5]; /* * Allow: * groups: * names: wheel, daemon, kmem, sys, tty * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "wheel"; names[1] = "daemon"; names[2] = "kmem"; names[3] = "sys"; names[4] = "tty"; CHECK(cap_grp_limit_groups(capgrp, names, 5, NULL, 0) == 0); gids[0] = 0; gids[1] = 1; gids[2] = 2; gids[3] = 3; gids[4] = 4; CHECK(runtest_groups(capgrp, names, gids, 5)); cap_close(capgrp); /* * Allow: * groups: * names: kmem, sys, tty * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "kmem"; names[1] = "sys"; names[2] = "tty"; CHECK(cap_grp_limit_groups(capgrp, names, 3, NULL, 0) == 0); names[3] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 4, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "kmem"; gids[0] = 2; gids[1] = 3; gids[2] = 4; CHECK(runtest_groups(capgrp, names, gids, 3)); cap_close(capgrp); /* * Allow: * groups: * names: wheel, kmem, tty * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "wheel"; names[1] = "kmem"; names[2] = "tty"; CHECK(cap_grp_limit_groups(capgrp, names, 3, NULL, 0) == 0); names[3] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 4, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "wheel"; gids[0] = 0; gids[1] = 2; gids[2] = 4; CHECK(runtest_groups(capgrp, names, gids, 3)); cap_close(capgrp); /* * Allow: * groups: * names: * gids: 2, 3, 4 */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "kmem"; names[1] = "sys"; names[2] = "tty"; gids[0] = 2; gids[1] = 3; gids[2] = 4; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 3) == 0); gids[3] = 0; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 4) == -1 && errno == ENOTCAPABLE); gids[0] = 0; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 1) == -1 && errno == ENOTCAPABLE); gids[0] = 2; CHECK(runtest_groups(capgrp, names, gids, 3)); cap_close(capgrp); /* * Allow: * groups: * names: * gids: 0, 2, 4 */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "wheel"; names[1] = "kmem"; names[2] = "tty"; gids[0] = 0; gids[1] = 2; gids[2] = 4; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 3) == 0); gids[3] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 4) == -1 && errno == ENOTCAPABLE); gids[0] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 1) == -1 && errno == ENOTCAPABLE); gids[0] = 0; CHECK(runtest_groups(capgrp, names, gids, 3)); cap_close(capgrp); /* * Allow: * groups: * names: kmem * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "kmem"; CHECK(cap_grp_limit_groups(capgrp, names, 1, NULL, 0) == 0); names[1] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 2, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "kmem"; gids[0] = 2; CHECK(runtest_groups(capgrp, names, gids, 1)); cap_close(capgrp); /* * Allow: * groups: * names: wheel, tty * gids: */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "wheel"; names[1] = "tty"; CHECK(cap_grp_limit_groups(capgrp, names, 2, NULL, 0) == 0); names[2] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 3, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; CHECK(cap_grp_limit_groups(capgrp, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "wheel"; gids[0] = 0; gids[1] = 4; CHECK(runtest_groups(capgrp, names, gids, 2)); cap_close(capgrp); /* * Allow: * groups: * names: * gids: 2 */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "kmem"; gids[0] = 2; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 1) == 0); gids[1] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 2) == -1 && errno == ENOTCAPABLE); gids[0] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 1) == -1 && errno == ENOTCAPABLE); gids[0] = 2; CHECK(runtest_groups(capgrp, names, gids, 1)); cap_close(capgrp); /* * Allow: * groups: * names: * gids: 0, 4 */ capgrp = cap_clone(origcapgrp); CHECK(capgrp != NULL); names[0] = "wheel"; names[1] = "tty"; gids[0] = 0; gids[1] = 4; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 2) == 0); gids[2] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 3) == -1 && errno == ENOTCAPABLE); gids[0] = 1; CHECK(cap_grp_limit_groups(capgrp, NULL, 0, gids, 1) == -1 && errno == ENOTCAPABLE); gids[0] = 0; CHECK(runtest_groups(capgrp, names, gids, 2)); cap_close(capgrp); } int main(void) { cap_channel_t *capcas, *capgrp; printf("1..199\n"); fflush(stdout); capcas = cap_init(); CHECKX(capcas != NULL); capgrp = cap_service_open(capcas, "system.grp"); CHECKX(capgrp != NULL); cap_close(capcas); /* No limits. */ CHECK(runtest_cmds(capgrp) == (SETGRENT | GETGRENT | GETGRENT_R | GETGRNAM | GETGRNAM_R | GETGRGID | GETGRGID_R)); test_cmds(capgrp); test_fields(capgrp); test_groups(capgrp); cap_close(capgrp); exit(0); } diff --git a/lib/libcasper/services/cap_pwd/cap_pwd.c b/lib/libcasper/services/cap_pwd/cap_pwd.c index 6bae01601d60..1b5963d8be3d 100644 --- a/lib/libcasper/services/cap_pwd/cap_pwd.c +++ b/lib/libcasper/services/cap_pwd/cap_pwd.c @@ -1,782 +1,781 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "cap_pwd.h" static struct passwd gpwd; static char *gbuffer; static size_t gbufsize; static int passwd_resize(void) { char *buf; if (gbufsize == 0) gbufsize = 1024; else gbufsize *= 2; buf = gbuffer; gbuffer = realloc(buf, gbufsize); if (gbuffer == NULL) { free(buf); gbufsize = 0; return (ENOMEM); } memset(gbuffer, 0, gbufsize); return (0); } static int passwd_unpack_string(const nvlist_t *nvl, const char *fieldname, char **fieldp, char **bufferp, size_t *bufsizep) { const char *str; size_t len; str = nvlist_get_string(nvl, fieldname); len = strlcpy(*bufferp, str, *bufsizep); if (len >= *bufsizep) return (ERANGE); *fieldp = *bufferp; *bufferp += len + 1; *bufsizep -= len + 1; return (0); } static int passwd_unpack(const nvlist_t *nvl, struct passwd *pwd, char *buffer, size_t bufsize) { int error; if (!nvlist_exists_string(nvl, "pw_name")) return (EINVAL); explicit_bzero(pwd, sizeof(*pwd)); error = passwd_unpack_string(nvl, "pw_name", &pwd->pw_name, &buffer, &bufsize); if (error != 0) return (error); pwd->pw_uid = (uid_t)nvlist_get_number(nvl, "pw_uid"); pwd->pw_gid = (gid_t)nvlist_get_number(nvl, "pw_gid"); pwd->pw_change = (time_t)nvlist_get_number(nvl, "pw_change"); error = passwd_unpack_string(nvl, "pw_passwd", &pwd->pw_passwd, &buffer, &bufsize); if (error != 0) return (error); error = passwd_unpack_string(nvl, "pw_class", &pwd->pw_class, &buffer, &bufsize); if (error != 0) return (error); error = passwd_unpack_string(nvl, "pw_gecos", &pwd->pw_gecos, &buffer, &bufsize); if (error != 0) return (error); error = passwd_unpack_string(nvl, "pw_dir", &pwd->pw_dir, &buffer, &bufsize); if (error != 0) return (error); error = passwd_unpack_string(nvl, "pw_shell", &pwd->pw_shell, &buffer, &bufsize); if (error != 0) return (error); pwd->pw_expire = (time_t)nvlist_get_number(nvl, "pw_expire"); pwd->pw_fields = (int)nvlist_get_number(nvl, "pw_fields"); return (0); } static int cap_getpwcommon_r(cap_channel_t *chan, const char *cmd, const char *login, uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { nvlist_t *nvl; bool getpw_r; int error; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", cmd); if (strcmp(cmd, "getpwent") == 0 || strcmp(cmd, "getpwent_r") == 0) { /* Add nothing. */ } else if (strcmp(cmd, "getpwnam") == 0 || strcmp(cmd, "getpwnam_r") == 0) { nvlist_add_string(nvl, "name", login); } else if (strcmp(cmd, "getpwuid") == 0 || strcmp(cmd, "getpwuid_r") == 0) { nvlist_add_number(nvl, "uid", (uint64_t)uid); } else { abort(); } nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) { assert(errno != 0); *result = NULL; return (errno); } error = (int)nvlist_get_number(nvl, "error"); if (error != 0) { nvlist_destroy(nvl); *result = NULL; return (error); } if (!nvlist_exists_string(nvl, "pw_name")) { /* Not found. */ nvlist_destroy(nvl); *result = NULL; return (0); } getpw_r = (strcmp(cmd, "getpwent_r") == 0 || strcmp(cmd, "getpwnam_r") == 0 || strcmp(cmd, "getpwuid_r") == 0); for (;;) { error = passwd_unpack(nvl, pwd, buffer, bufsize); if (getpw_r || error != ERANGE) break; assert(buffer == gbuffer); assert(bufsize == gbufsize); error = passwd_resize(); if (error != 0) break; /* Update pointers after resize. */ buffer = gbuffer; bufsize = gbufsize; } nvlist_destroy(nvl); if (error == 0) *result = pwd; else *result = NULL; return (error); } static struct passwd * cap_getpwcommon(cap_channel_t *chan, const char *cmd, const char *login, uid_t uid) { struct passwd *result; int error, serrno; serrno = errno; error = cap_getpwcommon_r(chan, cmd, login, uid, &gpwd, gbuffer, gbufsize, &result); if (error != 0) { errno = error; return (NULL); } errno = serrno; return (result); } struct passwd * cap_getpwent(cap_channel_t *chan) { return (cap_getpwcommon(chan, "getpwent", NULL, 0)); } struct passwd * cap_getpwnam(cap_channel_t *chan, const char *login) { return (cap_getpwcommon(chan, "getpwnam", login, 0)); } struct passwd * cap_getpwuid(cap_channel_t *chan, uid_t uid) { return (cap_getpwcommon(chan, "getpwuid", NULL, uid)); } int cap_getpwent_r(cap_channel_t *chan, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { return (cap_getpwcommon_r(chan, "getpwent_r", NULL, 0, pwd, buffer, bufsize, result)); } int cap_getpwnam_r(cap_channel_t *chan, const char *name, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { return (cap_getpwcommon_r(chan, "getpwnam_r", name, 0, pwd, buffer, bufsize, result)); } int cap_getpwuid_r(cap_channel_t *chan, uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { return (cap_getpwcommon_r(chan, "getpwuid_r", NULL, uid, pwd, buffer, bufsize, result)); } int cap_setpassent(cap_channel_t *chan, int stayopen) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", "setpassent"); nvlist_add_bool(nvl, "stayopen", stayopen != 0); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (0); if (nvlist_get_number(nvl, "error") != 0) { errno = nvlist_get_number(nvl, "error"); nvlist_destroy(nvl); return (0); } nvlist_destroy(nvl); return (1); } static void cap_set_end_pwent(cap_channel_t *chan, const char *cmd) { nvlist_t *nvl; nvl = nvlist_create(0); nvlist_add_string(nvl, "cmd", cmd); /* Ignore any errors, we have no way to report them. */ nvlist_destroy(cap_xfer_nvlist(chan, nvl)); } void cap_setpwent(cap_channel_t *chan) { cap_set_end_pwent(chan, "setpwent"); } void cap_endpwent(cap_channel_t *chan) { cap_set_end_pwent(chan, "endpwent"); } int cap_pwd_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds) { nvlist_t *limits, *nvl; unsigned int i; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "cmds")) nvlist_free_nvlist(limits, "cmds"); } nvl = nvlist_create(0); for (i = 0; i < ncmds; i++) nvlist_add_null(nvl, cmds[i]); nvlist_move_nvlist(limits, "cmds", nvl); return (cap_limit_set(chan, limits)); } int cap_pwd_limit_fields(cap_channel_t *chan, const char * const *fields, size_t nfields) { nvlist_t *limits, *nvl; unsigned int i; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "fields")) nvlist_free_nvlist(limits, "fields"); } nvl = nvlist_create(0); for (i = 0; i < nfields; i++) nvlist_add_null(nvl, fields[i]); nvlist_move_nvlist(limits, "fields", nvl); return (cap_limit_set(chan, limits)); } int cap_pwd_limit_users(cap_channel_t *chan, const char * const *names, size_t nnames, uid_t *uids, size_t nuids) { nvlist_t *limits, *users; char nvlname[64]; unsigned int i; int n; if (cap_limit_get(chan, &limits) < 0) return (-1); if (limits == NULL) { limits = nvlist_create(0); } else { if (nvlist_exists_nvlist(limits, "users")) nvlist_free_nvlist(limits, "users"); } users = nvlist_create(0); for (i = 0; i < nuids; i++) { n = snprintf(nvlname, sizeof(nvlname), "uid%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_number(users, nvlname, (uint64_t)uids[i]); } for (i = 0; i < nnames; i++) { n = snprintf(nvlname, sizeof(nvlname), "name%u", i); assert(n > 0 && n < (int)sizeof(nvlname)); nvlist_add_string(users, nvlname, names[i]); } nvlist_move_nvlist(limits, "users", users); return (cap_limit_set(chan, limits)); } /* * Service functions. */ static bool pwd_allowed_cmd(const nvlist_t *limits, const char *cmd) { if (limits == NULL) return (true); /* * If no limit was set on allowed commands, then all commands * are allowed. */ if (!nvlist_exists_nvlist(limits, "cmds")) return (true); limits = nvlist_get_nvlist(limits, "cmds"); return (nvlist_exists_null(limits, cmd)); } static int pwd_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits) { const char *name; void *cookie; int type; cookie = NULL; while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { if (type != NV_TYPE_NULL) return (EINVAL); if (!pwd_allowed_cmd(oldlimits, name)) return (ENOTCAPABLE); } return (0); } static bool pwd_allowed_user(const nvlist_t *limits, const char *uname, uid_t uid) { const char *name; void *cookie; int type; if (limits == NULL) return (true); /* * If no limit was set on allowed users, then all users are allowed. */ if (!nvlist_exists_nvlist(limits, "users")) return (true); limits = nvlist_get_nvlist(limits, "users"); cookie = NULL; while ((name = nvlist_next(limits, &type, &cookie)) != NULL) { switch (type) { case NV_TYPE_NUMBER: if (uid != (uid_t)-1 && nvlist_get_number(limits, name) == (uint64_t)uid) { return (true); } break; case NV_TYPE_STRING: if (uname != NULL && strcmp(nvlist_get_string(limits, name), uname) == 0) { return (true); } break; default: abort(); } } return (false); } static int pwd_allowed_users(const nvlist_t *oldlimits, const nvlist_t *newlimits) { const char *name, *uname; void *cookie; uid_t uid; int type; cookie = NULL; while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { switch (type) { case NV_TYPE_NUMBER: uid = (uid_t)nvlist_get_number(newlimits, name); uname = NULL; break; case NV_TYPE_STRING: uid = (uid_t)-1; uname = nvlist_get_string(newlimits, name); break; default: return (EINVAL); } if (!pwd_allowed_user(oldlimits, uname, uid)) return (ENOTCAPABLE); } return (0); } static bool pwd_allowed_field(const nvlist_t *limits, const char *field) { if (limits == NULL) return (true); /* * If no limit was set on allowed fields, then all fields are allowed. */ if (!nvlist_exists_nvlist(limits, "fields")) return (true); limits = nvlist_get_nvlist(limits, "fields"); return (nvlist_exists_null(limits, field)); } static int pwd_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits) { const char *name; void *cookie; int type; cookie = NULL; while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { if (type != NV_TYPE_NULL) return (EINVAL); if (!pwd_allowed_field(oldlimits, name)) return (ENOTCAPABLE); } return (0); } static bool pwd_pack(const nvlist_t *limits, const struct passwd *pwd, nvlist_t *nvl) { int fields; if (pwd == NULL) return (true); /* * If either name or UID is allowed, we allow it. */ if (!pwd_allowed_user(limits, pwd->pw_name, pwd->pw_uid)) return (false); fields = pwd->pw_fields; if (pwd_allowed_field(limits, "pw_name")) { nvlist_add_string(nvl, "pw_name", pwd->pw_name); } else { nvlist_add_string(nvl, "pw_name", ""); fields &= ~_PWF_NAME; } if (pwd_allowed_field(limits, "pw_uid")) { nvlist_add_number(nvl, "pw_uid", (uint64_t)pwd->pw_uid); } else { nvlist_add_number(nvl, "pw_uid", (uint64_t)-1); fields &= ~_PWF_UID; } if (pwd_allowed_field(limits, "pw_gid")) { nvlist_add_number(nvl, "pw_gid", (uint64_t)pwd->pw_gid); } else { nvlist_add_number(nvl, "pw_gid", (uint64_t)-1); fields &= ~_PWF_GID; } if (pwd_allowed_field(limits, "pw_change")) { nvlist_add_number(nvl, "pw_change", (uint64_t)pwd->pw_change); } else { nvlist_add_number(nvl, "pw_change", (uint64_t)0); fields &= ~_PWF_CHANGE; } if (pwd_allowed_field(limits, "pw_passwd")) { nvlist_add_string(nvl, "pw_passwd", pwd->pw_passwd); } else { nvlist_add_string(nvl, "pw_passwd", ""); fields &= ~_PWF_PASSWD; } if (pwd_allowed_field(limits, "pw_class")) { nvlist_add_string(nvl, "pw_class", pwd->pw_class); } else { nvlist_add_string(nvl, "pw_class", ""); fields &= ~_PWF_CLASS; } if (pwd_allowed_field(limits, "pw_gecos")) { nvlist_add_string(nvl, "pw_gecos", pwd->pw_gecos); } else { nvlist_add_string(nvl, "pw_gecos", ""); fields &= ~_PWF_GECOS; } if (pwd_allowed_field(limits, "pw_dir")) { nvlist_add_string(nvl, "pw_dir", pwd->pw_dir); } else { nvlist_add_string(nvl, "pw_dir", ""); fields &= ~_PWF_DIR; } if (pwd_allowed_field(limits, "pw_shell")) { nvlist_add_string(nvl, "pw_shell", pwd->pw_shell); } else { nvlist_add_string(nvl, "pw_shell", ""); fields &= ~_PWF_SHELL; } if (pwd_allowed_field(limits, "pw_expire")) { nvlist_add_number(nvl, "pw_expire", (uint64_t)pwd->pw_expire); } else { nvlist_add_number(nvl, "pw_expire", (uint64_t)0); fields &= ~_PWF_EXPIRE; } nvlist_add_number(nvl, "pw_fields", (uint64_t)fields); return (true); } static int pwd_getpwent(const nvlist_t *limits, const nvlist_t *nvlin __unused, nvlist_t *nvlout) { struct passwd *pwd; for (;;) { errno = 0; pwd = getpwent(); if (errno != 0) return (errno); if (pwd_pack(limits, pwd, nvlout)) return (0); } /* NOTREACHED */ } static int pwd_getpwnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) { struct passwd *pwd; const char *name; if (!nvlist_exists_string(nvlin, "name")) return (EINVAL); name = nvlist_get_string(nvlin, "name"); assert(name != NULL); errno = 0; pwd = getpwnam(name); if (errno != 0) return (errno); (void)pwd_pack(limits, pwd, nvlout); return (0); } static int pwd_getpwuid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) { struct passwd *pwd; uid_t uid; if (!nvlist_exists_number(nvlin, "uid")) return (EINVAL); uid = (uid_t)nvlist_get_number(nvlin, "uid"); errno = 0; pwd = getpwuid(uid); if (errno != 0) return (errno); (void)pwd_pack(limits, pwd, nvlout); return (0); } static int pwd_setpassent(const nvlist_t *limits __unused, const nvlist_t *nvlin, nvlist_t *nvlout __unused) { int stayopen; if (!nvlist_exists_bool(nvlin, "stayopen")) return (EINVAL); stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0; return (setpassent(stayopen) == 0 ? EFAULT : 0); } static int pwd_setpwent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, nvlist_t *nvlout __unused) { setpwent(); return (0); } static int pwd_endpwent(const nvlist_t *limits __unused, const nvlist_t *nvlin __unused, nvlist_t *nvlout __unused) { endpwent(); return (0); } static int pwd_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) { const nvlist_t *limits; const char *name; void *cookie; int error, type; if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") && !nvlist_exists_nvlist(newlimits, "cmds")) { return (ENOTCAPABLE); } if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") && !nvlist_exists_nvlist(newlimits, "fields")) { return (ENOTCAPABLE); } if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "users") && !nvlist_exists_nvlist(newlimits, "users")) { return (ENOTCAPABLE); } cookie = NULL; while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { if (type != NV_TYPE_NVLIST) return (EINVAL); limits = nvlist_get_nvlist(newlimits, name); if (strcmp(name, "cmds") == 0) error = pwd_allowed_cmds(oldlimits, limits); else if (strcmp(name, "fields") == 0) error = pwd_allowed_fields(oldlimits, limits); else if (strcmp(name, "users") == 0) error = pwd_allowed_users(oldlimits, limits); else error = EINVAL; if (error != 0) return (error); } return (0); } static int pwd_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout) { int error; if (!pwd_allowed_cmd(limits, cmd)) return (ENOTCAPABLE); if (strcmp(cmd, "getpwent") == 0 || strcmp(cmd, "getpwent_r") == 0) error = pwd_getpwent(limits, nvlin, nvlout); else if (strcmp(cmd, "getpwnam") == 0 || strcmp(cmd, "getpwnam_r") == 0) error = pwd_getpwnam(limits, nvlin, nvlout); else if (strcmp(cmd, "getpwuid") == 0 || strcmp(cmd, "getpwuid_r") == 0) error = pwd_getpwuid(limits, nvlin, nvlout); else if (strcmp(cmd, "setpassent") == 0) error = pwd_setpassent(limits, nvlin, nvlout); else if (strcmp(cmd, "setpwent") == 0) error = pwd_setpwent(limits, nvlin, nvlout); else if (strcmp(cmd, "endpwent") == 0) error = pwd_endpwent(limits, nvlin, nvlout); else error = EINVAL; return (error); } CREATE_SERVICE("system.pwd", pwd_limit, pwd_command, 0); diff --git a/lib/libcasper/services/cap_pwd/cap_pwd.h b/lib/libcasper/services/cap_pwd/cap_pwd.h index 35e0e177bc62..496beea26392 100644 --- a/lib/libcasper/services/cap_pwd/cap_pwd.h +++ b/lib/libcasper/services/cap_pwd/cap_pwd.h @@ -1,160 +1,159 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _CAP_PWD_H_ #define _CAP_PWD_H_ #ifdef HAVE_CASPER #define WITH_CASPER #endif #include #ifdef WITH_CASPER __BEGIN_DECLS struct passwd *cap_getpwent(cap_channel_t *chan); struct passwd *cap_getpwnam(cap_channel_t *chan, const char *login); struct passwd *cap_getpwuid(cap_channel_t *chan, uid_t uid); int cap_getpwent_r(cap_channel_t *chan, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result); int cap_getpwnam_r(cap_channel_t *chan, const char *name, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result); int cap_getpwuid_r(cap_channel_t *chan, uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result); int cap_setpassent(cap_channel_t *chan, int stayopen); void cap_setpwent(cap_channel_t *chan); void cap_endpwent(cap_channel_t *chan); int cap_pwd_limit_cmds(cap_channel_t *chan, const char * const *cmds, size_t ncmds); int cap_pwd_limit_fields(cap_channel_t *chan, const char * const *fields, size_t nfields); int cap_pwd_limit_users(cap_channel_t *chan, const char * const *names, size_t nnames, uid_t *uids, size_t nuids); __END_DECLS #else static inline struct passwd * cap_getpwent(cap_channel_t *chan __unused) { return (getpwent()); } static inline struct passwd * cap_getpwnam(cap_channel_t *chan __unused, const char *login) { return (getpwnam(login)); } static inline struct passwd * cap_getpwuid(cap_channel_t *chan __unused, uid_t uid) { return (getpwuid(uid)); } static inline int cap_getpwent_r(cap_channel_t *chan __unused, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { return (getpwent_r(pwd, buffer, bufsize, result)); } static inline int cap_getpwnam_r(cap_channel_t *chan __unused, const char *name, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { return (getpwnam_r(name, pwd, buffer, bufsize, result)); } static inline int cap_getpwuid_r(cap_channel_t *chan __unused, uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) { return (getpwuid_r(uid, pwd, buffer, bufsize, result)); } static inline int cap_setpassent(cap_channel_t *chan __unused, int stayopen) { return (setpassent(stayopen)); } static inline void cap_setpwent(cap_channel_t *chan __unused) { return (setpwent()); } static inline void cap_endpwent(cap_channel_t *chan __unused) { return (endpwent()); } static inline int cap_pwd_limit_cmds(cap_channel_t *chan __unused, const char * const *cmds __unused, size_t ncmds __unused) { return (0); } static inline int cap_pwd_limit_fields(cap_channel_t *chan __unused, const char * const *fields __unused, size_t nfields __unused) { return (0); } static inline int cap_pwd_limit_users(cap_channel_t *chan __unused, const char * const *names __unused, size_t nnames __unused, uid_t *uids __unused, size_t nuids __unused) { return (0); } #endif #endif /* !_CAP_PWD_H_ */ diff --git a/lib/libcasper/services/cap_pwd/tests/pwd_test.c b/lib/libcasper/services/cap_pwd/tests/pwd_test.c index ff796005e298..68bb00650350 100644 --- a/lib/libcasper/services/cap_pwd/tests/pwd_test.c +++ b/lib/libcasper/services/cap_pwd/tests/pwd_test.c @@ -1,1539 +1,1538 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ fflush(stdout); \ ntest++; \ } while (0) #define CHECKX(expr) do { \ if ((expr)) { \ printf("ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ } else { \ printf("not ok %d # %s:%u\n", ntest, __FILE__, __LINE__); \ exit(1); \ } \ fflush(stdout); \ ntest++; \ } while (0) #define UID_ROOT 0 #define UID_OPERATOR 2 #define GETPWENT0 0x0001 #define GETPWENT1 0x0002 #define GETPWENT2 0x0004 #define GETPWENT (GETPWENT0 | GETPWENT1 | GETPWENT2) #define GETPWENT_R0 0x0008 #define GETPWENT_R1 0x0010 #define GETPWENT_R2 0x0020 #define GETPWENT_R (GETPWENT_R0 | GETPWENT_R1 | GETPWENT_R2) #define GETPWNAM 0x0040 #define GETPWNAM_R 0x0080 #define GETPWUID 0x0100 #define GETPWUID_R 0x0200 static bool passwd_compare(const struct passwd *pwd0, const struct passwd *pwd1) { if (pwd0 == NULL && pwd1 == NULL) return (true); if (pwd0 == NULL || pwd1 == NULL) return (false); if (strcmp(pwd0->pw_name, pwd1->pw_name) != 0) return (false); if (pwd0->pw_passwd != NULL || pwd1->pw_passwd != NULL) { if (pwd0->pw_passwd == NULL || pwd1->pw_passwd == NULL) return (false); if (strcmp(pwd0->pw_passwd, pwd1->pw_passwd) != 0) return (false); } if (pwd0->pw_uid != pwd1->pw_uid) return (false); if (pwd0->pw_gid != pwd1->pw_gid) return (false); if (pwd0->pw_change != pwd1->pw_change) return (false); if (pwd0->pw_class != NULL || pwd1->pw_class != NULL) { if (pwd0->pw_class == NULL || pwd1->pw_class == NULL) return (false); if (strcmp(pwd0->pw_class, pwd1->pw_class) != 0) return (false); } if (pwd0->pw_gecos != NULL || pwd1->pw_gecos != NULL) { if (pwd0->pw_gecos == NULL || pwd1->pw_gecos == NULL) return (false); if (strcmp(pwd0->pw_gecos, pwd1->pw_gecos) != 0) return (false); } if (pwd0->pw_dir != NULL || pwd1->pw_dir != NULL) { if (pwd0->pw_dir == NULL || pwd1->pw_dir == NULL) return (false); if (strcmp(pwd0->pw_dir, pwd1->pw_dir) != 0) return (false); } if (pwd0->pw_shell != NULL || pwd1->pw_shell != NULL) { if (pwd0->pw_shell == NULL || pwd1->pw_shell == NULL) return (false); if (strcmp(pwd0->pw_shell, pwd1->pw_shell) != 0) return (false); } if (pwd0->pw_expire != pwd1->pw_expire) return (false); if (pwd0->pw_fields != pwd1->pw_fields) return (false); return (true); } static unsigned int runtest_cmds(cap_channel_t *cappwd) { char bufs[1024], bufc[1024]; unsigned int result; struct passwd *pwds, *pwdc; struct passwd sts, stc; result = 0; setpwent(); cap_setpwent(cappwd); pwds = getpwent(); pwdc = cap_getpwent(cappwd); if (passwd_compare(pwds, pwdc)) { result |= GETPWENT0; pwds = getpwent(); pwdc = cap_getpwent(cappwd); if (passwd_compare(pwds, pwdc)) result |= GETPWENT1; } getpwent_r(&sts, bufs, sizeof(bufs), &pwds); cap_getpwent_r(cappwd, &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) { result |= GETPWENT_R0; getpwent_r(&sts, bufs, sizeof(bufs), &pwds); cap_getpwent_r(cappwd, &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) result |= GETPWENT_R1; } setpwent(); cap_setpwent(cappwd); getpwent_r(&sts, bufs, sizeof(bufs), &pwds); cap_getpwent_r(cappwd, &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) result |= GETPWENT_R2; pwds = getpwent(); pwdc = cap_getpwent(cappwd); if (passwd_compare(pwds, pwdc)) result |= GETPWENT2; pwds = getpwnam("root"); pwdc = cap_getpwnam(cappwd, "root"); if (passwd_compare(pwds, pwdc)) { pwds = getpwnam("operator"); pwdc = cap_getpwnam(cappwd, "operator"); if (passwd_compare(pwds, pwdc)) result |= GETPWNAM; } getpwnam_r("root", &sts, bufs, sizeof(bufs), &pwds); cap_getpwnam_r(cappwd, "root", &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) { getpwnam_r("operator", &sts, bufs, sizeof(bufs), &pwds); cap_getpwnam_r(cappwd, "operator", &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) result |= GETPWNAM_R; } pwds = getpwuid(UID_ROOT); pwdc = cap_getpwuid(cappwd, UID_ROOT); if (passwd_compare(pwds, pwdc)) { pwds = getpwuid(UID_OPERATOR); pwdc = cap_getpwuid(cappwd, UID_OPERATOR); if (passwd_compare(pwds, pwdc)) result |= GETPWUID; } getpwuid_r(UID_ROOT, &sts, bufs, sizeof(bufs), &pwds); cap_getpwuid_r(cappwd, UID_ROOT, &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) { getpwuid_r(UID_OPERATOR, &sts, bufs, sizeof(bufs), &pwds); cap_getpwuid_r(cappwd, UID_OPERATOR, &stc, bufc, sizeof(bufc), &pwdc); if (passwd_compare(pwds, pwdc)) result |= GETPWUID_R; } return (result); } static void test_cmds(cap_channel_t *origcappwd) { cap_channel_t *cappwd; const char *cmds[7], *fields[10], *names[6]; uid_t uids[5]; fields[0] = "pw_name"; fields[1] = "pw_passwd"; fields[2] = "pw_uid"; fields[3] = "pw_gid"; fields[4] = "pw_change"; fields[5] = "pw_class"; fields[6] = "pw_gecos"; fields[7] = "pw_dir"; fields[8] = "pw_shell"; fields[9] = "pw_expire"; names[0] = "root"; names[1] = "toor"; names[2] = "daemon"; names[3] = "operator"; names[4] = "bin"; names[5] = "kmem"; uids[0] = 0; uids[1] = 1; uids[2] = 2; uids[3] = 3; uids[4] = 5; /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == 0); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == 0); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: setpwent * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cap_setpwent(cappwd); cmds[0] = "getpwent"; cmds[1] = "getpwent_r"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "setpwent"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 | GETPWENT_R0 | GETPWENT_R1 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: setpwent * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cap_setpwent(cappwd); cmds[0] = "getpwent"; cmds[1] = "getpwent_r"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "setpwent"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 | GETPWENT_R0 | GETPWENT_R1 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwent * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent_r"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwent"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT_R2 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwent * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent_r"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwent"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT_R2 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwent_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwent_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwnam, getpwnam_r, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwent_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwnam"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwent_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT0 | GETPWENT1 | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam_r, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwnam * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwnam"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam_r, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwnam * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam_r"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwnam"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM_R | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, * getpwuid, getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwnam_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwnam_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, * getpwuid, getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwnam_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwuid"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwnam_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWUID | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid_r * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwuid * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwuid"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid_r * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwuid * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwuid"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID_R)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid * users: * names: root, toor, daemon, operator, bin, kmem * uids: * Disallow: * cmds: getpwuid_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID)); cap_close(cappwd); /* * Allow: * cmds: setpwent, getpwent, getpwent_r, getpwnam, getpwnam_r, * getpwuid * users: * names: * uids: 0, 1, 2, 3, 5 * Disallow: * cmds: getpwuid_r * users: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 6) == 0); cmds[0] = "setpwent"; cmds[1] = "getpwent"; cmds[2] = "getpwent_r"; cmds[3] = "getpwnam"; cmds[4] = "getpwnam_r"; cmds[5] = "getpwuid"; cmds[6] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 7) == -1 && errno == ENOTCAPABLE); cmds[0] = "getpwuid_r"; CHECK(cap_pwd_limit_cmds(cappwd, cmds, 1) == -1 && errno == ENOTCAPABLE); CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 5) == 0); CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID)); cap_close(cappwd); } #define PW_NAME _PWF_NAME #define PW_PASSWD _PWF_PASSWD #define PW_UID _PWF_UID #define PW_GID _PWF_GID #define PW_CHANGE _PWF_CHANGE #define PW_CLASS _PWF_CLASS #define PW_GECOS _PWF_GECOS #define PW_DIR _PWF_DIR #define PW_SHELL _PWF_SHELL #define PW_EXPIRE _PWF_EXPIRE static unsigned int passwd_fields(const struct passwd *pwd) { unsigned int result; result = 0; if (pwd->pw_name != NULL && pwd->pw_name[0] != '\0') result |= PW_NAME; // else // printf("No pw_name\n"); if (pwd->pw_passwd != NULL && pwd->pw_passwd[0] != '\0') result |= PW_PASSWD; else if ((pwd->pw_fields & _PWF_PASSWD) != 0) result |= PW_PASSWD; // else // printf("No pw_passwd\n"); if (pwd->pw_uid != (uid_t)-1) result |= PW_UID; // else // printf("No pw_uid\n"); if (pwd->pw_gid != (gid_t)-1) result |= PW_GID; // else // printf("No pw_gid\n"); if (pwd->pw_change != 0 || (pwd->pw_fields & _PWF_CHANGE) != 0) result |= PW_CHANGE; // else // printf("No pw_change\n"); if (pwd->pw_class != NULL && pwd->pw_class[0] != '\0') result |= PW_CLASS; else if ((pwd->pw_fields & _PWF_CLASS) != 0) result |= PW_CLASS; // else // printf("No pw_class\n"); if (pwd->pw_gecos != NULL && pwd->pw_gecos[0] != '\0') result |= PW_GECOS; else if ((pwd->pw_fields & _PWF_GECOS) != 0) result |= PW_GECOS; // else // printf("No pw_gecos\n"); if (pwd->pw_dir != NULL && pwd->pw_dir[0] != '\0') result |= PW_DIR; else if ((pwd->pw_fields & _PWF_DIR) != 0) result |= PW_DIR; // else // printf("No pw_dir\n"); if (pwd->pw_shell != NULL && pwd->pw_shell[0] != '\0') result |= PW_SHELL; else if ((pwd->pw_fields & _PWF_SHELL) != 0) result |= PW_SHELL; // else // printf("No pw_shell\n"); if (pwd->pw_expire != 0 || (pwd->pw_fields & _PWF_EXPIRE) != 0) result |= PW_EXPIRE; // else // printf("No pw_expire\n"); if (false && pwd->pw_fields != (int)result) { printf("fields=0x%x != result=0x%x\n", (const unsigned int)pwd->pw_fields, result); printf(" fields result\n"); printf("PW_NAME %d %d\n", (pwd->pw_fields & PW_NAME) != 0, (result & PW_NAME) != 0); printf("PW_PASSWD %d %d\n", (pwd->pw_fields & PW_PASSWD) != 0, (result & PW_PASSWD) != 0); printf("PW_UID %d %d\n", (pwd->pw_fields & PW_UID) != 0, (result & PW_UID) != 0); printf("PW_GID %d %d\n", (pwd->pw_fields & PW_GID) != 0, (result & PW_GID) != 0); printf("PW_CHANGE %d %d\n", (pwd->pw_fields & PW_CHANGE) != 0, (result & PW_CHANGE) != 0); printf("PW_CLASS %d %d\n", (pwd->pw_fields & PW_CLASS) != 0, (result & PW_CLASS) != 0); printf("PW_GECOS %d %d\n", (pwd->pw_fields & PW_GECOS) != 0, (result & PW_GECOS) != 0); printf("PW_DIR %d %d\n", (pwd->pw_fields & PW_DIR) != 0, (result & PW_DIR) != 0); printf("PW_SHELL %d %d\n", (pwd->pw_fields & PW_SHELL) != 0, (result & PW_SHELL) != 0); printf("PW_EXPIRE %d %d\n", (pwd->pw_fields & PW_EXPIRE) != 0, (result & PW_EXPIRE) != 0); } //printf("result=0x%x\n", result); return (result); } static bool runtest_fields(cap_channel_t *cappwd, unsigned int expected) { char buf[1024]; struct passwd *pwd; struct passwd st; //printf("expected=0x%x\n", expected); cap_setpwent(cappwd); pwd = cap_getpwent(cappwd); if ((passwd_fields(pwd) & ~expected) != 0) return (false); cap_setpwent(cappwd); cap_getpwent_r(cappwd, &st, buf, sizeof(buf), &pwd); if ((passwd_fields(pwd) & ~expected) != 0) return (false); pwd = cap_getpwnam(cappwd, "root"); if ((passwd_fields(pwd) & ~expected) != 0) return (false); cap_getpwnam_r(cappwd, "root", &st, buf, sizeof(buf), &pwd); if ((passwd_fields(pwd) & ~expected) != 0) return (false); pwd = cap_getpwuid(cappwd, UID_ROOT); if ((passwd_fields(pwd) & ~expected) != 0) return (false); cap_getpwuid_r(cappwd, UID_ROOT, &st, buf, sizeof(buf), &pwd); if ((passwd_fields(pwd) & ~expected) != 0) return (false); return (true); } static void test_fields(cap_channel_t *origcappwd) { cap_channel_t *cappwd; const char *fields[10]; /* No limits. */ CHECK(runtest_fields(origcappwd, PW_NAME | PW_PASSWD | PW_UID | PW_GID | PW_CHANGE | PW_CLASS | PW_GECOS | PW_DIR | PW_SHELL | PW_EXPIRE)); /* * Allow: * fields: pw_name, pw_passwd, pw_uid, pw_gid, pw_change, pw_class, * pw_gecos, pw_dir, pw_shell, pw_expire */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_name"; fields[1] = "pw_passwd"; fields[2] = "pw_uid"; fields[3] = "pw_gid"; fields[4] = "pw_change"; fields[5] = "pw_class"; fields[6] = "pw_gecos"; fields[7] = "pw_dir"; fields[8] = "pw_shell"; fields[9] = "pw_expire"; CHECK(cap_pwd_limit_fields(cappwd, fields, 10) == 0); CHECK(runtest_fields(origcappwd, PW_NAME | PW_PASSWD | PW_UID | PW_GID | PW_CHANGE | PW_CLASS | PW_GECOS | PW_DIR | PW_SHELL | PW_EXPIRE)); cap_close(cappwd); /* * Allow: * fields: pw_name, pw_passwd, pw_uid, pw_gid, pw_change */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_name"; fields[1] = "pw_passwd"; fields[2] = "pw_uid"; fields[3] = "pw_gid"; fields[4] = "pw_change"; CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0); fields[5] = "pw_class"; CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_class"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_NAME | PW_PASSWD | PW_UID | PW_GID | PW_CHANGE)); cap_close(cappwd); /* * Allow: * fields: pw_class, pw_gecos, pw_dir, pw_shell, pw_expire */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_class"; fields[1] = "pw_gecos"; fields[2] = "pw_dir"; fields[3] = "pw_shell"; fields[4] = "pw_expire"; CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0); fields[5] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_CLASS | PW_GECOS | PW_DIR | PW_SHELL | PW_EXPIRE)); cap_close(cappwd); /* * Allow: * fields: pw_name, pw_uid, pw_change, pw_gecos, pw_shell */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_name"; fields[1] = "pw_uid"; fields[2] = "pw_change"; fields[3] = "pw_gecos"; fields[4] = "pw_shell"; CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0); fields[5] = "pw_class"; CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_class"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_NAME | PW_UID | PW_CHANGE | PW_GECOS | PW_SHELL)); cap_close(cappwd); /* * Allow: * fields: pw_passwd, pw_gid, pw_class, pw_dir, pw_expire */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_passwd"; fields[1] = "pw_gid"; fields[2] = "pw_class"; fields[3] = "pw_dir"; fields[4] = "pw_expire"; CHECK(cap_pwd_limit_fields(cappwd, fields, 5) == 0); fields[5] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 6) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_PASSWD | PW_GID | PW_CLASS | PW_DIR | PW_EXPIRE)); cap_close(cappwd); /* * Allow: * fields: pw_uid, pw_class, pw_shell */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_uid"; fields[1] = "pw_class"; fields[2] = "pw_shell"; CHECK(cap_pwd_limit_fields(cappwd, fields, 3) == 0); fields[3] = "pw_change"; CHECK(cap_pwd_limit_fields(cappwd, fields, 4) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_change"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_UID | PW_CLASS | PW_SHELL)); cap_close(cappwd); /* * Allow: * fields: pw_change */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); fields[0] = "pw_change"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == 0); fields[1] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 2) == -1 && errno == ENOTCAPABLE); fields[0] = "pw_uid"; CHECK(cap_pwd_limit_fields(cappwd, fields, 1) == -1 && errno == ENOTCAPABLE); CHECK(runtest_fields(cappwd, PW_CHANGE)); cap_close(cappwd); } static bool runtest_users(cap_channel_t *cappwd, const char **names, const uid_t *uids, size_t nusers) { char buf[1024]; struct passwd *pwd; struct passwd st; unsigned int i, got; cap_setpwent(cappwd); got = 0; for (;;) { pwd = cap_getpwent(cappwd); if (pwd == NULL) break; got++; for (i = 0; i < nusers; i++) { if (strcmp(names[i], pwd->pw_name) == 0 && uids[i] == pwd->pw_uid) { break; } } if (i == nusers) return (false); } if (got != nusers) return (false); cap_setpwent(cappwd); got = 0; for (;;) { cap_getpwent_r(cappwd, &st, buf, sizeof(buf), &pwd); if (pwd == NULL) break; got++; for (i = 0; i < nusers; i++) { if (strcmp(names[i], pwd->pw_name) == 0 && uids[i] == pwd->pw_uid) { break; } } if (i == nusers) return (false); } if (got != nusers) return (false); for (i = 0; i < nusers; i++) { pwd = cap_getpwnam(cappwd, names[i]); if (pwd == NULL) return (false); } for (i = 0; i < nusers; i++) { cap_getpwnam_r(cappwd, names[i], &st, buf, sizeof(buf), &pwd); if (pwd == NULL) return (false); } for (i = 0; i < nusers; i++) { pwd = cap_getpwuid(cappwd, uids[i]); if (pwd == NULL) return (false); } for (i = 0; i < nusers; i++) { cap_getpwuid_r(cappwd, uids[i], &st, buf, sizeof(buf), &pwd); if (pwd == NULL) return (false); } return (true); } static void test_users(cap_channel_t *origcappwd) { cap_channel_t *cappwd; const char *names[6]; uid_t uids[6]; /* * Allow: * users: * names: root, toor, daemon, operator, bin, tty * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "root"; names[1] = "toor"; names[2] = "daemon"; names[3] = "operator"; names[4] = "bin"; names[5] = "tty"; CHECK(cap_pwd_limit_users(cappwd, names, 6, NULL, 0) == 0); uids[0] = 0; uids[1] = 0; uids[2] = 1; uids[3] = 2; uids[4] = 3; uids[5] = 4; CHECK(runtest_users(cappwd, names, uids, 6)); cap_close(cappwd); /* * Allow: * users: * names: daemon, operator, bin * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "operator"; names[2] = "bin"; CHECK(cap_pwd_limit_users(cappwd, names, 3, NULL, 0) == 0); names[3] = "tty"; CHECK(cap_pwd_limit_users(cappwd, names, 4, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "tty"; CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; uids[0] = 1; uids[1] = 2; uids[2] = 3; CHECK(runtest_users(cappwd, names, uids, 3)); cap_close(cappwd); /* * Allow: * users: * names: daemon, bin, tty * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "bin"; names[2] = "tty"; CHECK(cap_pwd_limit_users(cappwd, names, 3, NULL, 0) == 0); names[3] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 4, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; uids[0] = 1; uids[1] = 3; uids[2] = 4; CHECK(runtest_users(cappwd, names, uids, 3)); cap_close(cappwd); /* * Allow: * users: * names: * uids: 1, 2, 3 */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "operator"; names[2] = "bin"; uids[0] = 1; uids[1] = 2; uids[2] = 3; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 3) == 0); uids[3] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 4) == -1 && errno == ENOTCAPABLE); uids[0] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 && errno == ENOTCAPABLE); uids[0] = 1; CHECK(runtest_users(cappwd, names, uids, 3)); cap_close(cappwd); /* * Allow: * users: * names: * uids: 1, 3, 4 */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "bin"; names[2] = "tty"; uids[0] = 1; uids[1] = 3; uids[2] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 3) == 0); uids[3] = 5; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 4) == -1 && errno == ENOTCAPABLE); uids[0] = 5; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 && errno == ENOTCAPABLE); uids[0] = 1; CHECK(runtest_users(cappwd, names, uids, 3)); cap_close(cappwd); /* * Allow: * users: * names: bin * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "bin"; CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == 0); names[1] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 2, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "bin"; uids[0] = 3; CHECK(runtest_users(cappwd, names, uids, 1)); cap_close(cappwd); /* * Allow: * users: * names: daemon, tty * uids: */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "tty"; CHECK(cap_pwd_limit_users(cappwd, names, 2, NULL, 0) == 0); names[2] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 3, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "operator"; CHECK(cap_pwd_limit_users(cappwd, names, 1, NULL, 0) == -1 && errno == ENOTCAPABLE); names[0] = "daemon"; uids[0] = 1; uids[1] = 4; CHECK(runtest_users(cappwd, names, uids, 2)); cap_close(cappwd); /* * Allow: * users: * names: * uids: 3 */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "bin"; uids[0] = 3; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == 0); uids[1] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 2) == -1 && errno == ENOTCAPABLE); uids[0] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 && errno == ENOTCAPABLE); uids[0] = 3; CHECK(runtest_users(cappwd, names, uids, 1)); cap_close(cappwd); /* * Allow: * users: * names: * uids: 1, 4 */ cappwd = cap_clone(origcappwd); CHECK(cappwd != NULL); names[0] = "daemon"; names[1] = "tty"; uids[0] = 1; uids[1] = 4; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 2) == 0); uids[2] = 3; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 3) == -1 && errno == ENOTCAPABLE); uids[0] = 3; CHECK(cap_pwd_limit_users(cappwd, NULL, 0, uids, 1) == -1 && errno == ENOTCAPABLE); uids[0] = 1; CHECK(runtest_users(cappwd, names, uids, 2)); cap_close(cappwd); } int main(void) { cap_channel_t *capcas, *cappwd; printf("1..188\n"); fflush(stdout); capcas = cap_init(); CHECKX(capcas != NULL); cappwd = cap_service_open(capcas, "system.pwd"); CHECKX(cappwd != NULL); cap_close(capcas); /* No limits. */ CHECK(runtest_cmds(cappwd) == (GETPWENT | GETPWENT_R | GETPWNAM | GETPWNAM_R | GETPWUID | GETPWUID_R)); test_cmds(cappwd); test_fields(cappwd); test_users(cappwd); cap_close(cappwd); exit(0); } diff --git a/lib/libcasper/services/cap_sysctl/cap_sysctl.c b/lib/libcasper/services/cap_sysctl/cap_sysctl.c index 8e185e6474a0..c99fd74cb169 100644 --- a/lib/libcasper/services/cap_sysctl/cap_sysctl.c +++ b/lib/libcasper/services/cap_sysctl/cap_sysctl.c @@ -1,532 +1,531 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013, 2018 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Portions of this software were developed by Mark Johnston * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include "cap_sysctl.h" /* * Limit interface. */ struct cap_sysctl_limit { cap_channel_t *chan; nvlist_t *nv; }; cap_sysctl_limit_t * cap_sysctl_limit_init(cap_channel_t *chan) { cap_sysctl_limit_t *limit; int error; limit = malloc(sizeof(*limit)); if (limit != NULL) { limit->chan = chan; limit->nv = nvlist_create(NV_FLAG_NO_UNIQUE); if (limit->nv == NULL) { error = errno; free(limit); limit = NULL; errno = error; } } return (limit); } cap_sysctl_limit_t * cap_sysctl_limit_name(cap_sysctl_limit_t *limit, const char *name, int flags) { nvlist_t *lnv; size_t mibsz; int error, mib[CTL_MAXNAME]; lnv = nvlist_create(0); if (lnv == NULL) { error = errno; if (limit->nv != NULL) nvlist_destroy(limit->nv); free(limit); errno = error; return (NULL); } nvlist_add_string(lnv, "name", name); nvlist_add_number(lnv, "operation", flags); mibsz = nitems(mib); error = cap_sysctlnametomib(limit->chan, name, mib, &mibsz); if (error == 0) nvlist_add_binary(lnv, "mib", mib, mibsz * sizeof(int)); nvlist_move_nvlist(limit->nv, "limit", lnv); return (limit); } cap_sysctl_limit_t * cap_sysctl_limit_mib(cap_sysctl_limit_t *limit, const int *mibp, u_int miblen, int flags) { nvlist_t *lnv; int error; lnv = nvlist_create(0); if (lnv == NULL) { error = errno; if (limit->nv != NULL) nvlist_destroy(limit->nv); free(limit); errno = error; return (NULL); } nvlist_add_binary(lnv, "mib", mibp, miblen * sizeof(int)); nvlist_add_number(lnv, "operation", flags); nvlist_add_nvlist(limit->nv, "limit", lnv); return (limit); } int cap_sysctl_limit(cap_sysctl_limit_t *limit) { cap_channel_t *chan; nvlist_t *lnv; chan = limit->chan; lnv = limit->nv; free(limit); /* cap_limit_set(3) will always free the nvlist. */ return (cap_limit_set(chan, lnv)); } /* * Service interface. */ static int do_sysctl(cap_channel_t *chan, nvlist_t *nvl, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) { const uint8_t *retoldp; size_t oldlen; int error; uint8_t operation; operation = 0; if (oldlenp != NULL) operation |= CAP_SYSCTL_READ; if (newp != NULL) operation |= CAP_SYSCTL_WRITE; nvlist_add_number(nvl, "operation", (uint64_t)operation); if (oldp == NULL && oldlenp != NULL) nvlist_add_null(nvl, "justsize"); else if (oldlenp != NULL) nvlist_add_number(nvl, "oldlen", (uint64_t)*oldlenp); if (newp != NULL) nvlist_add_binary(nvl, "newp", newp, newlen); nvl = cap_xfer_nvlist(chan, nvl); if (nvl == NULL) return (-1); error = (int)dnvlist_get_number(nvl, "error", 0); if (error != 0) { nvlist_destroy(nvl); errno = error; return (-1); } if (oldp == NULL && oldlenp != NULL) { *oldlenp = (size_t)nvlist_get_number(nvl, "oldlen"); } else if (oldp != NULL) { retoldp = nvlist_get_binary(nvl, "oldp", &oldlen); memcpy(oldp, retoldp, oldlen); if (oldlenp != NULL) *oldlenp = oldlen; } nvlist_destroy(nvl); return (0); } int cap_sysctl(cap_channel_t *chan, const int *name, u_int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) { nvlist_t *req; req = nvlist_create(0); nvlist_add_string(req, "cmd", "sysctl"); nvlist_add_binary(req, "mib", name, (size_t)namelen * sizeof(int)); return (do_sysctl(chan, req, oldp, oldlenp, newp, newlen)); } int cap_sysctlbyname(cap_channel_t *chan, const char *name, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) { nvlist_t *req; req = nvlist_create(0); nvlist_add_string(req, "cmd", "sysctlbyname"); nvlist_add_string(req, "name", name); return (do_sysctl(chan, req, oldp, oldlenp, newp, newlen)); } int cap_sysctlnametomib(cap_channel_t *chan, const char *name, int *mibp, size_t *sizep) { nvlist_t *req; const void *mib; size_t mibsz; int error; req = nvlist_create(0); nvlist_add_string(req, "cmd", "sysctlnametomib"); nvlist_add_string(req, "name", name); nvlist_add_number(req, "operation", 0); nvlist_add_number(req, "size", (uint64_t)*sizep); req = cap_xfer_nvlist(chan, req); if (req == NULL) return (-1); error = (int)dnvlist_get_number(req, "error", 0); if (error != 0) { nvlist_destroy(req); errno = error; return (-1); } mib = nvlist_get_binary(req, "mib", &mibsz); *sizep = mibsz / sizeof(int); memcpy(mibp, mib, mibsz); nvlist_destroy(req); return (0); } /* * Service implementation. */ /* * Validate a sysctl description. This must consist of an nvlist with either a * binary "mib" field or a string "name", and an operation. */ static int sysctl_valid(const nvlist_t *nvl, bool limit) { const char *name; void *cookie; int type; size_t size; unsigned int field, fields; /* NULL nvl is of course invalid. */ if (nvl == NULL) return (EINVAL); if (nvlist_error(nvl) != 0) return (nvlist_error(nvl)); #define HAS_NAME 0x01 #define HAS_MIB 0x02 #define HAS_ID (HAS_NAME | HAS_MIB) #define HAS_OPERATION 0x04 fields = 0; cookie = NULL; while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { if ((strcmp(name, "name") == 0 && type == NV_TYPE_STRING) || (strcmp(name, "mib") == 0 && type == NV_TYPE_BINARY)) { if (strcmp(name, "mib") == 0) { /* A MIB must be an array of integers. */ (void)cnvlist_get_binary(cookie, &size); if (size % sizeof(int) != 0) return (EINVAL); field = HAS_MIB; } else field = HAS_NAME; /* * A limit may contain both a name and a MIB identifier. */ if ((fields & field) != 0 || (!limit && (fields & HAS_ID) != 0)) return (EINVAL); fields |= field; } else if (strcmp(name, "operation") == 0) { uint64_t mask, operation; if (type != NV_TYPE_NUMBER) return (EINVAL); operation = cnvlist_get_number(cookie); /* * Requests can only include the RDWR flags; limits may * also include the RECURSIVE flag. */ mask = limit ? (CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE) : CAP_SYSCTL_RDWR; if ((operation & ~mask) != 0 || (operation & CAP_SYSCTL_RDWR) == 0) return (EINVAL); /* Only one 'operation' can be present. */ if ((fields & HAS_OPERATION) != 0) return (EINVAL); fields |= HAS_OPERATION; } else if (limit) return (EINVAL); } if ((fields & HAS_OPERATION) == 0 || (fields & HAS_ID) == 0) return (EINVAL); #undef HAS_OPERATION #undef HAS_ID #undef HAS_MIB #undef HAS_NAME return (0); } static bool sysctl_allowed(const nvlist_t *limits, const nvlist_t *req) { const nvlist_t *limit; uint64_t op, reqop; const char *lname, *name, *reqname; void *cookie; size_t lsize, reqsize; const int *lmib, *reqmib; int type; if (limits == NULL) return (true); reqmib = dnvlist_get_binary(req, "mib", &reqsize, NULL, 0); reqname = dnvlist_get_string(req, "name", NULL); reqop = nvlist_get_number(req, "operation"); cookie = NULL; while ((name = nvlist_next(limits, &type, &cookie)) != NULL) { assert(type == NV_TYPE_NVLIST); limit = cnvlist_get_nvlist(cookie); op = nvlist_get_number(limit, "operation"); if ((reqop & op) != reqop) continue; if (reqname != NULL) { lname = dnvlist_get_string(limit, "name", NULL); if (lname == NULL) continue; if ((op & CAP_SYSCTL_RECURSIVE) == 0) { if (strcmp(lname, reqname) != 0) continue; } else { size_t namelen; namelen = strlen(lname); if (strncmp(lname, reqname, namelen) != 0) continue; if (reqname[namelen] != '.' && reqname[namelen] != '\0') continue; } } else { lmib = dnvlist_get_binary(limit, "mib", &lsize, NULL, 0); if (lmib == NULL) continue; if (lsize > reqsize || ((op & CAP_SYSCTL_RECURSIVE) == 0 && lsize < reqsize)) continue; if (memcmp(lmib, reqmib, lsize) != 0) continue; } return (true); } return (false); } static int sysctl_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) { const nvlist_t *nvl; const char *name; void *cookie; int error, type; cookie = NULL; while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { if (strcmp(name, "limit") != 0 || type != NV_TYPE_NVLIST) return (EINVAL); nvl = cnvlist_get_nvlist(cookie); error = sysctl_valid(nvl, true); if (error != 0) return (error); if (!sysctl_allowed(oldlimits, nvl)) return (ENOTCAPABLE); } return (0); } static int nametomib(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) { const char *name; size_t size; int error, *mibp; if (!sysctl_allowed(limits, nvlin)) return (ENOTCAPABLE); name = nvlist_get_string(nvlin, "name"); size = (size_t)nvlist_get_number(nvlin, "size"); mibp = malloc(size * sizeof(*mibp)); if (mibp == NULL) return (ENOMEM); error = sysctlnametomib(name, mibp, &size); if (error != 0) { error = errno; free(mibp); return (error); } nvlist_add_binary(nvlout, "mib", mibp, size * sizeof(*mibp)); return (0); } static int sysctl_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout) { const char *name; const void *newp; const int *mibp; void *oldp; uint64_t operation; size_t oldlen, newlen, size; size_t *oldlenp; int error; if (strcmp(cmd, "sysctlnametomib") == 0) return (nametomib(limits, nvlin, nvlout)); if (strcmp(cmd, "sysctlbyname") != 0 && strcmp(cmd, "sysctl") != 0) return (EINVAL); error = sysctl_valid(nvlin, false); if (error != 0) return (error); if (!sysctl_allowed(limits, nvlin)) return (ENOTCAPABLE); operation = nvlist_get_number(nvlin, "operation"); if ((operation & CAP_SYSCTL_WRITE) != 0) { if (!nvlist_exists_binary(nvlin, "newp")) return (EINVAL); newp = nvlist_get_binary(nvlin, "newp", &newlen); assert(newp != NULL && newlen > 0); } else { newp = NULL; newlen = 0; } if ((operation & CAP_SYSCTL_READ) != 0) { if (nvlist_exists_null(nvlin, "justsize")) { oldp = NULL; oldlen = 0; oldlenp = &oldlen; } else { if (!nvlist_exists_number(nvlin, "oldlen")) return (EINVAL); oldlen = (size_t)nvlist_get_number(nvlin, "oldlen"); if (oldlen == 0) return (EINVAL); oldp = calloc(1, oldlen); if (oldp == NULL) return (ENOMEM); oldlenp = &oldlen; } } else { oldp = NULL; oldlen = 0; oldlenp = NULL; } if (strcmp(cmd, "sysctlbyname") == 0) { name = nvlist_get_string(nvlin, "name"); error = sysctlbyname(name, oldp, oldlenp, newp, newlen); } else { mibp = nvlist_get_binary(nvlin, "mib", &size); error = sysctl(mibp, size / sizeof(*mibp), oldp, oldlenp, newp, newlen); } if (error != 0) { error = errno; free(oldp); return (error); } if ((operation & CAP_SYSCTL_READ) != 0) { if (nvlist_exists_null(nvlin, "justsize")) nvlist_add_number(nvlout, "oldlen", (uint64_t)oldlen); else nvlist_move_binary(nvlout, "oldp", oldp, oldlen); } return (0); } CREATE_SERVICE("system.sysctl", sysctl_limit, sysctl_command, 0); diff --git a/lib/libcasper/services/cap_sysctl/cap_sysctl.h b/lib/libcasper/services/cap_sysctl/cap_sysctl.h index fd7d051f21c8..51243128a683 100644 --- a/lib/libcasper/services/cap_sysctl/cap_sysctl.h +++ b/lib/libcasper/services/cap_sysctl/cap_sysctl.h @@ -1,124 +1,123 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _CAP_SYSCTL_H_ #define _CAP_SYSCTL_H_ #ifdef HAVE_CASPER #define WITH_CASPER #endif #include #define CAP_SYSCTL_READ 0x01 #define CAP_SYSCTL_WRITE 0x02 #define CAP_SYSCTL_RDWR (CAP_SYSCTL_READ | CAP_SYSCTL_WRITE) #define CAP_SYSCTL_RECURSIVE 0x04 struct cap_sysctl_limit; typedef struct cap_sysctl_limit cap_sysctl_limit_t; #ifdef WITH_CASPER __BEGIN_DECLS int cap_sysctl(cap_channel_t *chan, const int *name, u_int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen); int cap_sysctlbyname(cap_channel_t *chan, const char *name, void *oldp, size_t *oldlenp, const void *newp, size_t newlen); int cap_sysctlnametomib(cap_channel_t *chan, const char *name, int *mibp, size_t *sizep); cap_sysctl_limit_t *cap_sysctl_limit_init(cap_channel_t *); cap_sysctl_limit_t *cap_sysctl_limit_name(cap_sysctl_limit_t *limit, const char *name, int flags); cap_sysctl_limit_t *cap_sysctl_limit_mib(cap_sysctl_limit_t *limit, const int *mibp, u_int miblen, int flags); int cap_sysctl_limit(cap_sysctl_limit_t *limit); __END_DECLS #else /* !WITH_CASPER */ static inline int cap_sysctl(cap_channel_t *chan __unused, const int *name, u_int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) { return (sysctl(name, namelen, oldp, oldlenp, newp, newlen)); } static inline int cap_sysctlbyname(cap_channel_t *chan __unused, const char *name, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) { return (sysctlbyname(name, oldp, oldlenp, newp, newlen)); } static inline int cap_sysctlnametomib(cap_channel_t *chan __unused, const char *name, int *mibp, size_t *sizep) { return (sysctlnametomib(name, mibp, sizep)); } static inline cap_sysctl_limit_t * cap_sysctl_limit_init(cap_channel_t *limit __unused) { return (NULL); } static inline cap_sysctl_limit_t * cap_sysctl_limit_name(cap_sysctl_limit_t *limit __unused, const char *name __unused, int flags __unused) { return (NULL); } static inline cap_sysctl_limit_t * cap_sysctl_limit_mib(cap_sysctl_limit_t *limit __unused, const int *mibp __unused, u_int miblen __unused, int flags __unused) { return (NULL); } static inline int cap_sysctl_limit(cap_sysctl_limit_t *limit __unused) { return (0); } #endif /* WITH_CASPER */ #endif /* !_CAP_SYSCTL_H_ */ diff --git a/lib/libcasper/services/cap_sysctl/tests/sysctl_test.c b/lib/libcasper/services/cap_sysctl/tests/sysctl_test.c index 19803692141b..300333f11790 100644 --- a/lib/libcasper/services/cap_sysctl/tests/sysctl_test.c +++ b/lib/libcasper/services/cap_sysctl/tests/sysctl_test.c @@ -1,1690 +1,1689 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013, 2018 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Portions of this software were developed by Mark Johnston * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * We need some sysctls to perform the tests on. * We remember their values and restore them afer the test is done. */ #define SYSCTL0_PARENT "kern" #define SYSCTL0_NAME "kern.sync_on_panic" #define SYSCTL0_FILE "./sysctl0" #define SYSCTL1_PARENT "debug" #define SYSCTL1_NAME "debug.minidump" #define SYSCTL1_FILE "./sysctl1" #define SYSCTL0_READ0 0x0001 #define SYSCTL0_READ1 0x0002 #define SYSCTL0_READ2 0x0004 #define SYSCTL0_WRITE 0x0008 #define SYSCTL0_READ_WRITE 0x0010 #define SYSCTL1_READ0 0x0020 #define SYSCTL1_READ1 0x0040 #define SYSCTL1_READ2 0x0080 #define SYSCTL1_WRITE 0x0100 #define SYSCTL1_READ_WRITE 0x0200 static void save_int_sysctl(const char *name, const char *file) { ssize_t n; size_t sz; int error, fd, val; sz = sizeof(val); error = sysctlbyname(name, &val, &sz, NULL, 0); ATF_REQUIRE_MSG(error == 0, "sysctlbyname(%s): %s", name, strerror(errno)); fd = open(file, O_CREAT | O_WRONLY, 0600); ATF_REQUIRE_MSG(fd >= 0, "open(%s): %s", file, strerror(errno)); n = write(fd, &val, sz); ATF_REQUIRE(n >= 0 && (size_t)n == sz); error = close(fd); ATF_REQUIRE(error == 0); } static void restore_int_sysctl(const char *name, const char *file) { ssize_t n; size_t sz; int error, fd, val; fd = open(file, O_RDONLY); ATF_REQUIRE(fd >= 0); sz = sizeof(val); n = read(fd, &val, sz); ATF_REQUIRE(n >= 0 && (size_t)n == sz); error = unlink(file); ATF_REQUIRE(error == 0); error = close(fd); ATF_REQUIRE(error == 0); error = sysctlbyname(name, NULL, NULL, &val, sz); ATF_REQUIRE_MSG(error == 0, "sysctlbyname(%s): %s", name, strerror(errno)); } static cap_channel_t * initcap(void) { cap_channel_t *capcas, *capsysctl; save_int_sysctl(SYSCTL0_NAME, SYSCTL0_FILE); save_int_sysctl(SYSCTL1_NAME, SYSCTL1_FILE); capcas = cap_init(); ATF_REQUIRE(capcas != NULL); capsysctl = cap_service_open(capcas, "system.sysctl"); ATF_REQUIRE(capsysctl != NULL); cap_close(capcas); return (capsysctl); } static void cleanup(void) { restore_int_sysctl(SYSCTL0_NAME, SYSCTL0_FILE); restore_int_sysctl(SYSCTL1_NAME, SYSCTL1_FILE); } static unsigned int checkcaps(cap_channel_t *capsysctl) { unsigned int result; size_t len0, len1, oldsize; int error, mib0[2], mib1[2], oldvalue, newvalue; result = 0; len0 = nitems(mib0); ATF_REQUIRE(sysctlnametomib(SYSCTL0_NAME, mib0, &len0) == 0); len1 = nitems(mib1); ATF_REQUIRE(sysctlnametomib(SYSCTL1_NAME, mib1, &len1) == 0); oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL0_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue)) result |= SYSCTL0_READ0; } error = cap_sysctl(capsysctl, mib0, len0, &oldvalue, &oldsize, NULL, 0); if ((result & SYSCTL0_READ0) != 0) ATF_REQUIRE(error == 0); else ATF_REQUIRE_ERRNO(ENOTCAPABLE, error != 0); newvalue = 123; if (cap_sysctlbyname(capsysctl, SYSCTL0_NAME, NULL, NULL, &newvalue, sizeof(newvalue)) == 0) { result |= SYSCTL0_WRITE; } if ((result & SYSCTL0_WRITE) != 0) { oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL0_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 123) result |= SYSCTL0_READ1; } } newvalue = 123; error = cap_sysctl(capsysctl, mib0, len0, NULL, NULL, &newvalue, sizeof(newvalue)); if ((result & SYSCTL0_WRITE) != 0) ATF_REQUIRE(error == 0); else ATF_REQUIRE_ERRNO(ENOTCAPABLE, error != 0); oldsize = sizeof(oldvalue); newvalue = 4567; if (cap_sysctlbyname(capsysctl, SYSCTL0_NAME, &oldvalue, &oldsize, &newvalue, sizeof(newvalue)) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 123) result |= SYSCTL0_READ_WRITE; } if ((result & SYSCTL0_READ_WRITE) != 0) { oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL0_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 4567) result |= SYSCTL0_READ2; } } oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL1_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue)) result |= SYSCTL1_READ0; } error = cap_sysctl(capsysctl, mib1, len1, &oldvalue, &oldsize, NULL, 0); if ((result & SYSCTL1_READ0) != 0) ATF_REQUIRE(error == 0); else ATF_REQUIRE_ERRNO(ENOTCAPABLE, error != 0); newvalue = 506; if (cap_sysctlbyname(capsysctl, SYSCTL1_NAME, NULL, NULL, &newvalue, sizeof(newvalue)) == 0) { result |= SYSCTL1_WRITE; } if ((result & SYSCTL1_WRITE) != 0) { newvalue = 506; ATF_REQUIRE(cap_sysctl(capsysctl, mib1, len1, NULL, NULL, &newvalue, sizeof(newvalue)) == 0); oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL1_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 506) result |= SYSCTL1_READ1; } } newvalue = 506; error = cap_sysctl(capsysctl, mib1, len1, NULL, NULL, &newvalue, sizeof(newvalue)); if ((result & SYSCTL1_WRITE) != 0) ATF_REQUIRE(error == 0); else ATF_REQUIRE_ERRNO(ENOTCAPABLE, error != 0); oldsize = sizeof(oldvalue); newvalue = 7008; if (cap_sysctlbyname(capsysctl, SYSCTL1_NAME, &oldvalue, &oldsize, &newvalue, sizeof(newvalue)) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 506) result |= SYSCTL1_READ_WRITE; } if ((result & SYSCTL1_READ_WRITE) != 0) { oldsize = sizeof(oldvalue); if (cap_sysctlbyname(capsysctl, SYSCTL1_NAME, &oldvalue, &oldsize, NULL, 0) == 0) { if (oldsize == sizeof(oldvalue) && oldvalue == 7008) result |= SYSCTL1_READ2; } } return (result); } ATF_TC_WITH_CLEANUP(cap_sysctl__operation); ATF_TC_HEAD(cap_sysctl__operation, tc) { } ATF_TC_BODY(cap_sysctl__operation, tc) { cap_channel_t *capsysctl, *ocapsysctl; void *limit; ocapsysctl = initcap(); /* * Allow: * SYSCTL0_PARENT/RDWR/RECURSIVE * SYSCTL1_PARENT/RDWR/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, "foo.bar", CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, "foo.bar", CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE(checkcaps(capsysctl) == SYSCTL0_READ0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/RDWR/RECURSIVE * SYSCTL1_NAME/RDWR/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/RDWR * SYSCTL1_PARENT/RDWR */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/RDWR * SYSCTL1_NAME/RDWR */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/RDWR * SYSCTL1_PARENT/RDWR/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/RDWR * SYSCTL1_NAME/RDWR/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ/RECURSIVE * SYSCTL1_PARENT/READ/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_READ0)); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ/RECURSIVE * SYSCTL1_NAME/READ/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_READ0)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ * SYSCTL1_PARENT/READ */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ * SYSCTL1_NAME/READ */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_READ0)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ * SYSCTL1_PARENT/READ/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == SYSCTL1_READ0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ * SYSCTL1_NAME/READ/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_READ0)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/WRITE/RECURSIVE * SYSCTL1_PARENT/WRITE/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_WRITE | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/WRITE/RECURSIVE * SYSCTL1_NAME/WRITE/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_WRITE | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/WRITE * SYSCTL1_PARENT/WRITE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/WRITE * SYSCTL1_NAME/WRITE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_WRITE | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/WRITE * SYSCTL1_PARENT/WRITE/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == SYSCTL1_WRITE); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/WRITE * SYSCTL1_NAME/WRITE/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_WRITE | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ/RECURSIVE * SYSCTL1_PARENT/WRITE/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ/RECURSIVE * SYSCTL1_NAME/WRITE/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ * SYSCTL1_PARENT/WRITE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE(checkcaps(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ * SYSCTL1_NAME/WRITE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ * SYSCTL1_PARENT/WRITE/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE(checkcaps(capsysctl) == SYSCTL1_WRITE); cap_close(capsysctl); /* * Allow: * SYSCTL0_NAME/READ * SYSCTL1_NAME/WRITE/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL1_WRITE)); cap_close(capsysctl); } ATF_TC_CLEANUP(cap_sysctl__operation, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(cap_sysctl__names); ATF_TC_HEAD(cap_sysctl__names, tc) { } ATF_TC_BODY(cap_sysctl__names, tc) { cap_channel_t *capsysctl, *ocapsysctl; void *limit; ocapsysctl = initcap(); /* * Allow: * SYSCTL0_PARENT/READ/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == SYSCTL0_READ0); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/READ/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == SYSCTL1_READ0); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/WRITE/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == SYSCTL0_WRITE); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/WRITE/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == SYSCTL1_WRITE); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/RDWR/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/RDWR/RECURSIVE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/READ */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/READ */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_READ); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == SYSCTL1_READ0); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/WRITE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/WRITE */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_WRITE); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == SYSCTL1_WRITE); cap_close(capsysctl); /* * Allow: * SYSCTL0_PARENT/RDWR */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_PARENT, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_PARENT, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == 0); cap_close(capsysctl); /* * Allow: * SYSCTL1_NAME/RDWR */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR); (void)cap_sysctl_limit_name(limit, SYSCTL1_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, SYSCTL0_NAME, CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == -1 && errno == ENOTCAPABLE); ATF_REQUIRE(checkcaps(capsysctl) == (SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); cap_close(capsysctl); } ATF_TC_CLEANUP(cap_sysctl__names, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(cap_sysctl__no_limits); ATF_TC_HEAD(cap_sysctl__no_limits, tc) { } ATF_TC_BODY(cap_sysctl__no_limits, tc) { cap_channel_t *capsysctl; capsysctl = initcap(); ATF_REQUIRE_EQ(checkcaps(capsysctl), (SYSCTL0_READ0 | SYSCTL0_READ1 | SYSCTL0_READ2 | SYSCTL0_WRITE | SYSCTL0_READ_WRITE | SYSCTL1_READ0 | SYSCTL1_READ1 | SYSCTL1_READ2 | SYSCTL1_WRITE | SYSCTL1_READ_WRITE)); } ATF_TC_CLEANUP(cap_sysctl__no_limits, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(cap_sysctl__recursive_limits); ATF_TC_HEAD(cap_sysctl__recursive_limits, tc) { } ATF_TC_BODY(cap_sysctl__recursive_limits, tc) { cap_channel_t *capsysctl, *ocapsysctl; void *limit; size_t len; int mib[2], val = 420; len = nitems(mib); ATF_REQUIRE(sysctlnametomib(SYSCTL0_NAME, mib, &len) == 0); ocapsysctl = initcap(); /* * Make sure that we match entire components. */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, "ker", CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_sysctlbyname(capsysctl, SYSCTL0_NAME, NULL, NULL, &val, sizeof(val))); ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_sysctl(capsysctl, mib, len, NULL, NULL, &val, sizeof(val))); cap_close(capsysctl); /* * Verify that we check for CAP_SYSCTL_RECURSIVE. */ capsysctl = cap_clone(ocapsysctl); ATF_REQUIRE(capsysctl != NULL); limit = cap_sysctl_limit_init(capsysctl); (void)cap_sysctl_limit_name(limit, "kern", CAP_SYSCTL_RDWR); ATF_REQUIRE(cap_sysctl_limit(limit) == 0); ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_sysctlbyname(capsysctl, SYSCTL0_NAME, NULL, NULL, &val, sizeof(val))); ATF_REQUIRE_ERRNO(ENOTCAPABLE, cap_sysctl(capsysctl, mib, len, NULL, NULL, &val, sizeof(val))); cap_close(capsysctl); } ATF_TC_CLEANUP(cap_sysctl__recursive_limits, tc) { cleanup(); } ATF_TC_WITH_CLEANUP(cap_sysctl__just_size); ATF_TC_HEAD(cap_sysctl__just_size, tc) { } ATF_TC_BODY(cap_sysctl__just_size, tc) { cap_channel_t *capsysctl; size_t len; int mib0[2]; capsysctl = initcap(); len = nitems(mib0); ATF_REQUIRE(sysctlnametomib(SYSCTL0_NAME, mib0, &len) == 0); ATF_REQUIRE(cap_sysctlbyname(capsysctl, SYSCTL0_NAME, NULL, &len, NULL, 0) == 0); ATF_REQUIRE(len == sizeof(int)); ATF_REQUIRE(cap_sysctl(capsysctl, mib0, nitems(mib0), NULL, &len, NULL, 0) == 0); ATF_REQUIRE(len == sizeof(int)); cap_close(capsysctl); } ATF_TC_CLEANUP(cap_sysctl__just_size, tc) { cleanup(); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, cap_sysctl__operation); ATF_TP_ADD_TC(tp, cap_sysctl__names); ATF_TP_ADD_TC(tp, cap_sysctl__no_limits); ATF_TP_ADD_TC(tp, cap_sysctl__recursive_limits); ATF_TP_ADD_TC(tp, cap_sysctl__just_size); return (atf_no_error()); } diff --git a/lib/libnv/common_impl.h b/lib/libnv/common_impl.h index 03de9749aaf6..27af1710540a 100644 --- a/lib/libnv/common_impl.h +++ b/lib/libnv/common_impl.h @@ -1,46 +1,45 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _COMMON_IMPL_H_ #define _COMMON_IMPL_H_ #include #include #include static inline bool fd_is_valid(int fd) { return (fcntl(fd, F_GETFD) != -1 || errno != EBADF); } #endif /* !_COMMON_IMPL_H_ */ diff --git a/lib/libnv/tests/nvlist_add_test.c b/lib/libnv/tests/nvlist_add_test.c index 4478efadf4e0..28f2b6f45446 100644 --- a/lib/libnv/tests/nvlist_add_test.c +++ b/lib/libnv/tests/nvlist_add_test.c @@ -1,194 +1,193 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ ntest++; \ } while (0) int main(void) { const nvlist_t *cnvl; nvlist_t *nvl; printf("1..94\n"); nvl = nvlist_create(0); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); nvlist_add_null(nvl, "nvlist/null"); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/true")); nvlist_add_bool(nvl, "nvlist/bool/true", true); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_bool(nvl, "nvlist/bool/true")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/false")); nvlist_add_bool(nvl, "nvlist/bool/false", false); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_bool(nvl, "nvlist/bool/false")); CHECK(!nvlist_exists_number(nvl, "nvlist/number/0")); nvlist_add_number(nvl, "nvlist/number/0", 0); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_number(nvl, "nvlist/number/0")); CHECK(!nvlist_exists_number(nvl, "nvlist/number/1")); nvlist_add_number(nvl, "nvlist/number/1", 1); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_number(nvl, "nvlist/number/1")); CHECK(!nvlist_exists_number(nvl, "nvlist/number/-1")); nvlist_add_number(nvl, "nvlist/number/-1", -1); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_number(nvl, "nvlist/number/-1")); CHECK(!nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); nvlist_add_number(nvl, "nvlist/number/UINT64_MAX", UINT64_MAX); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); nvlist_add_number(nvl, "nvlist/number/INT64_MIN", INT64_MIN); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); nvlist_add_number(nvl, "nvlist/number/INT64_MAX", INT64_MAX); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); CHECK(!nvlist_exists_string(nvl, "nvlist/string/")); nvlist_add_string(nvl, "nvlist/string/", ""); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_string(nvl, "nvlist/string/")); CHECK(!nvlist_exists_string(nvl, "nvlist/string/x")); nvlist_add_string(nvl, "nvlist/string/x", "x"); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); CHECK(!nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); nvlist_add_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); CHECK(!nvlist_exists_string(nvl, "nvlist/stringf/")); nvlist_add_stringf(nvl, "nvlist/stringf/", "%s", ""); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_string(nvl, "nvlist/stringf/")); CHECK(!nvlist_exists_string(nvl, "nvlist/stringf/x")); nvlist_add_stringf(nvl, "nvlist/stringf/x", "%s", "x"); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_string(nvl, "nvlist/stringf/x")); CHECK(!nvlist_exists_string(nvl, "nvlist/stringf/666Xabc")); nvlist_add_stringf(nvl, "nvlist/stringf/666Xabc", "%d%c%s", 666, 'X', "abc"); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_string(nvl, "nvlist/stringf/666Xabc")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); nvlist_add_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", STDERR_FILENO); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/x")); nvlist_add_binary(nvl, "nvlist/binary/x", "x", 1); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); nvlist_add_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_null(nvl, "nvlist/null")); CHECK(nvlist_exists_bool(nvl, "nvlist/bool/true")); CHECK(nvlist_exists_bool(nvl, "nvlist/bool/false")); CHECK(nvlist_exists_number(nvl, "nvlist/number/0")); CHECK(nvlist_exists_number(nvl, "nvlist/number/1")); CHECK(nvlist_exists_number(nvl, "nvlist/number/-1")); CHECK(nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); CHECK(nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); CHECK(nvlist_exists_string(nvl, "nvlist/string/")); CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); CHECK(nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); CHECK(nvlist_exists_string(nvl, "nvlist/stringf/")); CHECK(nvlist_exists_string(nvl, "nvlist/stringf/x")); CHECK(nvlist_exists_string(nvl, "nvlist/stringf/666Xabc")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); cnvl = nvlist_get_nvlist(nvl, "nvlist/nvlist"); CHECK(nvlist_exists_null(cnvl, "nvlist/null")); CHECK(nvlist_exists_bool(cnvl, "nvlist/bool/true")); CHECK(nvlist_exists_bool(cnvl, "nvlist/bool/false")); CHECK(nvlist_exists_number(cnvl, "nvlist/number/0")); CHECK(nvlist_exists_number(cnvl, "nvlist/number/1")); CHECK(nvlist_exists_number(cnvl, "nvlist/number/-1")); CHECK(nvlist_exists_number(cnvl, "nvlist/number/UINT64_MAX")); CHECK(nvlist_exists_number(cnvl, "nvlist/number/INT64_MIN")); CHECK(nvlist_exists_number(cnvl, "nvlist/number/INT64_MAX")); CHECK(nvlist_exists_string(cnvl, "nvlist/string/")); CHECK(nvlist_exists_string(cnvl, "nvlist/string/x")); CHECK(nvlist_exists_string(cnvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); CHECK(nvlist_exists_string(cnvl, "nvlist/stringf/")); CHECK(nvlist_exists_string(cnvl, "nvlist/stringf/x")); CHECK(nvlist_exists_string(cnvl, "nvlist/stringf/666Xabc")); CHECK(nvlist_exists_descriptor(cnvl, "nvlist/descriptor/STDERR_FILENO")); CHECK(nvlist_exists_binary(cnvl, "nvlist/binary/x")); CHECK(nvlist_exists_binary(cnvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); nvlist_destroy(nvl); return (0); } diff --git a/lib/libnv/tests/nvlist_exists_test.c b/lib/libnv/tests/nvlist_exists_test.c index 7450256ab760..808692feb85b 100644 --- a/lib/libnv/tests/nvlist_exists_test.c +++ b/lib/libnv/tests/nvlist_exists_test.c @@ -1,319 +1,318 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ ntest++; \ } while (0) int main(void) { nvlist_t *nvl; printf("1..232\n"); nvl = nvlist_create(0); CHECK(!nvlist_exists(nvl, "nvlist/null")); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/null")); CHECK(!nvlist_exists_number(nvl, "nvlist/null")); CHECK(!nvlist_exists_string(nvl, "nvlist/null")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/null")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/null")); CHECK(!nvlist_exists_binary(nvl, "nvlist/null")); nvlist_add_null(nvl, "nvlist/null"); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists(nvl, "nvlist/null")); CHECK(nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/null")); CHECK(!nvlist_exists_number(nvl, "nvlist/null")); CHECK(!nvlist_exists_string(nvl, "nvlist/null")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/null")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/null")); CHECK(!nvlist_exists_binary(nvl, "nvlist/null")); CHECK(!nvlist_exists(nvl, "nvlist/bool")); CHECK(!nvlist_exists_null(nvl, "nvlist/bool")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/bool")); CHECK(!nvlist_exists_string(nvl, "nvlist/bool")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/bool")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/bool")); CHECK(!nvlist_exists_binary(nvl, "nvlist/bool")); nvlist_add_bool(nvl, "nvlist/bool", true); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists(nvl, "nvlist/bool")); CHECK(!nvlist_exists_null(nvl, "nvlist/bool")); CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/bool")); CHECK(!nvlist_exists_string(nvl, "nvlist/bool")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/bool")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/bool")); CHECK(!nvlist_exists_binary(nvl, "nvlist/bool")); CHECK(!nvlist_exists(nvl, "nvlist/number")); CHECK(!nvlist_exists_null(nvl, "nvlist/number")); CHECK(!nvlist_exists_bool(nvl, "nvlist/number")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/number")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/number")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/number")); CHECK(!nvlist_exists_binary(nvl, "nvlist/number")); nvlist_add_number(nvl, "nvlist/number", 0); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists(nvl, "nvlist/number")); CHECK(!nvlist_exists_null(nvl, "nvlist/number")); CHECK(!nvlist_exists_bool(nvl, "nvlist/number")); CHECK(nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/number")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/number")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/number")); CHECK(!nvlist_exists_binary(nvl, "nvlist/number")); CHECK(!nvlist_exists(nvl, "nvlist/string")); CHECK(!nvlist_exists_null(nvl, "nvlist/string")); CHECK(!nvlist_exists_bool(nvl, "nvlist/string")); CHECK(!nvlist_exists_number(nvl, "nvlist/string")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/string")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/string")); CHECK(!nvlist_exists_binary(nvl, "nvlist/string")); nvlist_add_string(nvl, "nvlist/string", "test"); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists(nvl, "nvlist/string")); CHECK(!nvlist_exists_null(nvl, "nvlist/string")); CHECK(!nvlist_exists_bool(nvl, "nvlist/string")); CHECK(!nvlist_exists_number(nvl, "nvlist/string")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/string")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/string")); CHECK(!nvlist_exists_binary(nvl, "nvlist/string")); CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_null(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_bool(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_number(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_string(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_binary(nvl, "nvlist/nvlist")); nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_null(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_bool(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_number(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_string(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_binary(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_null(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_bool(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_number(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_string(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_binary(nvl, "nvlist/descriptor")); nvlist_add_descriptor(nvl, "nvlist/descriptor", STDERR_FILENO); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_null(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_bool(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_number(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_string(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_binary(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists(nvl, "nvlist/binary")); CHECK(!nvlist_exists_null(nvl, "nvlist/binary")); CHECK(!nvlist_exists_bool(nvl, "nvlist/binary")); CHECK(!nvlist_exists_number(nvl, "nvlist/binary")); CHECK(!nvlist_exists_string(nvl, "nvlist/binary")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/binary")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/binary")); CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_add_binary(nvl, "nvlist/binary", "test", 4); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists(nvl, "nvlist/binary")); CHECK(!nvlist_exists_null(nvl, "nvlist/binary")); CHECK(!nvlist_exists_bool(nvl, "nvlist/binary")); CHECK(!nvlist_exists_number(nvl, "nvlist/binary")); CHECK(!nvlist_exists_string(nvl, "nvlist/binary")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/binary")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/binary")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); CHECK(nvlist_exists(nvl, "nvlist/null")); CHECK(nvlist_exists(nvl, "nvlist/bool")); CHECK(nvlist_exists(nvl, "nvlist/number")); CHECK(nvlist_exists(nvl, "nvlist/string")); CHECK(nvlist_exists(nvl, "nvlist/nvlist")); CHECK(nvlist_exists(nvl, "nvlist/descriptor")); CHECK(nvlist_exists(nvl, "nvlist/binary")); CHECK(nvlist_exists_null(nvl, "nvlist/null")); CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_null(nvl, "nvlist/null"); CHECK(!nvlist_exists(nvl, "nvlist/null")); CHECK(nvlist_exists(nvl, "nvlist/bool")); CHECK(nvlist_exists(nvl, "nvlist/number")); CHECK(nvlist_exists(nvl, "nvlist/string")); CHECK(nvlist_exists(nvl, "nvlist/nvlist")); CHECK(nvlist_exists(nvl, "nvlist/descriptor")); CHECK(nvlist_exists(nvl, "nvlist/binary")); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_bool(nvl, "nvlist/bool"); CHECK(!nvlist_exists(nvl, "nvlist/null")); CHECK(!nvlist_exists(nvl, "nvlist/bool")); CHECK(nvlist_exists(nvl, "nvlist/number")); CHECK(nvlist_exists(nvl, "nvlist/string")); CHECK(nvlist_exists(nvl, "nvlist/nvlist")); CHECK(nvlist_exists(nvl, "nvlist/descriptor")); CHECK(nvlist_exists(nvl, "nvlist/binary")); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_number(nvl, "nvlist/number"); CHECK(!nvlist_exists(nvl, "nvlist/null")); CHECK(!nvlist_exists(nvl, "nvlist/bool")); CHECK(!nvlist_exists(nvl, "nvlist/number")); CHECK(nvlist_exists(nvl, "nvlist/string")); CHECK(nvlist_exists(nvl, "nvlist/nvlist")); CHECK(nvlist_exists(nvl, "nvlist/descriptor")); CHECK(nvlist_exists(nvl, "nvlist/binary")); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_string(nvl, "nvlist/string"); CHECK(!nvlist_exists(nvl, "nvlist/null")); CHECK(!nvlist_exists(nvl, "nvlist/bool")); CHECK(!nvlist_exists(nvl, "nvlist/number")); CHECK(!nvlist_exists(nvl, "nvlist/string")); CHECK(nvlist_exists(nvl, "nvlist/nvlist")); CHECK(nvlist_exists(nvl, "nvlist/descriptor")); CHECK(nvlist_exists(nvl, "nvlist/binary")); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_nvlist(nvl, "nvlist/nvlist"); CHECK(!nvlist_exists(nvl, "nvlist/null")); CHECK(!nvlist_exists(nvl, "nvlist/bool")); CHECK(!nvlist_exists(nvl, "nvlist/number")); CHECK(!nvlist_exists(nvl, "nvlist/string")); CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); CHECK(nvlist_exists(nvl, "nvlist/descriptor")); CHECK(nvlist_exists(nvl, "nvlist/binary")); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_descriptor(nvl, "nvlist/descriptor"); CHECK(!nvlist_exists(nvl, "nvlist/null")); CHECK(!nvlist_exists(nvl, "nvlist/bool")); CHECK(!nvlist_exists(nvl, "nvlist/number")); CHECK(!nvlist_exists(nvl, "nvlist/string")); CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists(nvl, "nvlist/descriptor")); CHECK(nvlist_exists(nvl, "nvlist/binary")); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_binary(nvl, "nvlist/binary"); CHECK(!nvlist_exists(nvl, "nvlist/null")); CHECK(!nvlist_exists(nvl, "nvlist/bool")); CHECK(!nvlist_exists(nvl, "nvlist/number")); CHECK(!nvlist_exists(nvl, "nvlist/string")); CHECK(!nvlist_exists(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists(nvl, "nvlist/binary")); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); CHECK(nvlist_empty(nvl)); nvlist_destroy(nvl); return (0); } diff --git a/lib/libnv/tests/nvlist_free_test.c b/lib/libnv/tests/nvlist_free_test.c index c715aef6657b..bb5eeaf7d04c 100644 --- a/lib/libnv/tests/nvlist_free_test.c +++ b/lib/libnv/tests/nvlist_free_test.c @@ -1,219 +1,218 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ ntest++; \ } while (0) int main(void) { nvlist_t *nvl; printf("1..114\n"); nvl = nvlist_create(0); nvlist_add_null(nvl, "nvlist/null"); nvlist_add_bool(nvl, "nvlist/bool", true); nvlist_add_number(nvl, "nvlist/number", 0); nvlist_add_string(nvl, "nvlist/string", "test"); nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); nvlist_add_descriptor(nvl, "nvlist/descriptor", STDERR_FILENO); nvlist_add_binary(nvl, "nvlist/binary", "test", 4); CHECK(nvlist_exists_null(nvl, "nvlist/null")); CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_null(nvl, "nvlist/null"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_bool(nvl, "nvlist/bool"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_number(nvl, "nvlist/number"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_string(nvl, "nvlist/string"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_nvlist(nvl, "nvlist/nvlist"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_descriptor(nvl, "nvlist/descriptor"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free_binary(nvl, "nvlist/binary"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); CHECK(nvlist_empty(nvl)); nvlist_add_null(nvl, "nvlist/null"); nvlist_add_bool(nvl, "nvlist/bool", true); nvlist_add_number(nvl, "nvlist/number", 0); nvlist_add_string(nvl, "nvlist/string", "test"); nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); nvlist_add_descriptor(nvl, "nvlist/descriptor", STDERR_FILENO); nvlist_add_binary(nvl, "nvlist/binary", "test", 4); CHECK(nvlist_exists_null(nvl, "nvlist/null")); CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free(nvl, "nvlist/null"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free(nvl, "nvlist/bool"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free(nvl, "nvlist/number"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free(nvl, "nvlist/string"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free(nvl, "nvlist/nvlist"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free(nvl, "nvlist/descriptor"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary")); nvlist_free(nvl, "nvlist/binary"); CHECK(!nvlist_exists_null(nvl, "nvlist/null")); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool")); CHECK(!nvlist_exists_number(nvl, "nvlist/number")); CHECK(!nvlist_exists_string(nvl, "nvlist/string")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor")); CHECK(!nvlist_exists_binary(nvl, "nvlist/binary")); CHECK(nvlist_empty(nvl)); nvlist_destroy(nvl); return (0); } diff --git a/lib/libnv/tests/nvlist_get_test.c b/lib/libnv/tests/nvlist_get_test.c index d6e4a9b4d65a..f06be569c90d 100644 --- a/lib/libnv/tests/nvlist_get_test.c +++ b/lib/libnv/tests/nvlist_get_test.c @@ -1,179 +1,178 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ ntest++; \ } while (0) #define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) int main(void) { const nvlist_t *cnvl; nvlist_t *nvl; size_t size; printf("1..83\n"); nvl = nvlist_create(0); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/true")); nvlist_add_bool(nvl, "nvlist/bool/true", true); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_get_bool(nvl, "nvlist/bool/true") == true); CHECK(!nvlist_exists_bool(nvl, "nvlist/bool/false")); nvlist_add_bool(nvl, "nvlist/bool/false", false); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_get_bool(nvl, "nvlist/bool/false") == false); CHECK(!nvlist_exists_number(nvl, "nvlist/number/0")); nvlist_add_number(nvl, "nvlist/number/0", 0); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_get_number(nvl, "nvlist/number/0") == 0); CHECK(!nvlist_exists_number(nvl, "nvlist/number/1")); nvlist_add_number(nvl, "nvlist/number/1", 1); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_get_number(nvl, "nvlist/number/1") == 1); CHECK(!nvlist_exists_number(nvl, "nvlist/number/-1")); nvlist_add_number(nvl, "nvlist/number/-1", -1); CHECK(nvlist_error(nvl) == 0); CHECK((int)nvlist_get_number(nvl, "nvlist/number/-1") == -1); CHECK(!nvlist_exists_number(nvl, "nvlist/number/UINT64_MAX")); nvlist_add_number(nvl, "nvlist/number/UINT64_MAX", UINT64_MAX); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_get_number(nvl, "nvlist/number/UINT64_MAX") == UINT64_MAX); CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MIN")); nvlist_add_number(nvl, "nvlist/number/INT64_MIN", INT64_MIN); CHECK(nvlist_error(nvl) == 0); CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MIN") == INT64_MIN); CHECK(!nvlist_exists_number(nvl, "nvlist/number/INT64_MAX")); nvlist_add_number(nvl, "nvlist/number/INT64_MAX", INT64_MAX); CHECK(nvlist_error(nvl) == 0); CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MAX") == INT64_MAX); CHECK(!nvlist_exists_string(nvl, "nvlist/string/")); nvlist_add_string(nvl, "nvlist/string/", ""); CHECK(nvlist_error(nvl) == 0); CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/"), "") == 0); CHECK(!nvlist_exists_string(nvl, "nvlist/string/x")); nvlist_add_string(nvl, "nvlist/string/x", "x"); CHECK(nvlist_error(nvl) == 0); CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/x"), "x") == 0); CHECK(!nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); nvlist_add_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz"); CHECK(nvlist_error(nvl) == 0); CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz") == 0); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); nvlist_add_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", STDERR_FILENO); CHECK(nvlist_error(nvl) == 0); CHECK(fd_is_valid(nvlist_get_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO"))); CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/x")); nvlist_add_binary(nvl, "nvlist/binary/x", "x", 1); CHECK(nvlist_error(nvl) == 0); CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", NULL), "x", 1) == 0); CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", &size), "x", 1) == 0); CHECK(size == 1); CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); nvlist_add_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")); CHECK(nvlist_error(nvl) == 0); CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); CHECK(nvlist_error(nvl) == 0); cnvl = nvlist_get_nvlist(nvl, "nvlist/nvlist"); CHECK(nvlist_get_bool(cnvl, "nvlist/bool/true") == true); CHECK(nvlist_get_bool(cnvl, "nvlist/bool/false") == false); CHECK(nvlist_get_number(cnvl, "nvlist/number/0") == 0); CHECK(nvlist_get_number(cnvl, "nvlist/number/1") == 1); CHECK((int)nvlist_get_number(cnvl, "nvlist/number/-1") == -1); CHECK(nvlist_get_number(cnvl, "nvlist/number/UINT64_MAX") == UINT64_MAX); CHECK((int64_t)nvlist_get_number(cnvl, "nvlist/number/INT64_MIN") == INT64_MIN); CHECK((int64_t)nvlist_get_number(cnvl, "nvlist/number/INT64_MAX") == INT64_MAX); CHECK(strcmp(nvlist_get_string(cnvl, "nvlist/string/"), "") == 0); CHECK(strcmp(nvlist_get_string(cnvl, "nvlist/string/x"), "x") == 0); CHECK(strcmp(nvlist_get_string(cnvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz") == 0); /* TODO */ CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/x", NULL), "x", 1) == 0); CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/x", &size), "x", 1) == 0); CHECK(size == 1); CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); CHECK(memcmp(nvlist_get_binary(cnvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); CHECK(nvlist_get_bool(nvl, "nvlist/bool/true") == true); CHECK(nvlist_get_bool(nvl, "nvlist/bool/false") == false); CHECK(nvlist_get_number(nvl, "nvlist/number/0") == 0); CHECK(nvlist_get_number(nvl, "nvlist/number/1") == 1); CHECK((int)nvlist_get_number(nvl, "nvlist/number/-1") == -1); CHECK(nvlist_get_number(nvl, "nvlist/number/UINT64_MAX") == UINT64_MAX); CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MIN") == INT64_MIN); CHECK((int64_t)nvlist_get_number(nvl, "nvlist/number/INT64_MAX") == INT64_MAX); CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/"), "") == 0); CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/x"), "x") == 0); CHECK(strcmp(nvlist_get_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz"), "abcdefghijklmnopqrstuvwxyz") == 0); CHECK(fd_is_valid(nvlist_get_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO"))); CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", NULL), "x", 1) == 0); CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/x", &size), "x", 1) == 0); CHECK(size == 1); CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); CHECK(memcmp(nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size), "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")) == 0); CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); nvlist_destroy(nvl); return (0); } diff --git a/lib/libnv/tests/nvlist_move_test.c b/lib/libnv/tests/nvlist_move_test.c index 3c99f4582c5e..2ef7ca4de384 100644 --- a/lib/libnv/tests/nvlist_move_test.c +++ b/lib/libnv/tests/nvlist_move_test.c @@ -1,159 +1,158 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include static int ntest = 1; #define CHECK(expr) do { \ if ((expr)) \ printf("ok # %d %s:%u\n", ntest, __FILE__, __LINE__); \ else \ printf("not ok # %d %s:%u\n", ntest, __FILE__, __LINE__);\ ntest++; \ } while (0) int main(void) { const nvlist_t *cnvl; nvlist_t *nvl; void *ptr; size_t size; int fd; printf("1..52\n"); nvl = nvlist_create(0); CHECK(!nvlist_exists_string(nvl, "nvlist/string/")); ptr = strdup(""); CHECK(ptr != NULL); nvlist_move_string(nvl, "nvlist/string/", ptr); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_string(nvl, "nvlist/string/")); CHECK(ptr == nvlist_get_string(nvl, "nvlist/string/")); CHECK(!nvlist_exists_string(nvl, "nvlist/string/x")); ptr = strdup("x"); CHECK(ptr != NULL); nvlist_move_string(nvl, "nvlist/string/x", ptr); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); CHECK(ptr == nvlist_get_string(nvl, "nvlist/string/x")); CHECK(!nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); ptr = strdup("abcdefghijklmnopqrstuvwxyz"); CHECK(ptr != NULL); nvlist_move_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz", ptr); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); CHECK(ptr == nvlist_get_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); CHECK(!nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); fd = dup(STDERR_FILENO); CHECK(fd >= 0); nvlist_move_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", fd); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); CHECK(fd == nvlist_get_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/x")); ptr = malloc(1); CHECK(ptr != NULL); memcpy(ptr, "x", 1); nvlist_move_binary(nvl, "nvlist/binary/x", ptr, 1); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); CHECK(ptr == nvlist_get_binary(nvl, "nvlist/binary/x", NULL)); CHECK(ptr == nvlist_get_binary(nvl, "nvlist/binary/x", &size)); CHECK(size == 1); CHECK(!nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); ptr = malloc(sizeof("abcdefghijklmnopqrstuvwxyz")); CHECK(ptr != NULL); memcpy(ptr, "abcdefghijklmnopqrstuvwxyz", sizeof("abcdefghijklmnopqrstuvwxyz")); nvlist_move_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", ptr, sizeof("abcdefghijklmnopqrstuvwxyz")); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); CHECK(ptr == nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", NULL)); CHECK(ptr == nvlist_get_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz", &size)); CHECK(size == sizeof("abcdefghijklmnopqrstuvwxyz")); CHECK(!nvlist_exists_nvlist(nvl, "nvlist/nvlist")); ptr = nvlist_clone(nvl); CHECK(ptr != NULL); nvlist_move_nvlist(nvl, "nvlist/nvlist", ptr); CHECK(nvlist_error(nvl) == 0); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); CHECK(ptr == nvlist_get_nvlist(nvl, "nvlist/nvlist")); CHECK(nvlist_exists_string(nvl, "nvlist/string/")); CHECK(nvlist_exists_string(nvl, "nvlist/string/x")); CHECK(nvlist_exists_string(nvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); CHECK(nvlist_exists_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary/x")); CHECK(nvlist_exists_binary(nvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); CHECK(nvlist_exists_nvlist(nvl, "nvlist/nvlist")); cnvl = nvlist_get_nvlist(nvl, "nvlist/nvlist"); CHECK(nvlist_exists_string(cnvl, "nvlist/string/")); CHECK(nvlist_exists_string(cnvl, "nvlist/string/x")); CHECK(nvlist_exists_string(cnvl, "nvlist/string/abcdefghijklmnopqrstuvwxyz")); CHECK(nvlist_exists_descriptor(cnvl, "nvlist/descriptor/STDERR_FILENO")); CHECK(nvlist_exists_binary(cnvl, "nvlist/binary/x")); CHECK(nvlist_exists_binary(cnvl, "nvlist/binary/abcdefghijklmnopqrstuvwxyz")); nvlist_destroy(nvl); return (0); } diff --git a/lib/libnv/tests/nvlist_send_recv_test.c b/lib/libnv/tests/nvlist_send_recv_test.c index 4dd9fd35421f..f060ee2684d5 100644 --- a/lib/libnv/tests/nvlist_send_recv_test.c +++ b/lib/libnv/tests/nvlist_send_recv_test.c @@ -1,557 +1,556 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ALPHABET "abcdefghijklmnopqrstuvwxyz" #define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) static void send_nvlist_child(int sock) { nvlist_t *nvl; nvlist_t *empty; int pfd[2]; nvl = nvlist_create(0); empty = nvlist_create(0); nvlist_add_bool(nvl, "nvlist/bool/true", true); nvlist_add_bool(nvl, "nvlist/bool/false", false); nvlist_add_number(nvl, "nvlist/number/0", 0); nvlist_add_number(nvl, "nvlist/number/1", 1); nvlist_add_number(nvl, "nvlist/number/-1", -1); nvlist_add_number(nvl, "nvlist/number/UINT64_MAX", UINT64_MAX); nvlist_add_number(nvl, "nvlist/number/INT64_MIN", INT64_MIN); nvlist_add_number(nvl, "nvlist/number/INT64_MAX", INT64_MAX); nvlist_add_string(nvl, "nvlist/string/", ""); nvlist_add_string(nvl, "nvlist/string/x", "x"); nvlist_add_string(nvl, "nvlist/string/" ALPHABET, ALPHABET); nvlist_add_descriptor(nvl, "nvlist/descriptor/STDERR_FILENO", STDERR_FILENO); if (pipe(pfd) == -1) err(EXIT_FAILURE, "pipe"); if (write(pfd[1], "test", 4) != 4) err(EXIT_FAILURE, "write"); close(pfd[1]); nvlist_add_descriptor(nvl, "nvlist/descriptor/pipe_rd", pfd[0]); close(pfd[0]); nvlist_add_binary(nvl, "nvlist/binary/x", "x", 1); nvlist_add_binary(nvl, "nvlist/binary/" ALPHABET, ALPHABET, sizeof(ALPHABET)); nvlist_move_nvlist(nvl, "nvlist/nvlist/empty", empty); nvlist_add_nvlist(nvl, "nvlist/nvlist", nvl); nvlist_send(sock, nvl); nvlist_destroy(nvl); } static void send_nvlist_parent(int sock) { nvlist_t *nvl; const nvlist_t *cnvl, *empty; const char *name, *cname; void *cookie, *ccookie; int type, ctype, fd; size_t size; char buf[4]; nvl = nvlist_recv(sock, 0); ATF_REQUIRE(nvlist_error(nvl) == 0); if (nvlist_error(nvl) != 0) err(1, "nvlist_recv() failed"); cookie = NULL; name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_BOOL); ATF_REQUIRE(strcmp(name, "nvlist/bool/true") == 0); ATF_REQUIRE(nvlist_get_bool(nvl, name) == true); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_BOOL); ATF_REQUIRE(strcmp(name, "nvlist/bool/false") == 0); ATF_REQUIRE(nvlist_get_bool(nvl, name) == false); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(name, "nvlist/number/0") == 0); ATF_REQUIRE(nvlist_get_number(nvl, name) == 0); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(name, "nvlist/number/1") == 0); ATF_REQUIRE(nvlist_get_number(nvl, name) == 1); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(name, "nvlist/number/-1") == 0); ATF_REQUIRE((int)nvlist_get_number(nvl, name) == -1); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(name, "nvlist/number/UINT64_MAX") == 0); ATF_REQUIRE(nvlist_get_number(nvl, name) == UINT64_MAX); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(name, "nvlist/number/INT64_MIN") == 0); ATF_REQUIRE((int64_t)nvlist_get_number(nvl, name) == INT64_MIN); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(name, "nvlist/number/INT64_MAX") == 0); ATF_REQUIRE((int64_t)nvlist_get_number(nvl, name) == INT64_MAX); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_STRING); ATF_REQUIRE(strcmp(name, "nvlist/string/") == 0); ATF_REQUIRE(strcmp(nvlist_get_string(nvl, name), "") == 0); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_STRING); ATF_REQUIRE(strcmp(name, "nvlist/string/x") == 0); ATF_REQUIRE(strcmp(nvlist_get_string(nvl, name), "x") == 0); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_STRING); ATF_REQUIRE(strcmp(name, "nvlist/string/" ALPHABET) == 0); ATF_REQUIRE(strcmp(nvlist_get_string(nvl, name), ALPHABET) == 0); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_DESCRIPTOR); ATF_REQUIRE(strcmp(name, "nvlist/descriptor/STDERR_FILENO") == 0); ATF_REQUIRE(fd_is_valid(nvlist_get_descriptor(nvl, name))); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_DESCRIPTOR); ATF_REQUIRE(strcmp(name, "nvlist/descriptor/pipe_rd") == 0); fd = nvlist_get_descriptor(nvl, name); ATF_REQUIRE(fd_is_valid(fd)); ATF_REQUIRE(read(fd, buf, sizeof(buf)) == 4); ATF_REQUIRE(strncmp(buf, "test", sizeof(buf)) == 0); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_BINARY); ATF_REQUIRE(strcmp(name, "nvlist/binary/x") == 0); ATF_REQUIRE(memcmp(nvlist_get_binary(nvl, name, NULL), "x", 1) == 0); ATF_REQUIRE(memcmp(nvlist_get_binary(nvl, name, &size), "x", 1) == 0); ATF_REQUIRE(size == 1); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_BINARY); ATF_REQUIRE(strcmp(name, "nvlist/binary/" ALPHABET) == 0); ATF_REQUIRE(memcmp(nvlist_get_binary(nvl, name, NULL), ALPHABET, sizeof(ALPHABET)) == 0); ATF_REQUIRE(memcmp(nvlist_get_binary(nvl, name, &size), ALPHABET, sizeof(ALPHABET)) == 0); ATF_REQUIRE(size == sizeof(ALPHABET)); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_NVLIST); ATF_REQUIRE(strcmp(name, "nvlist/nvlist/empty") == 0); cnvl = nvlist_get_nvlist(nvl, name); ATF_REQUIRE(nvlist_empty(cnvl)); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name != NULL); ATF_REQUIRE(type == NV_TYPE_NVLIST); ATF_REQUIRE(strcmp(name, "nvlist/nvlist") == 0); cnvl = nvlist_get_nvlist(nvl, name); ccookie = NULL; cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_BOOL); ATF_REQUIRE(strcmp(cname, "nvlist/bool/true") == 0); ATF_REQUIRE(nvlist_get_bool(cnvl, cname) == true); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_BOOL); ATF_REQUIRE(strcmp(cname, "nvlist/bool/false") == 0); ATF_REQUIRE(nvlist_get_bool(cnvl, cname) == false); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(cname, "nvlist/number/0") == 0); ATF_REQUIRE(nvlist_get_number(cnvl, cname) == 0); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(cname, "nvlist/number/1") == 0); ATF_REQUIRE(nvlist_get_number(cnvl, cname) == 1); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(cname, "nvlist/number/-1") == 0); ATF_REQUIRE((int)nvlist_get_number(cnvl, cname) == -1); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(cname, "nvlist/number/UINT64_MAX") == 0); ATF_REQUIRE(nvlist_get_number(cnvl, cname) == UINT64_MAX); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(cname, "nvlist/number/INT64_MIN") == 0); ATF_REQUIRE((int64_t)nvlist_get_number(cnvl, cname) == INT64_MIN); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_NUMBER); ATF_REQUIRE(strcmp(cname, "nvlist/number/INT64_MAX") == 0); ATF_REQUIRE((int64_t)nvlist_get_number(cnvl, cname) == INT64_MAX); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_STRING); ATF_REQUIRE(strcmp(cname, "nvlist/string/") == 0); ATF_REQUIRE(strcmp(nvlist_get_string(cnvl, cname), "") == 0); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_STRING); ATF_REQUIRE(strcmp(cname, "nvlist/string/x") == 0); ATF_REQUIRE(strcmp(nvlist_get_string(cnvl, cname), "x") == 0); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_STRING); ATF_REQUIRE(strcmp(cname, "nvlist/string/" ALPHABET) == 0); ATF_REQUIRE(strcmp(nvlist_get_string(cnvl, cname), ALPHABET) == 0); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_DESCRIPTOR); ATF_REQUIRE(strcmp(cname, "nvlist/descriptor/STDERR_FILENO") == 0); ATF_REQUIRE(fd_is_valid(nvlist_get_descriptor(cnvl, cname))); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_DESCRIPTOR); ATF_REQUIRE(strcmp(cname, "nvlist/descriptor/pipe_rd") == 0); ATF_REQUIRE(fd_is_valid(nvlist_get_descriptor(cnvl, cname))); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_BINARY); ATF_REQUIRE(strcmp(cname, "nvlist/binary/x") == 0); ATF_REQUIRE(memcmp(nvlist_get_binary(cnvl, cname, NULL), "x", 1) == 0); ATF_REQUIRE(memcmp(nvlist_get_binary(cnvl, cname, &size), "x", 1) == 0); ATF_REQUIRE(size == 1); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_BINARY); ATF_REQUIRE(strcmp(cname, "nvlist/binary/" ALPHABET) == 0); ATF_REQUIRE(memcmp(nvlist_get_binary(cnvl, cname, NULL), ALPHABET, sizeof(ALPHABET)) == 0); ATF_REQUIRE(memcmp(nvlist_get_binary(cnvl, cname, &size), ALPHABET, sizeof(ALPHABET)) == 0); ATF_REQUIRE(size == sizeof(ALPHABET)); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname != NULL); ATF_REQUIRE(ctype == NV_TYPE_NVLIST); ATF_REQUIRE(strcmp(cname, "nvlist/nvlist/empty") == 0); empty = nvlist_get_nvlist(cnvl, cname); ATF_REQUIRE(nvlist_empty(empty)); cname = nvlist_next(cnvl, &ctype, &ccookie); ATF_REQUIRE(cname == NULL); name = nvlist_next(nvl, &type, &cookie); ATF_REQUIRE(name == NULL); nvlist_destroy(nvl); } static void nvlist_send_recv__send_nvlist(short sotype) { int socks[2], status; pid_t pid; ATF_REQUIRE(socketpair(PF_UNIX, sotype, 0, socks) == 0); pid = fork(); ATF_REQUIRE(pid >= 0); if (pid == 0) { /* Child. */ (void)close(socks[0]); send_nvlist_child(socks[1]); _exit(0); } (void)close(socks[1]); send_nvlist_parent(socks[0]); ATF_REQUIRE(waitpid(pid, &status, 0) == pid); ATF_REQUIRE(status == 0); } static void nvlist_send_recv__send_closed_fd(short sotype) { nvlist_t *nvl; int socks[2]; ATF_REQUIRE(socketpair(PF_UNIX, sotype, 0, socks) == 0); nvl = nvlist_create(0); ATF_REQUIRE(nvl != NULL); nvlist_add_descriptor(nvl, "fd", 12345); ATF_REQUIRE(nvlist_error(nvl) == EBADF); ATF_REQUIRE_ERRNO(EBADF, nvlist_send(socks[1], nvl) != 0); } static int nopenfds(void) { size_t len; int error, mib[4], n; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_NFDS; mib[3] = 0; len = sizeof(n); error = sysctl(mib, nitems(mib), &n, &len, NULL, 0); if (error != 0) return (-1); return (n); } #define NFDS 512 static void send_many_fds_child(int sock) { char name[16]; nvlist_t *nvl; int anfds, bnfds, fd, i, j; fd = open(_PATH_DEVNULL, O_RDONLY); ATF_REQUIRE(fd >= 0); for (i = 1; i < NFDS; i++) { nvl = nvlist_create(0); bnfds = nopenfds(); if (bnfds == -1) err(EXIT_FAILURE, "sysctl"); for (j = 0; j < i; j++) { snprintf(name, sizeof(name), "fd%d", j); nvlist_add_descriptor(nvl, name, fd); } nvlist_send(sock, nvl); nvlist_destroy(nvl); anfds = nopenfds(); if (anfds == -1) err(EXIT_FAILURE, "sysctl"); if (anfds != bnfds) errx(EXIT_FAILURE, "fd count mismatch"); } } static void nvlist_send_recv__send_many_fds(short sotype) { char name[16]; nvlist_t *nvl; int anfds, bnfds, fd, i, j, socks[2], status; pid_t pid; ATF_REQUIRE(socketpair(PF_UNIX, sotype, 0, socks) == 0); pid = fork(); ATF_REQUIRE(pid >= 0); if (pid == 0) { /* Child. */ (void)close(socks[0]); send_many_fds_child(socks[1]); _exit(0); } (void)close(socks[1]); for (i = 1; i < NFDS; i++) { bnfds = nopenfds(); ATF_REQUIRE(bnfds != -1); nvl = nvlist_recv(socks[0], 0); ATF_REQUIRE(nvl != NULL); for (j = 0; j < i; j++) { snprintf(name, sizeof(name), "fd%d", j); fd = nvlist_take_descriptor(nvl, name); ATF_REQUIRE(close(fd) == 0); } nvlist_destroy(nvl); anfds = nopenfds(); ATF_REQUIRE(anfds != -1); ATF_REQUIRE(anfds == bnfds); } ATF_REQUIRE(waitpid(pid, &status, 0) == pid); ATF_REQUIRE(status == 0); } /* * This test needs to tune the following sysctl's: * net.local.dgram.maxdgram * net.local.dgram.recvspace */ ATF_TC_WITHOUT_HEAD(nvlist_send_recv__send_many_fds__dgram); ATF_TC_BODY(nvlist_send_recv__send_many_fds__dgram, tc) { u_long maxdgram, recvspace, temp_maxdgram, temp_recvspace; size_t len; int error; atf_tc_skip("https://bugs.freebsd.org/260891"); /* size of the largest datagram to send */ temp_maxdgram = 16772; len = sizeof(maxdgram); error = sysctlbyname("net.local.dgram.maxdgram", &maxdgram, &len, &temp_maxdgram, sizeof(temp_maxdgram)); if (error != 0) atf_tc_skip("cannot set net.local.dgram.maxdgram: %s", strerror(errno)); /* * The receive queue fills up quicker than it's being emptied, * bump it to a sufficiently large enough value, 1M. */ temp_recvspace = 1048576; len = sizeof(recvspace); error = sysctlbyname("net.local.dgram.recvspace", &recvspace, &len, &temp_recvspace, sizeof(temp_recvspace)); if (error != 0) atf_tc_skip("cannot set net.local.dgram.recvspace: %s", strerror(errno)); nvlist_send_recv__send_many_fds(SOCK_DGRAM); /* restore original values */ error = sysctlbyname("net.local.dgram.maxdgram", NULL, NULL, &maxdgram, sizeof(maxdgram)); if (error != 0) warn("failed to restore net.local.dgram.maxdgram"); error = sysctlbyname("net.local.dgram.recvspace", NULL, NULL, &recvspace, sizeof(recvspace)); if (error != 0) warn("failed to restore net.local.dgram.recvspace"); } ATF_TC_WITHOUT_HEAD(nvlist_send_recv__send_many_fds__stream); ATF_TC_BODY(nvlist_send_recv__send_many_fds__stream, tc) { nvlist_send_recv__send_many_fds(SOCK_STREAM); } ATF_TC_WITHOUT_HEAD(nvlist_send_recv__send_nvlist__dgram); ATF_TC_BODY(nvlist_send_recv__send_nvlist__dgram, tc) { nvlist_send_recv__send_nvlist(SOCK_DGRAM); } ATF_TC_WITHOUT_HEAD(nvlist_send_recv__send_nvlist__stream); ATF_TC_BODY(nvlist_send_recv__send_nvlist__stream, tc) { nvlist_send_recv__send_nvlist(SOCK_STREAM); } ATF_TC_WITHOUT_HEAD(nvlist_send_recv__send_closed_fd__dgram); ATF_TC_BODY(nvlist_send_recv__send_closed_fd__dgram, tc) { nvlist_send_recv__send_closed_fd(SOCK_DGRAM); } ATF_TC_WITHOUT_HEAD(nvlist_send_recv__send_closed_fd__stream); ATF_TC_BODY(nvlist_send_recv__send_closed_fd__stream, tc) { nvlist_send_recv__send_closed_fd(SOCK_STREAM); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, nvlist_send_recv__send_nvlist__dgram); ATF_TP_ADD_TC(tp, nvlist_send_recv__send_nvlist__stream); ATF_TP_ADD_TC(tp, nvlist_send_recv__send_closed_fd__dgram); ATF_TP_ADD_TC(tp, nvlist_send_recv__send_closed_fd__stream); ATF_TP_ADD_TC(tp, nvlist_send_recv__send_many_fds__dgram); ATF_TP_ADD_TC(tp, nvlist_send_recv__send_many_fds__stream); return (atf_no_error()); } diff --git a/lib/libproc/proc_bkpt.c b/lib/libproc/proc_bkpt.c index 4d11bbc06c67..8649da178bb9 100644 --- a/lib/libproc/proc_bkpt.c +++ b/lib/libproc/proc_bkpt.c @@ -1,263 +1,262 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "_libproc.h" #if defined(__aarch64__) #define AARCH64_BRK 0xd4200000 #define AARCH64_BRK_IMM16_SHIFT 5 #define AARCH64_BRK_IMM16_VAL (0xd << AARCH64_BRK_IMM16_SHIFT) #define BREAKPOINT_INSTR (AARCH64_BRK | AARCH64_BRK_IMM16_VAL) #define BREAKPOINT_INSTR_SZ 4 #elif defined(__amd64__) || defined(__i386__) #define BREAKPOINT_INSTR 0xcc /* int 0x3 */ #define BREAKPOINT_INSTR_SZ 1 #define BREAKPOINT_ADJUST_SZ BREAKPOINT_INSTR_SZ #elif defined(__arm__) #define BREAKPOINT_INSTR 0xe7ffffff /* bkpt */ #define BREAKPOINT_INSTR_SZ 4 #elif defined(__powerpc__) #define BREAKPOINT_INSTR 0x7fe00008 /* trap */ #define BREAKPOINT_INSTR_SZ 4 #elif defined(__riscv) #define BREAKPOINT_INSTR 0x00100073 /* sbreak */ #define BREAKPOINT_INSTR_SZ 4 #else #error "Add support for your architecture" #endif /* * Use 4-bytes holder for breakpoint instruction on all the platforms. * Works for x86 as well until it is endian-little platform. * (We are coping one byte only on x86 from this 4-bytes piece of * memory). */ typedef uint32_t instr_t; static int proc_stop(struct proc_handle *phdl) { int status; if (kill(proc_getpid(phdl), SIGSTOP) == -1) { DPRINTF("kill %d", proc_getpid(phdl)); return (-1); } else if (waitpid(proc_getpid(phdl), &status, WSTOPPED) == -1) { DPRINTF("waitpid %d", proc_getpid(phdl)); return (-1); } else if (!WIFSTOPPED(status)) { DPRINTFX("waitpid: unexpected status 0x%x", status); return (-1); } return (0); } int proc_bkptset(struct proc_handle *phdl, uintptr_t address, unsigned long *saved) { struct ptrace_io_desc piod; int ret = 0, stopped; instr_t instr; *saved = 0; if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || phdl->status == PS_IDLE) { errno = ENOENT; return (-1); } DPRINTFX("adding breakpoint at 0x%lx", (unsigned long)address); stopped = 0; if (phdl->status != PS_STOP) { if (proc_stop(phdl) != 0) return (-1); stopped = 1; } /* * Read the original instruction. */ instr = 0; piod.piod_op = PIOD_READ_I; piod.piod_offs = (void *)address; piod.piod_addr = &instr; piod.piod_len = BREAKPOINT_INSTR_SZ; if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { DPRINTF("ERROR: couldn't read instruction at address 0x%jx", (uintmax_t)address); ret = -1; goto done; } *saved = instr; /* * Write a breakpoint instruction to that address. */ instr = BREAKPOINT_INSTR; piod.piod_op = PIOD_WRITE_I; piod.piod_offs = (void *)address; piod.piod_addr = &instr; piod.piod_len = BREAKPOINT_INSTR_SZ; if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { DPRINTF("ERROR: couldn't write instruction at address 0x%jx", (uintmax_t)address); ret = -1; goto done; } done: if (stopped) /* Restart the process if we had to stop it. */ proc_continue(phdl); return (ret); } int proc_bkptdel(struct proc_handle *phdl, uintptr_t address, unsigned long saved) { struct ptrace_io_desc piod; int ret = 0, stopped; instr_t instr; if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || phdl->status == PS_IDLE) { errno = ENOENT; return (-1); } DPRINTFX("removing breakpoint at 0x%lx", (unsigned long)address); stopped = 0; if (phdl->status != PS_STOP) { if (proc_stop(phdl) != 0) return (-1); stopped = 1; } /* * Overwrite the breakpoint instruction that we setup previously. */ instr = saved; piod.piod_op = PIOD_WRITE_I; piod.piod_offs = (void *)address; piod.piod_addr = &instr; piod.piod_len = BREAKPOINT_INSTR_SZ; if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { DPRINTF("ERROR: couldn't write instruction at address 0x%jx", (uintmax_t)address); ret = -1; } if (stopped) /* Restart the process if we had to stop it. */ proc_continue(phdl); return (ret); } /* * Decrement pc so that we delete the breakpoint at the correct * address, i.e. at the BREAKPOINT_INSTR address. * * This is only needed on some architectures where the pc value * when reading registers points at the instruction after the * breakpoint, e.g. x86. */ void proc_bkptregadj(unsigned long *pc) { (void)pc; #ifdef BREAKPOINT_ADJUST_SZ *pc = *pc - BREAKPOINT_ADJUST_SZ; #endif } /* * Step over the breakpoint. */ int proc_bkptexec(struct proc_handle *phdl, unsigned long saved) { unsigned long pc; unsigned long samesaved; int status; if (proc_regget(phdl, REG_PC, &pc) < 0) { DPRINTFX("ERROR: couldn't get PC register"); return (-1); } proc_bkptregadj(&pc); if (proc_bkptdel(phdl, pc, saved) < 0) { DPRINTFX("ERROR: couldn't delete breakpoint"); return (-1); } /* * Go back in time and step over the new instruction just * set up by proc_bkptdel(). */ proc_regset(phdl, REG_PC, pc); if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) { DPRINTFX("ERROR: ptrace step failed"); return (-1); } proc_wstatus(phdl); status = proc_getwstat(phdl); if (!WIFSTOPPED(status)) { DPRINTFX("ERROR: don't know why process stopped"); return (-1); } /* * Restore the breakpoint. The saved instruction should be * the same as the one that we were passed in. */ if (proc_bkptset(phdl, pc, &samesaved) < 0) { DPRINTFX("ERROR: couldn't restore breakpoint"); return (-1); } assert(samesaved == saved); return (0); } diff --git a/lib/libproc/proc_regs.c b/lib/libproc/proc_regs.c index df4bf19c3512..8d41233ec108 100644 --- a/lib/libproc/proc_regs.c +++ b/lib/libproc/proc_regs.c @@ -1,145 +1,144 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include "_libproc.h" int proc_regget(struct proc_handle *phdl, proc_reg_t reg, unsigned long *regvalue) { struct reg regs; if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || phdl->status == PS_IDLE) { errno = ENOENT; return (-1); } memset(®s, 0, sizeof(regs)); if (ptrace(PT_GETREGS, proc_getpid(phdl), (caddr_t)®s, 0) < 0) return (-1); switch (reg) { case REG_PC: #if defined(__aarch64__) *regvalue = regs.elr; #elif defined(__amd64__) *regvalue = regs.r_rip; #elif defined(__arm__) *regvalue = regs.r_pc; #elif defined(__i386__) *regvalue = regs.r_eip; #elif defined(__powerpc__) *regvalue = regs.pc; #elif defined(__riscv) *regvalue = regs.sepc; #endif break; case REG_SP: #if defined(__aarch64__) *regvalue = regs.sp; #elif defined(__amd64__) *regvalue = regs.r_rsp; #elif defined(__arm__) *regvalue = regs.r_sp; #elif defined(__i386__) *regvalue = regs.r_esp; #elif defined(__powerpc__) *regvalue = regs.fixreg[1]; #elif defined(__riscv) *regvalue = regs.sp; #endif break; default: DPRINTFX("ERROR: no support for reg number %d", reg); return (-1); } return (0); } int proc_regset(struct proc_handle *phdl, proc_reg_t reg, unsigned long regvalue) { struct reg regs; if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || phdl->status == PS_IDLE) { errno = ENOENT; return (-1); } if (ptrace(PT_GETREGS, proc_getpid(phdl), (caddr_t)®s, 0) < 0) return (-1); switch (reg) { case REG_PC: #if defined(__aarch64__) regs.elr = regvalue; #elif defined(__amd64__) regs.r_rip = regvalue; #elif defined(__arm__) regs.r_pc = regvalue; #elif defined(__i386__) regs.r_eip = regvalue; #elif defined(__powerpc__) regs.pc = regvalue; #elif defined(__riscv) regs.sepc = regvalue; #endif break; case REG_SP: #if defined(__aarch64__) regs.sp = regvalue; #elif defined(__amd64__) regs.r_rsp = regvalue; #elif defined(__arm__) regs.r_sp = regvalue; #elif defined(__i386__) regs.r_esp = regvalue; #elif defined(__powerpc__) regs.fixreg[1] = regvalue; #elif defined(__riscv) regs.sp = regvalue; #endif break; default: DPRINTFX("ERROR: no support for reg number %d", reg); return (-1); } if (ptrace(PT_SETREGS, proc_getpid(phdl), (caddr_t)®s, 0) < 0) return (-1); return (0); } diff --git a/lib/libproc/proc_rtld.c b/lib/libproc/proc_rtld.c index db17c2b02b95..1d6fc732933a 100644 --- a/lib/libproc/proc_rtld.c +++ b/lib/libproc/proc_rtld.c @@ -1,136 +1,135 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include "_libproc.h" static void rdl2prmap(const rd_loadobj_t *, prmap_t *); static int map_iter(const rd_loadobj_t *lop, void *arg) { struct file_info *file; struct map_info *mapping, *tmp; struct proc_handle *phdl; size_t i; phdl = arg; if (phdl->nmappings >= phdl->maparrsz) { phdl->maparrsz *= 2; tmp = reallocarray(phdl->mappings, phdl->maparrsz, sizeof(*phdl->mappings)); if (tmp == NULL) return (-1); phdl->mappings = tmp; } mapping = &phdl->mappings[phdl->nmappings]; rdl2prmap(lop, &mapping->map); if (strcmp(lop->rdl_path, phdl->execpath) == 0 && (lop->rdl_prot & RD_RDL_X) != 0) phdl->exec_map = phdl->nmappings; file = NULL; if (lop->rdl_path[0] != '\0') { /* Look for an existing mapping of the same file. */ for (i = 0; i < phdl->nmappings; i++) if (strcmp(mapping->map.pr_mapname, phdl->mappings[i].map.pr_mapname) == 0) { file = phdl->mappings[i].file; break; } if (file == NULL) { file = malloc(sizeof(*file)); if (file == NULL) return (-1); file->elf = NULL; file->fd = -1; file->refs = 1; } else file->refs++; } mapping->file = file; phdl->nmappings++; return (0); } static void rdl2prmap(const rd_loadobj_t *rdl, prmap_t *map) { map->pr_vaddr = rdl->rdl_saddr; map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr; map->pr_offset = rdl->rdl_offset; map->pr_mflags = 0; if (rdl->rdl_prot & RD_RDL_R) map->pr_mflags |= MA_READ; if (rdl->rdl_prot & RD_RDL_W) map->pr_mflags |= MA_WRITE; if (rdl->rdl_prot & RD_RDL_X) map->pr_mflags |= MA_EXEC; (void)strlcpy(map->pr_mapname, rdl->rdl_path, sizeof(map->pr_mapname)); } rd_agent_t * proc_rdagent(struct proc_handle *phdl) { if (phdl->rdap == NULL && phdl->status != PS_UNDEAD && phdl->status != PS_IDLE) { if ((phdl->rdap = rd_new(phdl)) == NULL) return (NULL); phdl->maparrsz = 64; phdl->mappings = calloc(phdl->maparrsz, sizeof(*phdl->mappings)); if (phdl->mappings == NULL) return (phdl->rdap); if (rd_loadobj_iter(phdl->rdap, map_iter, phdl) != RD_OK) return (NULL); } return (phdl->rdap); } void proc_updatesyms(struct proc_handle *phdl) { memset(phdl->mappings, 0, sizeof(*phdl->mappings) * phdl->maparrsz); rd_loadobj_iter(phdl->rdap, map_iter, phdl); } diff --git a/lib/librtld_db/librtld_db.3 b/lib/librtld_db/librtld_db.3 index 041cc2cfa67b..07e1b67c096b 100644 --- a/lib/librtld_db/librtld_db.3 +++ b/lib/librtld_db/librtld_db.3 @@ -1,189 +1,188 @@ .\"- .\" Copyright (c) 2010 The FreeBSD Foundation -.\" All rights reserved. .\" .\" This software was developed by Rui Paulo under sponsorship from .\" the FreeBSD Foundation. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd June 10, 2010 .Dt LIBRTLD_DB 3 .Os .Sh NAME .Nm librtld_db .Nd library for run-time linker debugging .Sh LIBRARY .Lb librtld_db .Sh SYNOPSIS .In rtld_db.h .Ft void .Fo rd_delete .Fa "rd_agent_t *rdap" .Fc .Ft char * .Fo rd_errstr .Fa "rd_err_e rderr" .Fc .Ft rd_err_e .Fo rd_event_addr .Fa "rd_agent_t *rdap, rd_event_e event, rd_notify_t *notify" .Fc .Ft rd_err_e .Fo rd_event_enable .Fa "rd_agent_t *rdap, int onoff" .Fc .Ft rd_err_e .Fo rd_event_getmsg .Fa "rd_agent_t *rdap, rd_event_msg_t *msg" .Fc .Ft rd_err_e .Fo rd_init .Fa "int version" .Fc .Ft typedef int .Fo rl_iter_f .Fa "const rd_loadobj_t *, void *" .Fc .Ft rd_err_e .Fo rd_loadobj_iter .Fa "rd_agent_t *rdap, rl_iter_f *cb, void *clnt_data" .Fc .Ft void .Fo rd_log .Fa "const int onoff" .Fc .Ft rd_agent_t * .Fo rd_new .Fa "struct proc_handle *php" .Fc .Ft rd_err_e .Fo rd_objpad_enable .Fa "rd_agent_t *rdap, size_t padsize" .Fc .Ft rd_err_e .Fo rd_plt_resolution .Fa "rd_agent_t *rdap, uintptr_t pc, struct proc *proc" .Fa "uintptr_t plt_base, rd_plt_info_t *rpi" .Fc .Ft rd_err_e .Fo rd_reset .Fa "rd_agent_t *rdap" .Fc .Sh DESCRIPTION The .Nm librtld_db library provides a debugging interface to the run-time linker (rtld). This library must be used along with .Xr libproc 3 . .Pp Most library functions take a .Ft rd_agent_t argument. This argument is an opaque structure containing information associated with the current status of the agent. .Pp Before you start using .Nm you should call .Fn rd_init with the .Ft RD_VERSION argument. This initializes the library to the correct version your program was compiled with and provides proper ABI stability. .Pp What follows is a description of what each function. .Pp .Fn rd_new creates a new .Nm agent. The .Ft php argument should be the .Ft proc_handle you received from .Xr libproc 3 . .Pp .Fn rd_reset resets your previously created agent. .Pp .Fn rd_delete deallocates the resources associated with the agent. .Pp .Fn rd_errstr returns an error string describing the error present in .Ft rderr . .Pp .Fn rd_event_enable enables reporting of events. This function always returns RD_OK. .Pp .Fn rd_event_addr returns the event address corresponding to the .Ft event parameter. At the moment we only report events of type RD_NOTIFY_BPT. .Pp .Fn rd_event_getmsg returns the message associated with the latest event. At the moment only RD_POSTINIT events are supported. .Pp .Fn rd_loadobj_iter allows you to iterate over the program's loaded objects. .Ft cb is a callback of type .Fn rl_iter_f . .Sh RETURN VALUES Most functions return an .Ft rd_err_e type error. The error codes are described in the header file for this library. You can get the error string using .Fn rd_errstr . .Sh SEE ALSO .Xr ld 1 , .Xr ld-elf.so.1 1 , .Xr ld.so 1 , .Xr rtld 1 , .Xr libproc 3 .Sh HISTORY The .Nm librtld_db library first appeared in .Fx 9.0 and was modeled after the same library present in the Solaris operating system. .Sh AUTHORS The .Nm librtld_db library and this manual page were written by .An Rui Paulo Aq Mt rpaulo@FreeBSD.org under sponsorship from the FreeBSD Foundation. .Sh CAVEATS The functions .Fn rd_event_enable , .Fn rd_log , .Fn rd_objpad_enable and .Fn rd_plt_resolution are not yet implemented. diff --git a/lib/librtld_db/rtld_db.c b/lib/librtld_db/rtld_db.c index 0c2130db973f..0d2adf88e089 100644 --- a/lib/librtld_db/rtld_db.c +++ b/lib/librtld_db/rtld_db.c @@ -1,406 +1,405 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtld_db.h" static int _librtld_db_debug = 0; #define DPRINTF(...) do { \ if (_librtld_db_debug) { \ fprintf(stderr, "librtld_db: DEBUG: "); \ fprintf(stderr, __VA_ARGS__); \ } \ } while (0) void rd_delete(rd_agent_t *rdap) { if (rdap->rda_procstat != NULL) procstat_close(rdap->rda_procstat); free(rdap); } const char * rd_errstr(rd_err_e rderr) { switch (rderr) { case RD_ERR: return "generic error"; case RD_OK: return "no error"; case RD_NOCAPAB: return "capability not supported"; case RD_DBERR: return "database error"; case RD_NOBASE: return "NOBASE"; case RD_NOMAPS: return "NOMAPS"; default: return "unknown error"; } } rd_err_e rd_event_addr(rd_agent_t *rdap, rd_event_e event, rd_notify_t *notify) { rd_err_e ret; DPRINTF("%s rdap %p event %d notify %p\n", __func__, rdap, event, notify); ret = RD_OK; switch (event) { case RD_NONE: break; case RD_PREINIT: notify->type = RD_NOTIFY_BPT; notify->u.bptaddr = rdap->rda_preinit_addr; break; case RD_POSTINIT: notify->type = RD_NOTIFY_BPT; notify->u.bptaddr = rdap->rda_postinit_addr; break; case RD_DLACTIVITY: notify->type = RD_NOTIFY_BPT; notify->u.bptaddr = rdap->rda_dlactivity_addr; break; default: ret = RD_ERR; break; } return (ret); } rd_err_e rd_event_enable(rd_agent_t *rdap __unused, int onoff) { DPRINTF("%s onoff %d\n", __func__, onoff); return (RD_OK); } rd_err_e rd_event_getmsg(rd_agent_t *rdap __unused, rd_event_msg_t *msg) { DPRINTF("%s\n", __func__); msg->type = RD_POSTINIT; msg->u.state = RD_CONSISTENT; return (RD_OK); } rd_err_e rd_init(int version) { char *debug = NULL; if (version == RD_VERSION) { debug = getenv("LIBRTLD_DB_DEBUG"); _librtld_db_debug = debug ? atoi(debug) : 0; return (RD_OK); } else return (RD_NOCAPAB); } rd_err_e rd_loadobj_iter(rd_agent_t *rdap, rl_iter_f *cb, void *clnt_data) { struct kinfo_vmentry *kves, *kve; const char *path; uint64_t fileid; rd_loadobj_t rdl; rd_err_e ret; uintptr_t base; uint32_t offset; int cnt, i; DPRINTF("%s\n", __func__); if ((kves = kinfo_getvmmap(proc_getpid(rdap->rda_php), &cnt)) == NULL) { warn("ERROR: kinfo_getvmmap() failed"); return (RD_ERR); } base = 0; fileid = 0; path = NULL; ret = RD_OK; for (i = 0; i < cnt; i++) { kve = &kves[i]; /* * Cache the base offset of the file mapping. The kve_offset * field gives the file offset of a particular mapping into the * file, but we want the mapping offset relative to the base * mapping. */ if (kve->kve_type == KVME_TYPE_VNODE) { if (kve->kve_vn_fileid != fileid) { base = kve->kve_start; fileid = kve->kve_vn_fileid; } path = kve->kve_path; offset = kve->kve_start - base; } else { path = NULL; offset = 0; } memset(&rdl, 0, sizeof(rdl)); /* * Map the kinfo_vmentry struct to the rd_loadobj structure. */ rdl.rdl_saddr = kve->kve_start; rdl.rdl_eaddr = kve->kve_end; rdl.rdl_offset = offset; if (kve->kve_protection & KVME_PROT_READ) rdl.rdl_prot |= RD_RDL_R; if (kve->kve_protection & KVME_PROT_WRITE) rdl.rdl_prot |= RD_RDL_W; if (kve->kve_protection & KVME_PROT_EXEC) rdl.rdl_prot |= RD_RDL_X; if (path != NULL) strlcpy(rdl.rdl_path, path, sizeof(rdl.rdl_path)); if ((*cb)(&rdl, clnt_data) != 0) { ret = RD_ERR; break; } } free(kves); return (ret); } void rd_log(const int onoff) { DPRINTF("%s\n", __func__); (void)onoff; } rd_agent_t * rd_new(struct proc_handle *php) { rd_agent_t *rdap; rdap = malloc(sizeof(*rdap)); if (rdap == NULL) return (NULL); memset(rdap, 0, sizeof(rd_agent_t)); rdap->rda_php = php; rdap->rda_procstat = procstat_open_sysctl(); if (rd_reset(rdap) != RD_OK) { rd_delete(rdap); rdap = NULL; } return (rdap); } rd_err_e rd_objpad_enable(rd_agent_t *rdap, size_t padsize) { DPRINTF("%s\n", __func__); (void)rdap; (void)padsize; return (RD_ERR); } rd_err_e rd_plt_resolution(rd_agent_t *rdap, uintptr_t pc, struct proc *proc, uintptr_t plt_base, rd_plt_info_t *rpi) { DPRINTF("%s\n", __func__); (void)rdap; (void)pc; (void)proc; (void)plt_base; (void)rpi; return (RD_ERR); } static int rtld_syms(rd_agent_t *rdap, const char *rtldpath, u_long base) { GElf_Shdr shdr; GElf_Sym sym; Elf *e; Elf_Data *data; Elf_Scn *scn; const char *symname; Elf64_Word strscnidx; int fd, i, ret; ret = 1; e = NULL; fd = open(rtldpath, O_RDONLY); if (fd < 0) goto err; if (elf_version(EV_CURRENT) == EV_NONE) goto err; e = elf_begin(fd, ELF_C_READ, NULL); if (e == NULL) goto err; scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { gelf_getshdr(scn, &shdr); if (shdr.sh_type == SHT_DYNSYM) break; } if (scn == NULL) goto err; strscnidx = shdr.sh_link; data = elf_getdata(scn, NULL); if (data == NULL) goto err; for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { if (GELF_ST_TYPE(sym.st_info) != STT_FUNC || GELF_ST_BIND(sym.st_info) != STB_GLOBAL) continue; symname = elf_strptr(e, strscnidx, sym.st_name); if (symname == NULL) continue; if (strcmp(symname, "r_debug_state") == 0) { rdap->rda_preinit_addr = sym.st_value + base; rdap->rda_dlactivity_addr = sym.st_value + base; } else if (strcmp(symname, "_r_debug_postinit") == 0) { rdap->rda_postinit_addr = sym.st_value + base; } } if (rdap->rda_preinit_addr != 0 && rdap->rda_postinit_addr != 0 && rdap->rda_dlactivity_addr != 0) ret = 0; err: if (e != NULL) (void)elf_end(e); if (fd >= 0) (void)close(fd); return (ret); } rd_err_e rd_reset(rd_agent_t *rdap) { struct kinfo_proc *kp; struct kinfo_vmentry *kve; Elf_Auxinfo *auxv; const char *rtldpath; u_long base; rd_err_e rderr; int count, i; kp = NULL; auxv = NULL; kve = NULL; rderr = RD_ERR; kp = procstat_getprocs(rdap->rda_procstat, KERN_PROC_PID, proc_getpid(rdap->rda_php), &count); if (kp == NULL) return (RD_ERR); assert(count == 1); auxv = procstat_getauxv(rdap->rda_procstat, kp, &count); if (auxv == NULL) goto err; base = 0; for (i = 0; i < count; i++) { if (auxv[i].a_type == AT_BASE) { base = auxv[i].a_un.a_val; break; } } if (i == count) goto err; rtldpath = NULL; kve = procstat_getvmmap(rdap->rda_procstat, kp, &count); if (kve == NULL) goto err; for (i = 0; i < count; i++) { if (kve[i].kve_start == base) { rtldpath = kve[i].kve_path; break; } } if (i == count) goto err; if (rtld_syms(rdap, rtldpath, base) != 0) goto err; rderr = RD_OK; err: if (kve != NULL) procstat_freevmmap(rdap->rda_procstat, kve); if (auxv != NULL) procstat_freeauxv(rdap->rda_procstat, auxv); if (kp != NULL) procstat_freeprocs(rdap->rda_procstat, kp); return (rderr); } diff --git a/lib/librtld_db/rtld_db.h b/lib/librtld_db/rtld_db.h index 24dc18a9a0ba..41aa7409fbf3 100644 --- a/lib/librtld_db/rtld_db.h +++ b/lib/librtld_db/rtld_db.h @@ -1,155 +1,154 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _RTLD_DB_H_ #define _RTLD_DB_H_ #include #define RD_VERSION 1 typedef enum { RD_OK, RD_ERR, RD_DBERR, RD_NOCAPAB, RD_NODYNAM, RD_NOBASE, RD_NOMAPS } rd_err_e; /* XXX struct rd_agent should be private. */ struct procstat; typedef struct rd_agent { struct proc_handle *rda_php; uintptr_t rda_dlactivity_addr; uintptr_t rda_preinit_addr; uintptr_t rda_postinit_addr; struct procstat *rda_procstat; } rd_agent_t; typedef struct rd_loadobj { uintptr_t rdl_saddr; /* start address */ uintptr_t rdl_eaddr; /* end address */ uint32_t rdl_offset; uint8_t rdl_prot; #define RD_RDL_R 0x01 #define RD_RDL_W 0x02 #define RD_RDL_X 0x04 enum { RDL_TYPE_NONE = 0, RDL_TYPE_DEF, RDL_TYPE_VNODE, RDL_TYPE_SWAP, RDL_TYPE_DEV, /* XXX some types missing */ RDL_TYPE_UNKNOWN = 255 } rdl_type; unsigned char rdl_path[PATH_MAX]; } rd_loadobj_t; typedef enum { RD_NONE = 0, RD_PREINIT, RD_POSTINIT, RD_DLACTIVITY } rd_event_e; typedef enum { RD_NOTIFY_BPT, RD_NOTIFY_AUTOBPT, RD_NOTIFY_SYSCALL } rd_notify_e; typedef struct rd_notify { rd_notify_e type; union { uintptr_t bptaddr; long syscallno; } u; } rd_notify_t; typedef enum { RD_NOSTATE = 0, RD_CONSISTENT, RD_ADD, RD_DELETE } rd_state_e; typedef struct rd_event_msg { rd_event_e type; union { rd_state_e state; } u; } rd_event_msg_t; typedef enum { RD_RESOLVE_NONE, RD_RESOLVE_STEP, RD_RESOLVE_TARGET, RD_RESOLVE_TARGET_STEP } rd_skip_e; typedef struct rd_plt_info { rd_skip_e pi_skip_method; long pi_nstep; uintptr_t pi_target; uintptr_t pi_baddr; unsigned int pi_flags; } rd_plt_info_t; #define RD_FLG_PI_PLTBOUND 0x0001 __BEGIN_DECLS struct proc_handle; void rd_delete(rd_agent_t *); const char *rd_errstr(rd_err_e); rd_err_e rd_event_addr(rd_agent_t *, rd_event_e, rd_notify_t *); rd_err_e rd_event_enable(rd_agent_t *, int); rd_err_e rd_event_getmsg(rd_agent_t *, rd_event_msg_t *); rd_err_e rd_init(int); typedef int rl_iter_f(const rd_loadobj_t *, void *); rd_err_e rd_loadobj_iter(rd_agent_t *, rl_iter_f *, void *); void rd_log(const int); rd_agent_t *rd_new(struct proc_handle *); rd_err_e rd_objpad_enable(rd_agent_t *, size_t); struct proc; rd_err_e rd_plt_resolution(rd_agent_t *, uintptr_t, struct proc *, uintptr_t, rd_plt_info_t *); rd_err_e rd_reset(rd_agent_t *); __END_DECLS #endif /* _RTLD_DB_H_ */ diff --git a/lib/libthr/plockstat.d b/lib/libthr/plockstat.d index de620d893651..39d8946f33d3 100644 --- a/lib/libthr/plockstat.d +++ b/lib/libthr/plockstat.d @@ -1,44 +1,43 @@ /* * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Rui Paulo under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ provider plockstat { probe mutex__acquire(void *mutex, int rec, int spincount); probe mutex__release(void *mutex, int rec); probe mutex__block(void *mutex); probe mutex__spin(void *mutex); probe mutex__spun(void *mutex, int success, int spincount); probe mutex__blocked(void *mutex, int success); probe mutex__error(void *mutex, int err); probe rw__acquire(void *lock, int wr); probe rw__release(void *lock, int wr); probe rw__block(void *lock, int wr); probe rw__blocked(void *lock, int wr, int success); probe rw__error(void *lock, int wr, int err); }; diff --git a/lib/libthr/thread/thr_malloc.c b/lib/libthr/thread/thr_malloc.c index 64ceb862c0ee..9a81d6d1bd6b 100644 --- a/lib/libthr/thread/thr_malloc.c +++ b/lib/libthr/thread/thr_malloc.c @@ -1,148 +1,147 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include "thr_private.h" int npagesizes; size_t *pagesizes; static size_t pagesizes_d[2]; static struct umutex thr_malloc_umtx; static u_int thr_malloc_umtx_level; void __thr_malloc_init(void) { if (npagesizes != 0) return; npagesizes = getpagesizes(pagesizes_d, nitems(pagesizes_d)); if (npagesizes == -1) { PANIC("Unable to read page sizes"); } pagesizes = pagesizes_d; _thr_umutex_init(&thr_malloc_umtx); } static void thr_malloc_lock(struct pthread *curthread) { uint32_t curtid; if (curthread == NULL) return; curthread->locklevel++; curtid = TID(curthread); if ((uint32_t)thr_malloc_umtx.m_owner == curtid) thr_malloc_umtx_level++; else _thr_umutex_lock(&thr_malloc_umtx, curtid); } static void thr_malloc_unlock(struct pthread *curthread) { if (curthread == NULL) return; if (thr_malloc_umtx_level > 0) thr_malloc_umtx_level--; else _thr_umutex_unlock(&thr_malloc_umtx, TID(curthread)); curthread->locklevel--; _thr_ast(curthread); } void * __thr_calloc(size_t num, size_t size) { struct pthread *curthread; void *res; curthread = _get_curthread(); thr_malloc_lock(curthread); res = __crt_calloc(num, size); thr_malloc_unlock(curthread); return (res); } void __thr_free(void *cp) { struct pthread *curthread; curthread = _get_curthread(); thr_malloc_lock(curthread); __crt_free(cp); thr_malloc_unlock(curthread); } void * __thr_malloc(size_t nbytes) { struct pthread *curthread; void *res; curthread = _get_curthread(); thr_malloc_lock(curthread); res = __crt_malloc(nbytes); thr_malloc_unlock(curthread); return (res); } void * __thr_realloc(void *cp, size_t nbytes) { struct pthread *curthread; void *res; curthread = _get_curthread(); thr_malloc_lock(curthread); res = __crt_realloc(cp, nbytes); thr_malloc_unlock(curthread); return (res); } void __thr_malloc_prefork(struct pthread *curthread) { _thr_umutex_lock(&thr_malloc_umtx, TID(curthread)); } void __thr_malloc_postfork(struct pthread *curthread) { _thr_umutex_unlock(&thr_malloc_umtx, TID(curthread)); } diff --git a/lib/libthread_db/arch/aarch64/libpthread_md.c b/lib/libthread_db/arch/aarch64/libpthread_md.c index 2536107b508f..1ff08e2804fa 100644 --- a/lib/libthread_db/arch/aarch64/libpthread_md.c +++ b/lib/libthread_db/arch/aarch64/libpthread_md.c @@ -1,93 +1,92 @@ /*- * Copyright (c) 2014-2015 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include "libpthread_db.h" void abort(void); void pt_reg_to_ucontext(const struct reg *r, ucontext_t *uc) { mcontext_t *mc = &uc->uc_mcontext; memcpy(mc->mc_gpregs.gp_x, r->x, sizeof(mc->mc_gpregs.gp_x)); mc->mc_gpregs.gp_sp = r->sp; mc->mc_gpregs.gp_lr = r->lr; mc->mc_gpregs.gp_elr = r->elr; mc->mc_gpregs.gp_spsr = r->spsr; } void pt_ucontext_to_reg(const ucontext_t *uc, struct reg *r) { const mcontext_t *mc = &uc->uc_mcontext; memcpy(r->x, mc->mc_gpregs.gp_x, sizeof(mc->mc_gpregs.gp_x)); r->sp = mc->mc_gpregs.gp_sp; r->lr = mc->mc_gpregs.gp_lr; r->elr = mc->mc_gpregs.gp_elr; r->spsr = mc->mc_gpregs.gp_spsr; } void pt_fpreg_to_ucontext(const struct fpreg *r, ucontext_t *uc) { mcontext_t *mc = &uc->uc_mcontext; memcpy(&mc->mc_fpregs, r, sizeof(*r)); mc->mc_flags |= _MC_FP_VALID; } void pt_ucontext_to_fpreg(const ucontext_t *uc, struct fpreg *r) { const mcontext_t *mc = &uc->uc_mcontext; if (mc->mc_flags & _MC_FP_VALID) memcpy(r, &mc->mc_fpregs, sizeof(*r)); else memset(r, 0, sizeof(*r)); } void pt_md_init(void) { } int pt_reg_sstep(struct reg *reg __unused, int step __unused) { return (0); } diff --git a/libexec/ftpd/blacklist.c b/libexec/ftpd/blacklist.c index e8954f11bbe7..0a45f9369074 100644 --- a/libexec/ftpd/blacklist.c +++ b/libexec/ftpd/blacklist.c @@ -1,56 +1,55 @@ /*- * Copyright (c) 2016 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Kurt Lidl under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include "blacklist_client.h" static struct blacklist *blstate; extern int use_blacklist; void blacklist_init(void) { if (use_blacklist) blstate = blacklist_open(); } void blacklist_notify(int action, int fd, const char *msg) { if (blstate == NULL) return; (void)blacklist_r(blstate, action, fd, msg); } diff --git a/libexec/ftpd/blacklist_client.h b/libexec/ftpd/blacklist_client.h index 94ecc66c17ce..0b6805dc218e 100644 --- a/libexec/ftpd/blacklist_client.h +++ b/libexec/ftpd/blacklist_client.h @@ -1,54 +1,53 @@ /*- * Copyright (c) 2016 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Kurt Lidl under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef BLACKLIST_CLIENT_H #define BLACKLIST_CLIENT_H #ifndef BLACKLIST_API_ENUM enum { BLACKLIST_AUTH_OK = 0, BLACKLIST_AUTH_FAIL }; #endif #ifdef USE_BLACKLIST void blacklist_init(void); void blacklist_notify(int, int, const char *); #define BLACKLIST_INIT() blacklist_init() #define BLACKLIST_NOTIFY(x, y, z) blacklist_notify(x, y, z) #else #define BLACKLIST_INIT() #define BLACKLIST_NOTIFY(x, y, z) #endif #endif /* BLACKLIST_CLIENT_H */ diff --git a/libexec/rc/rc.d/blacklistd b/libexec/rc/rc.d/blacklistd index ecbb71e41fca..5248b0ea3580 100755 --- a/libexec/rc/rc.d/blacklistd +++ b/libexec/rc/rc.d/blacklistd @@ -1,47 +1,46 @@ #!/bin/sh # # Copyright (c) 2016 The FreeBSD Foundation -# All rights reserved. # # This software was developed by Kurt Lidl under sponsorship from the # FreeBSD Foundation. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # # PROVIDE: blacklistd # REQUIRE: netif pf . /etc/rc.subr name="blacklistd" desc="System blacklist daemon" rcvar="blacklistd_enable" command="/usr/sbin/${name}" required_files="/etc/blacklistd.conf" # no svcj options needed : ${blacklistd_svcj_options:=""} load_rc_config $name run_rc_command "$1" diff --git a/libexec/rtld-elf/aarch64/reloc.c b/libexec/rtld-elf/aarch64/reloc.c index 5dfe19e3d4f4..f76ce7f4d580 100644 --- a/libexec/rtld-elf/aarch64/reloc.c +++ b/libexec/rtld-elf/aarch64/reloc.c @@ -1,637 +1,636 @@ /*- * Copyright (c) 2014-2015 The FreeBSD Foundation - * All rights reserved. * * Portions of this software were developed by Andrew Turner * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include "debug.h" #include "rtld.h" #include "rtld_printf.h" /* * It is possible for the compiler to emit relocations for unaligned data. * We handle this situation with these inlines. */ #define RELOC_ALIGNED_P(x) \ (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) /* * This is not the correct prototype, but we only need it for * a function pointer to a simple asm function. */ void *_rtld_tlsdesc_static(void *); void *_rtld_tlsdesc_undef(void *); void *_rtld_tlsdesc_dynamic(void *); void _exit(int); bool arch_digest_dynamic(struct Struct_Obj_Entry *obj, const Elf_Dyn *dynp) { if (dynp->d_tag == DT_AARCH64_VARIANT_PCS) { obj->variant_pcs = true; return (true); } return (false); } bool arch_digest_note(struct Struct_Obj_Entry *obj __unused, const Elf_Note *note) { const char *note_name; const uint32_t *note_data; note_name = (const char *)(note + 1); /* Only handle GNU notes */ if (note->n_namesz != sizeof(ELF_NOTE_GNU) || strncmp(note_name, ELF_NOTE_GNU, sizeof(ELF_NOTE_GNU)) != 0) return (false); /* Only handle GNU property notes */ if (note->n_type != NT_GNU_PROPERTY_TYPE_0) return (false); /* * note_data[0] - Type * note_data[1] - Length * note_data[2] - Data * note_data[3] - Padding? */ note_data = (const uint32_t *)(note_name + note->n_namesz); /* Only handle AArch64 feature notes */ if (note_data[0] != GNU_PROPERTY_AARCH64_FEATURE_1_AND) return (false); /* We expect at least 4 bytes of data */ if (note_data[1] < 4) return (false); /* TODO: Only guard if HWCAP2_BTI is set */ if ((note_data[2] & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) != 0) { struct arm64_guard_page_args guard; guard.addr = (uintptr_t)obj->mapbase; guard.len = obj->mapsize; sysarch(ARM64_GUARD_PAGE, &guard); } return (true); } void init_pltgot(Obj_Entry *obj) { if (obj->pltgot != NULL) { obj->pltgot[1] = (Elf_Addr) obj; obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; } } int do_copy_relocations(Obj_Entry *dstobj) { const Obj_Entry *srcobj, *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *srcsym; const Elf_Sym *dstsym; const void *srcaddr; const char *name; void *dstaddr; SymLook req; size_t size; int res; /* * COPY relocs are invalid outside of the main program */ assert(dstobj->mainprog); relalim = (const Elf_Rela *)((const char *)dstobj->rela + dstobj->relasize); for (rela = dstobj->rela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) != R_AARCH64_COPY) continue; dstaddr = (void *)(dstobj->relocbase + rela->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); name = dstobj->strtab + dstsym->st_name; size = dstsym->st_size; symlook_init(&req, name); req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); req.flags = SYMLOOK_EARLY; for (srcobj = globallist_next(dstobj); srcobj != NULL; srcobj = globallist_next(srcobj)) { res = symlook_obj(&req, srcobj); if (res == 0) { srcsym = req.sym_out; defobj = req.defobj_out; break; } } if (srcobj == NULL) { _rtld_error("Undefined symbol \"%s\" referenced from " "COPY relocation in %s", name, dstobj->path); return (-1); } srcaddr = (const void *)(defobj->relocbase + srcsym->st_value); memcpy(dstaddr, srcaddr, size); } return (0); } struct tls_data { Elf_Addr dtv_gen; int tls_index; Elf_Addr tls_offs; }; static struct tls_data * reloc_tlsdesc_alloc(int tlsindex, Elf_Addr tlsoffs) { struct tls_data *tlsdesc; tlsdesc = xmalloc(sizeof(struct tls_data)); tlsdesc->dtv_gen = tls_dtv_generation; tlsdesc->tls_index = tlsindex; tlsdesc->tls_offs = tlsoffs; return (tlsdesc); } struct tlsdesc_entry { void *(*func)(void *); union { Elf_Ssize addend; Elf_Size offset; struct tls_data *data; }; }; static void reloc_tlsdesc(const Obj_Entry *obj, const Elf_Rela *rela, struct tlsdesc_entry *where, int flags, RtldLockState *lockstate) { const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr offs; offs = 0; if (ELF_R_SYM(rela->r_info) != 0) { def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, NULL, lockstate); if (def == NULL) rtld_die(); offs = def->st_value; obj = defobj; if (def->st_shndx == SHN_UNDEF) { /* Weak undefined thread variable */ where->func = _rtld_tlsdesc_undef; where->addend = rela->r_addend; return; } } offs += rela->r_addend; if (obj->tlsoffset != 0) { /* Variable is in initialy allocated TLS segment */ where->func = _rtld_tlsdesc_static; where->offset = obj->tlsoffset + offs; } else { /* TLS offest is unknown at load time, use dynamic resolving */ where->func = _rtld_tlsdesc_dynamic; where->data = reloc_tlsdesc_alloc(obj->tlsindex, offs); } } /* * Process the PLT relocations. */ int reloc_plt(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def, *sym; bool lazy; relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { Elf_Addr *where, target; where = (Elf_Addr *)(obj->relocbase + rela->r_offset); switch(ELF_R_TYPE(rela->r_info)) { case R_AARCH64_JUMP_SLOT: lazy = true; if (obj->variant_pcs) { sym = &obj->symtab[ELF_R_SYM(rela->r_info)]; /* * Variant PCS functions don't follow the * standard register convention. Because of * this we can't use lazy relocation and * need to set the target address. */ if ((sym->st_other & STO_AARCH64_VARIANT_PCS) != 0) lazy = false; } if (lazy) { *where += (Elf_Addr)obj->relocbase; } else { def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) return (-1); if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC){ obj->gnu_ifunc = true; continue; } target = (Elf_Addr)(defobj->relocbase + def->st_value); /* * Ignore ld_bind_not as it requires lazy * binding */ *where = target; } break; case R_AARCH64_TLSDESC: reloc_tlsdesc(obj, rela, (struct tlsdesc_entry *)where, SYMLOOK_IN_PLT | flags, lockstate); break; case R_AARCH64_IRELATIVE: obj->irelative = true; break; case R_AARCH64_NONE: break; default: _rtld_error("Unknown relocation type %u in PLT", (unsigned int)ELF_R_TYPE(rela->r_info)); return (-1); } } return (0); } /* * LD_BIND_NOW was set - force relocation for all jump slots */ int reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; if (obj->jmpslots_done) return (0); relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { Elf_Addr *where, target; where = (Elf_Addr *)(obj->relocbase + rela->r_offset); switch(ELF_R_TYPE(rela->r_info)) { case R_AARCH64_JUMP_SLOT: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) return (-1); if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { obj->gnu_ifunc = true; continue; } target = (Elf_Addr)(defobj->relocbase + def->st_value); reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela); break; } } obj->jmpslots_done = true; return (0); } static void reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela, RtldLockState *lockstate) { Elf_Addr *where, target, *ptr; ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend); where = (Elf_Addr *)(obj->relocbase + rela->r_offset); lock_release(rtld_bind_lock, lockstate); target = call_ifunc_resolver(ptr); wlock_acquire(rtld_bind_lock, lockstate); *where = target; } int reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; if (!obj->irelative) return (0); obj->irelative = false; relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_AARCH64_IRELATIVE) reloc_iresolve_one(obj, rela, lockstate); } return (0); } int reloc_iresolve_nonplt(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; if (!obj->irelative_nonplt) return (0); obj->irelative_nonplt = false; relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize); for (rela = obj->rela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_AARCH64_IRELATIVE) reloc_iresolve_one(obj, rela, lockstate); } return (0); } int reloc_gnu_ifunc(Obj_Entry *obj, int flags, struct Struct_RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; Elf_Addr *where, target; const Elf_Sym *def; const Obj_Entry *defobj; if (!obj->gnu_ifunc) return (0); relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_AARCH64_JUMP_SLOT) { where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) return (-1); if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) continue; lock_release(rtld_bind_lock, lockstate); target = (Elf_Addr)rtld_resolve_ifunc(defobj, def); wlock_acquire(rtld_bind_lock, lockstate); reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela); } } obj->gnu_ifunc = false; return (0); } Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj __unused, const Obj_Entry *obj __unused, const Elf_Rel *rel) { assert(ELF_R_TYPE(rel->r_info) == R_AARCH64_JUMP_SLOT || ELF_R_TYPE(rel->r_info) == R_AARCH64_IRELATIVE); if (*where != target && !ld_bind_not) *where = target; return (target); } void ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) { } /* * Process non-PLT relocations */ int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; SymCache *cache; Elf_Addr *where, symval; /* * The dynamic loader may be called from a thread, we have * limited amounts of stack available so we cannot use alloca(). */ if (obj == obj_rtld) cache = NULL; else cache = calloc(obj->dynsymcount, sizeof(SymCache)); /* No need to check for NULL here */ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize); for (rela = obj->rela; rela < relalim; rela++) { /* * First, resolve symbol for relocations which * reference symbols. */ switch (ELF_R_TYPE(rela->r_info)) { case R_AARCH64_ABS64: case R_AARCH64_GLOB_DAT: case R_AARCH64_TLS_TPREL64: case R_AARCH64_TLS_DTPREL64: case R_AARCH64_TLS_DTPMOD64: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return (-1); /* * If symbol is IFUNC, only perform relocation * when caller allowed it by passing * SYMLOOK_IFUNC flag. Skip the relocations * otherwise. * * Also error out in case IFUNC relocations * are specified for TLS, which cannot be * usefully interpreted. */ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { switch (ELF_R_TYPE(rela->r_info)) { case R_AARCH64_ABS64: case R_AARCH64_GLOB_DAT: if ((flags & SYMLOOK_IFUNC) == 0) { obj->non_plt_gnu_ifunc = true; continue; } symval = (Elf_Addr)rtld_resolve_ifunc( defobj, def); break; default: _rtld_error("%s: IFUNC for TLS reloc", obj->path); return (-1); } } else { if ((flags & SYMLOOK_IFUNC) != 0) continue; symval = (Elf_Addr)defobj->relocbase + def->st_value; } break; default: if ((flags & SYMLOOK_IFUNC) != 0) continue; } where = (Elf_Addr *)(obj->relocbase + rela->r_offset); switch (ELF_R_TYPE(rela->r_info)) { case R_AARCH64_ABS64: case R_AARCH64_GLOB_DAT: *where = symval + rela->r_addend; break; case R_AARCH64_COPY: /* * These are deferred until all other relocations have * been done. All we do here is make sure that the * COPY relocation is not in a shared library. They * are allowed only in executable files. */ if (!obj->mainprog) { _rtld_error("%s: Unexpected R_AARCH64_COPY " "relocation in shared library", obj->path); return (-1); } break; case R_AARCH64_TLSDESC: reloc_tlsdesc(obj, rela, (struct tlsdesc_entry *)where, flags, lockstate); break; case R_AARCH64_TLS_TPREL64: /* * We lazily allocate offsets for static TLS as we * see the first relocation that references the * TLS block. This allows us to support (small * amounts of) static TLS in dynamically loaded * modules. If we run out of space, we generate an * error. */ if (!defobj->tls_static) { if (!allocate_tls_offset( __DECONST(Obj_Entry *, defobj))) { _rtld_error( "%s: No space available for static " "Thread Local Storage", obj->path); return (-1); } } *where = def->st_value + rela->r_addend + defobj->tlsoffset; break; /* * !!! BEWARE !!! * ARM ELF ABI defines TLS_DTPMOD64 as 1029, and TLS_DTPREL64 * as 1028. But actual bfd linker and the glibc RTLD linker * treats TLS_DTPMOD64 as 1028 and TLS_DTPREL64 1029. */ case R_AARCH64_TLS_DTPREL64: /* efectively is TLS_DTPMOD64 */ *where += (Elf_Addr)defobj->tlsindex; break; case R_AARCH64_TLS_DTPMOD64: /* efectively is TLS_DTPREL64 */ *where += (Elf_Addr)(def->st_value + rela->r_addend); break; case R_AARCH64_RELATIVE: *where = (Elf_Addr)(obj->relocbase + rela->r_addend); break; case R_AARCH64_NONE: break; case R_AARCH64_IRELATIVE: obj->irelative_nonplt = true; break; default: rtld_printf("%s: Unhandled relocation %lu\n", obj->path, ELF_R_TYPE(rela->r_info)); return (-1); } } return (0); } void allocate_initial_tls(Obj_Entry *objs) { /* * Fix the size of the static TLS block by using the maximum * offset allocated so far and adding a bit for dynamic modules to * use. */ tls_static_space = tls_last_offset + tls_last_size + ld_static_tls_extra; _tcb_set(allocate_tls(objs, NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN)); } void * __tls_get_addr(tls_index* ti) { uintptr_t **dtvp; dtvp = &_tcb_get()->tcb_dtv; return (tls_get_addr_common(dtvp, ti->ti_module, ti->ti_offset)); } diff --git a/libexec/rtld-elf/aarch64/rtld_start.S b/libexec/rtld-elf/aarch64/rtld_start.S index 8de3e021d567..53a7463e2634 100644 --- a/libexec/rtld-elf/aarch64/rtld_start.S +++ b/libexec/rtld-elf/aarch64/rtld_start.S @@ -1,255 +1,254 @@ /*- * Copyright (c) 2014 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include ENTRY(.rtld_start) .cfi_undefined x30 mov x19, x0 /* Put ps_strings in a callee-saved register */ sub sp, sp, #16 /* Make room for obj_main & exit proc */ .cfi_adjust_cfa_offset 16 mov x1, sp /* exit_proc */ add x2, x1, #8 /* obj_main */ bl _rtld /* Call the loader */ mov x8, x0 /* Backup the entry point */ ldp x2, x1, [sp], #16 /* Load cleanup, obj_main */ .cfi_adjust_cfa_offset 0 mov x0, x19 /* Restore ps_strings */ br x8 /* Jump to the entry point */ END(.rtld_start) /* * sp + 0 = &GOT[x + 3] * sp + 8 = RA * x16 = &GOT[2] * x17 = &_rtld_bind_start */ ENTRY(_rtld_bind_start) mov x17, sp /* Save frame pointer and SP */ stp x29, x30, [sp, #-16]! mov x29, sp .cfi_def_cfa x29, 16 .cfi_offset x30, -8 .cfi_offset x29, -16 /* Save the arguments */ stp x0, x1, [sp, #-16]! stp x2, x3, [sp, #-16]! stp x4, x5, [sp, #-16]! stp x6, x7, [sp, #-16]! stp x8, xzr, [sp, #-16]! /* Save any floating-point arguments */ stp q0, q1, [sp, #-32]! stp q2, q3, [sp, #-32]! stp q4, q5, [sp, #-32]! stp q6, q7, [sp, #-32]! /* Calculate reloff */ ldr x2, [x17, #0] /* Get the address of the entry */ sub x1, x2, x16 /* Find its offset */ sub x1, x1, #8 /* Adjust for x16 not being at offset 0 */ /* Each rela item has 3 entriesso we need reloff = 3 * index */ lsl x3, x1, #1 /* x3 = 2 * offset */ add x1, x1, x3 /* x1 = x3 + offset = 3 * offset */ /* Load obj */ ldr x0, [x16, #-8] /* Call into rtld */ bl _rtld_bind /* Backup the address to branch to */ mov x16, x0 /* restore the arguments */ ldp q6, q7, [sp], #32 ldp q4, q5, [sp], #32 ldp q2, q3, [sp], #32 ldp q0, q1, [sp], #32 ldp x8, xzr, [sp], #16 ldp x6, x7, [sp], #16 ldp x4, x5, [sp], #16 ldp x2, x3, [sp], #16 ldp x0, x1, [sp], #16 /* Restore frame pointer */ ldp x29, xzr, [sp], #16 /* Restore link register saved by the plt code */ ldp xzr, x30, [sp], #16 /* Call into the correct function */ br x16 END(_rtld_bind_start) /* * struct rel_tlsdesc { * uint64_t resolver_fnc; * uint64_t resolver_arg; * * * uint64_t _rtld_tlsdesc_static(struct rel_tlsdesc *); * * Resolver function for TLS symbols resolved at load time */ ENTRY(_rtld_tlsdesc_static) ldr x0, [x0, #8] ret END(_rtld_tlsdesc_static) /* * uint64_t _rtld_tlsdesc_undef(void); * * Resolver function for weak and undefined TLS symbols */ ENTRY(_rtld_tlsdesc_undef) str x1, [sp, #-16]! .cfi_adjust_cfa_offset 16 mrs x1, tpidr_el0 ldr x0, [x0, #8] sub x0, x0, x1 ldr x1, [sp], #16 .cfi_adjust_cfa_offset -16 ret END(_rtld_tlsdesc_undef) /* * uint64_t _rtld_tlsdesc_dynamic(struct rel_tlsdesc *); * * Resolver function for TLS symbols from dlopen() */ ENTRY(_rtld_tlsdesc_dynamic) /* Save registers used in fast path */ stp x1, x2, [sp, #(-2 * 16)]! stp x3, x4, [sp, #(1 * 16)] .cfi_adjust_cfa_offset 2 * 16 .cfi_rel_offset x1, 0 .cfi_rel_offset x2, 8 .cfi_rel_offset x3, 16 .cfi_rel_offset x4, 24 /* Test fastpath - inlined version of tls_get_addr_common(). */ ldr x1, [x0, #8] /* tlsdesc ptr */ mrs x4, tpidr_el0 ldr x0, [x4] /* DTV pointer */ ldr x2, [x0] /* dtv[0] (generation count) */ ldr x3, [x1] /* tlsdec->dtv_gen */ cmp x2, x3 b.ne 1f /* dtv[0] != tlsdec->dtv_gen */ ldr w2, [x1, #8] /* tlsdec->tls_index */ add w2, w2, #1 ldr x3, [x0, w2, sxtw #3] /* dtv[tlsdesc->tls_index + 1] */ cbz x3, 1f /* Return (dtv[tlsdesc->tls_index + 1] + tlsdesc->tls_offs - tp) */ ldr x2, [x1, #16] /* tlsdec->tls_offs */ add x2, x2, x3 sub x0, x2, x4 /* Restore registers and return */ ldp x3, x4, [sp, #(1 * 16)] ldp x1, x2, [sp], #(2 * 16) .cfi_adjust_cfa_offset -2 * 16 ret /* * Slow path * return( * tls_get_addr_common(tp, tlsdesc->tls_index, tlsdesc->tls_offs)); * */ 1: /* Save all integer registers */ stp x29, x30, [sp, #-(8 * 16)]! .cfi_adjust_cfa_offset 8 * 16 .cfi_rel_offset x29, 0 .cfi_rel_offset x30, 8 mov x29, sp stp x5, x6, [sp, #(1 * 16)] stp x7, x8, [sp, #(2 * 16)] stp x9, x10, [sp, #(3 * 16)] stp x11, x12, [sp, #(4 * 16)] stp x13, x14, [sp, #(5 * 16)] stp x15, x16, [sp, #(6 * 16)] stp x17, x18, [sp, #(7 * 16)] .cfi_rel_offset x5, 16 .cfi_rel_offset x6, 24 .cfi_rel_offset x7, 32 .cfi_rel_offset x8, 40 .cfi_rel_offset x9, 48 .cfi_rel_offset x10, 56 .cfi_rel_offset x11, 64 .cfi_rel_offset x12, 72 .cfi_rel_offset x13, 80 .cfi_rel_offset x14, 88 .cfi_rel_offset x15, 96 .cfi_rel_offset x16, 104 .cfi_rel_offset x17, 112 .cfi_rel_offset x18, 120 /* Find the tls offset */ mov x0, x4 /* tp */ mov x3, x1 /* tlsdesc ptr */ ldr w1, [x3, #8] /* tlsdec->tls_index */ ldr x2, [x3, #16] /* tlsdec->tls_offs */ bl tls_get_addr_common mrs x1, tpidr_el0 sub x0, x0, x1 /* Restore slow patch registers */ ldp x17, x18, [sp, #(7 * 16)] ldp x15, x16, [sp, #(6 * 16)] ldp x13, x14, [sp, #(5 * 16)] ldp x11, x12, [sp, #(4 * 16)] ldp x9, x10, [sp, #(3 * 16)] ldp x7, x8, [sp, #(2 * 16)] ldp x5, x6, [sp, #(1 * 16)] ldp x29, x30, [sp], #(8 * 16) .cfi_adjust_cfa_offset -8 * 16 .cfi_restore x29 .cfi_restore x30 /* Restore fast path registers and return */ ldp x3, x4, [sp, #16] ldp x1, x2, [sp], #(2 * 16) .cfi_adjust_cfa_offset -2 * 16 ret END(_rtld_tlsdesc_dynamic) GNU_PROPERTY_AARCH64_FEATURE_1_NOTE(GNU_PROPERTY_AARCH64_FEATURE_1_VAL) diff --git a/libexec/rtld-elf/rtld_malloc.h b/libexec/rtld-elf/rtld_malloc.h index 247726b9f470..408cdc84d45e 100644 --- a/libexec/rtld-elf/rtld_malloc.h +++ b/libexec/rtld-elf/rtld_malloc.h @@ -1,44 +1,43 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef RTLD_MALLOC_H #define RTLD_MALLOC_H void *__crt_aligned_alloc_offset(size_t align, size_t size, size_t offset); void *__crt_calloc(size_t num, size_t size); void __crt_free(void *cp); void *__crt_malloc(size_t nbytes); void *__crt_realloc(void *cp, size_t nbytes); extern int npagesizes; extern size_t *pagesizes; #endif diff --git a/sbin/hastctl/hastctl.8 b/sbin/hastctl/hastctl.8 index a0a1e1f22634..f696858a8071 100644 --- a/sbin/hastctl/hastctl.8 +++ b/sbin/hastctl/hastctl.8 @@ -1,231 +1,230 @@ .\" Copyright (c) 2010 The FreeBSD Foundation -.\" All rights reserved. .\" .\" This software was developed by Pawel Jakub Dawidek under sponsorship from .\" the FreeBSD Foundation. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd September 8, 2016 .Dt HASTCTL 8 .Os .Sh NAME .Nm hastctl .Nd "Highly Available Storage control utility" .Sh SYNOPSIS .Nm .Cm create .Op Fl d .Op Fl c Ar config .Op Fl e Ar extentsize .Op Fl k Ar keepdirty .Op Fl m Ar mediasize .Ar name ... .Nm .Cm role .Op Fl d .Op Fl c Ar config .Aq init | primary | secondary .Ar all | name ... .Nm .Cm list .Op Fl d .Op Fl c Ar config .Op Ar all | name ... .Nm .Cm status .Op Fl d .Op Fl c Ar config .Op Ar all | name ... .Nm .Cm dump .Op Fl d .Op Fl c Ar config .Op Ar all | name ... .Sh DESCRIPTION The .Nm utility is used to control the behaviour of the .Xr hastd 8 daemon. .Pp This utility should be used by HA software like .Nm heartbeat or .Nm ucarp to setup HAST resources role when changing from primary mode to secondary or vice versa. Be aware that if a file system like UFS exists on HAST provider and primary node dies, file system has to be checked for inconsistencies with the .Xr fsck 8 utility after switching secondary node to primary role. .Pp The first argument to .Nm indicates an action to be performed: .Bl -tag -width ".Cm create" .It Cm create Initialize local provider configured for the given resource. Additional options include: .Bl -tag -width ".Fl e Ar extentsize" .It Fl e Ar extentsize Size of an extent. Extent is a block which is used for synchronization. .Xr hastd 8 maintains a map of dirty extents and extent is the smallest region that can be marked as dirty. If any part of an extent is modified, entire extent will be synchronized when nodes connect. If extent size is too small, there will be too much disk activity related to dirty map updates, which will degrade performance of the given resource. If extent size is too large, synchronization, even in case of short outage, can take a long time increasing the risk of losing up-to-date node before synchronization process is completed. The default extent size is .Va 2MB . .It Fl k Ar keepdirty Maximum number of dirty extents to keep dirty all the time. Most recently used extents are kept dirty to reduce number of metadata updates. The default number of most recently used extents which will be kept dirty is .Va 64 . .It Fl m Ar mediasize Size of the smaller provider used as backend storage on both nodes. This option can be omitted if node providers have the same size on both sides. .El .Pp If size is suffixed with a k, M, G or T, it is taken as a kilobyte, megabyte, gigabyte or terabyte measurement respectively. .It Cm role Change role of the given resource. The role can be one of: .Bl -tag -width ".Cm secondary" .It Cm init Resource is turned off. .It Cm primary Local .Xr hastd 8 daemon will act as primary node for the given resource. System on which resource role is set to primary can use .Pa /dev/hast/ GEOM provider. .It Cm secondary Local .Xr hastd 8 daemon will act as secondary node for the given resource - it will wait for connection from the primary node and will handle I/O requests received from it. GEOM provider .Pa /dev/hast/ will not be created on secondary node. .El .It Cm list Present verbose status of the configured resources. .It Cm status Present terse (and more easy machine-parseable) status of the configured resources. .It Cm dump Dump metadata stored on local component for the configured resources. .El .Pp In addition, every subcommand can be followed by the following options: .Bl -tag -width ".Fl c Ar config" .It Fl c Ar config Specify alternative location of the configuration file. The default location is .Pa /etc/hast.conf . .It Fl d Print debugging information. This option can be specified multiple times to raise the verbosity level. .El .Sh FILES .Bl -tag -width ".Pa /var/run/hastctl" -compact .It Pa /etc/hast.conf Configuration file for .Nm and .Xr hastd 8 . .It Pa /var/run/hastctl Control socket used by .Nm to communicate with the .Xr hastd 8 daemon. .El .Sh EXIT STATUS Exit status is 0 on success, or one of the values described in .Xr sysexits 3 on failure. .Sh EXAMPLES Initialize HAST provider, create file system on it and mount it. .Bd -literal -offset indent nodeB# hastctl create shared nodeB# hastd nodeB# hastctl role secondary shared nodeA# hastctl create shared nodeA# hastd nodeA# hastctl role primary shared nodeA# newfs -U /dev/hast/shared nodeA# mount -o noatime /dev/hast/shared /shared nodeA# application_start .Ed .Pp Switch roles for the .Nm shared HAST resource. .Bd -literal -offset indent nodeA# application_stop nodeA# umount -f /shared nodeA# hastctl role secondary shared nodeB# hastctl role primary shared nodeB# fsck -t ufs /dev/hast/shared nodeB# mount -o noatime /dev/hast/shared /shared nodeB# application_start .Ed .Sh SEE ALSO .Xr sysexits 3 , .Xr geom 4 , .Xr hast.conf 5 , .Xr fsck 8 , .Xr ggatec 8 , .Xr ggatel 8 , .Xr hastd 8 , .Xr mount 8 , .Xr newfs 8 .Sh HISTORY The .Nm utility appeared in .Fx 8.1 . .Sh AUTHORS The .Nm was developed by .An Pawel Jakub Dawidek Aq Mt pjd@FreeBSD.org under sponsorship of the FreeBSD Foundation. diff --git a/sbin/hastctl/hastctl.c b/sbin/hastctl/hastctl.c index 224adce2810e..fe37dcaa1b58 100644 --- a/sbin/hastctl/hastctl.c +++ b/sbin/hastctl/hastctl.c @@ -1,583 +1,582 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "hast.h" #include "hast_proto.h" #include "metadata.h" #include "nv.h" #include "pjdlog.h" #include "proto.h" #include "subr.h" /* Path to configuration file. */ static const char *cfgpath = HAST_CONFIG; /* Hastd configuration. */ static struct hastd_config *cfg; /* Control connection. */ static struct proto_conn *controlconn; enum { CMD_INVALID, CMD_CREATE, CMD_ROLE, CMD_STATUS, CMD_DUMP, CMD_LIST }; static __dead2 void usage(void) { fprintf(stderr, "usage: %s create [-d] [-c config] [-e extentsize] [-k keepdirty]\n" "\t\t[-m mediasize] name ...\n", getprogname()); fprintf(stderr, " %s role [-d] [-c config] all | name ...\n", getprogname()); fprintf(stderr, " %s list [-d] [-c config] [all | name ...]\n", getprogname()); fprintf(stderr, " %s status [-d] [-c config] [all | name ...]\n", getprogname()); fprintf(stderr, " %s dump [-d] [-c config] [all | name ...]\n", getprogname()); exit(EX_USAGE); } static int create_one(struct hast_resource *res, intmax_t mediasize, intmax_t extentsize, intmax_t keepdirty) { unsigned char *buf; size_t mapsize; int ec; ec = 0; pjdlog_prefix_set("[%s] ", res->hr_name); if (provinfo(res, true) == -1) { ec = EX_NOINPUT; goto end; } if (mediasize == 0) mediasize = res->hr_local_mediasize; else if (mediasize > res->hr_local_mediasize) { pjdlog_error("Provided mediasize is larger than provider %s size.", res->hr_localpath); ec = EX_DATAERR; goto end; } if (!powerof2(res->hr_local_sectorsize)) { pjdlog_error("Sector size of provider %s is not power of 2 (%u).", res->hr_localpath, res->hr_local_sectorsize); ec = EX_DATAERR; goto end; } if (extentsize == 0) extentsize = HAST_EXTENTSIZE; if (extentsize < res->hr_local_sectorsize) { pjdlog_error("Extent size (%jd) is less than sector size (%u).", (intmax_t)extentsize, res->hr_local_sectorsize); ec = EX_DATAERR; goto end; } if ((extentsize % res->hr_local_sectorsize) != 0) { pjdlog_error("Extent size (%jd) is not multiple of sector size (%u).", (intmax_t)extentsize, res->hr_local_sectorsize); ec = EX_DATAERR; goto end; } mapsize = activemap_calc_ondisk_size(mediasize - METADATA_SIZE, extentsize, res->hr_local_sectorsize); if (keepdirty == 0) keepdirty = HAST_KEEPDIRTY; res->hr_datasize = mediasize - METADATA_SIZE - mapsize; res->hr_extentsize = extentsize; res->hr_keepdirty = keepdirty; res->hr_localoff = METADATA_SIZE + mapsize; if (metadata_write(res) == -1) { ec = EX_IOERR; goto end; } buf = calloc(1, mapsize); if (buf == NULL) { pjdlog_error("Unable to allocate %zu bytes of memory for initial bitmap.", mapsize); ec = EX_TEMPFAIL; goto end; } if (pwrite(res->hr_localfd, buf, mapsize, METADATA_SIZE) != (ssize_t)mapsize) { pjdlog_errno(LOG_ERR, "Unable to store initial bitmap on %s", res->hr_localpath); free(buf); ec = EX_IOERR; goto end; } free(buf); end: if (res->hr_localfd >= 0) close(res->hr_localfd); pjdlog_prefix_set("%s", ""); return (ec); } static void control_create(int argc, char *argv[], intmax_t mediasize, intmax_t extentsize, intmax_t keepdirty) { struct hast_resource *res; int ec, ii, ret; /* Initialize the given resources. */ if (argc < 1) usage(); ec = 0; for (ii = 0; ii < argc; ii++) { TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { if (strcmp(argv[ii], res->hr_name) == 0) break; } if (res == NULL) { pjdlog_error("Unknown resource %s.", argv[ii]); if (ec == 0) ec = EX_DATAERR; continue; } ret = create_one(res, mediasize, extentsize, keepdirty); if (ret != 0 && ec == 0) ec = ret; } exit(ec); } static int dump_one(struct hast_resource *res) { int ret; ret = metadata_read(res, false); if (ret != 0) return (ret); printf("resource: %s\n", res->hr_name); printf(" datasize: %ju (%NB)\n", (uintmax_t)res->hr_datasize, (intmax_t)res->hr_datasize); printf(" extentsize: %d (%NB)\n", res->hr_extentsize, (intmax_t)res->hr_extentsize); printf(" keepdirty: %d\n", res->hr_keepdirty); printf(" localoff: %ju\n", (uintmax_t)res->hr_localoff); printf(" resuid: %ju\n", (uintmax_t)res->hr_resuid); printf(" localcnt: %ju\n", (uintmax_t)res->hr_primary_localcnt); printf(" remotecnt: %ju\n", (uintmax_t)res->hr_primary_remotecnt); printf(" prevrole: %s\n", role2str(res->hr_previous_role)); return (0); } static void control_dump(int argc, char *argv[]) { struct hast_resource *res; int ec, ret; /* Dump metadata of the given resource(s). */ ec = 0; if (argc == 0 || (argc == 1 && strcmp(argv[0], "all") == 0)) { TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { ret = dump_one(res); if (ret != 0 && ec == 0) ec = ret; } } else { int ii; for (ii = 0; ii < argc; ii++) { TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { if (strcmp(argv[ii], res->hr_name) == 0) break; } if (res == NULL) { pjdlog_error("Unknown resource %s.", argv[ii]); if (ec == 0) ec = EX_DATAERR; continue; } ret = dump_one(res); if (ret != 0 && ec == 0) ec = ret; } } exit(ec); } static int control_set_role(struct nv *nv, const char *newrole) { const char *res, *oldrole; unsigned int ii; int error, ret; ret = 0; for (ii = 0; ; ii++) { res = nv_get_string(nv, "resource%u", ii); if (res == NULL) break; pjdlog_prefix_set("[%s] ", res); error = nv_get_int16(nv, "error%u", ii); if (error != 0) { if (ret == 0) ret = error; pjdlog_warning("Received error %d from hastd.", error); continue; } oldrole = nv_get_string(nv, "role%u", ii); if (strcmp(oldrole, newrole) == 0) pjdlog_debug(2, "Role unchanged (%s).", oldrole); else { pjdlog_debug(1, "Role changed from %s to %s.", oldrole, newrole); } } pjdlog_prefix_set("%s", ""); return (ret); } static int control_list(struct nv *nv) { pid_t pid; unsigned int ii; const char *str; int error, ret; ret = 0; for (ii = 0; ; ii++) { str = nv_get_string(nv, "resource%u", ii); if (str == NULL) break; printf("%s:\n", str); error = nv_get_int16(nv, "error%u", ii); if (error != 0) { if (ret == 0) ret = error; printf(" error: %d\n", error); continue; } printf(" role: %s\n", nv_get_string(nv, "role%u", ii)); printf(" provname: %s\n", nv_get_string(nv, "provname%u", ii)); printf(" localpath: %s\n", nv_get_string(nv, "localpath%u", ii)); printf(" extentsize: %u (%NB)\n", (unsigned int)nv_get_uint32(nv, "extentsize%u", ii), (intmax_t)nv_get_uint32(nv, "extentsize%u", ii)); printf(" keepdirty: %u\n", (unsigned int)nv_get_uint32(nv, "keepdirty%u", ii)); printf(" remoteaddr: %s\n", nv_get_string(nv, "remoteaddr%u", ii)); str = nv_get_string(nv, "sourceaddr%u", ii); if (str != NULL) printf(" sourceaddr: %s\n", str); printf(" replication: %s\n", nv_get_string(nv, "replication%u", ii)); str = nv_get_string(nv, "status%u", ii); if (str != NULL) printf(" status: %s\n", str); pid = nv_get_int32(nv, "workerpid%u", ii); if (pid != 0) printf(" workerpid: %d\n", pid); printf(" dirty: %ju (%NB)\n", (uintmax_t)nv_get_uint64(nv, "dirty%u", ii), (intmax_t)nv_get_uint64(nv, "dirty%u", ii)); printf(" statistics:\n"); printf(" reads: %ju\n", (uintmax_t)nv_get_uint64(nv, "stat_read%u", ii)); printf(" writes: %ju\n", (uintmax_t)nv_get_uint64(nv, "stat_write%u", ii)); printf(" deletes: %ju\n", (uintmax_t)nv_get_uint64(nv, "stat_delete%u", ii)); printf(" flushes: %ju\n", (uintmax_t)nv_get_uint64(nv, "stat_flush%u", ii)); printf(" activemap updates: %ju\n", (uintmax_t)nv_get_uint64(nv, "stat_activemap_update%u", ii)); printf(" local errors: " "read: %ju, write: %ju, delete: %ju, flush: %ju\n", (uintmax_t)nv_get_uint64(nv, "stat_read_error%u", ii), (uintmax_t)nv_get_uint64(nv, "stat_write_error%u", ii), (uintmax_t)nv_get_uint64(nv, "stat_delete_error%u", ii), (uintmax_t)nv_get_uint64(nv, "stat_flush_error%u", ii)); printf(" queues: " "local: %ju, send: %ju, recv: %ju, done: %ju, idle: %ju\n", (uintmax_t)nv_get_uint64(nv, "local_queue_size%u", ii), (uintmax_t)nv_get_uint64(nv, "send_queue_size%u", ii), (uintmax_t)nv_get_uint64(nv, "recv_queue_size%u", ii), (uintmax_t)nv_get_uint64(nv, "done_queue_size%u", ii), (uintmax_t)nv_get_uint64(nv, "idle_queue_size%u", ii)); } return (ret); } static int control_status(struct nv *nv) { unsigned int ii; const char *str; int error, hprinted, ret; hprinted = 0; ret = 0; for (ii = 0; ; ii++) { str = nv_get_string(nv, "resource%u", ii); if (str == NULL) break; if (!hprinted) { printf("Name\tStatus\t Role\t\tComponents\n"); hprinted = 1; } printf("%s\t", str); error = nv_get_int16(nv, "error%u", ii); if (error != 0) { if (ret == 0) ret = error; printf("ERR%d\n", error); continue; } str = nv_get_string(nv, "status%u", ii); printf("%-9s", (str != NULL) ? str : "-"); printf("%-15s", nv_get_string(nv, "role%u", ii)); printf("%s\t", nv_get_string(nv, "localpath%u", ii)); printf("%s\n", nv_get_string(nv, "remoteaddr%u", ii)); } return (ret); } int main(int argc, char *argv[]) { struct nv *nv; int64_t mediasize, extentsize, keepdirty; int cmd, debug, error, ii; const char *optstr; debug = 0; mediasize = extentsize = keepdirty = 0; if (argc == 1) usage(); if (strcmp(argv[1], "create") == 0) { cmd = CMD_CREATE; optstr = "c:de:k:m:h"; } else if (strcmp(argv[1], "role") == 0) { cmd = CMD_ROLE; optstr = "c:dh"; } else if (strcmp(argv[1], "list") == 0) { cmd = CMD_LIST; optstr = "c:dh"; } else if (strcmp(argv[1], "status") == 0) { cmd = CMD_STATUS; optstr = "c:dh"; } else if (strcmp(argv[1], "dump") == 0) { cmd = CMD_DUMP; optstr = "c:dh"; } else usage(); argc--; argv++; for (;;) { int ch; ch = getopt(argc, argv, optstr); if (ch == -1) break; switch (ch) { case 'c': cfgpath = optarg; break; case 'd': debug++; break; case 'e': if (expand_number(optarg, &extentsize) == -1) errx(EX_USAGE, "Invalid extentsize"); break; case 'k': if (expand_number(optarg, &keepdirty) == -1) errx(EX_USAGE, "Invalid keepdirty"); break; case 'm': if (expand_number(optarg, &mediasize) == -1) errx(EX_USAGE, "Invalid mediasize"); break; case 'h': default: usage(); } } argc -= optind; argv += optind; switch (cmd) { case CMD_CREATE: case CMD_ROLE: if (argc == 0) usage(); break; } pjdlog_init(PJDLOG_MODE_STD); pjdlog_debug_set(debug); cfg = yy_config_parse(cfgpath, true); PJDLOG_ASSERT(cfg != NULL); switch (cmd) { case CMD_CREATE: control_create(argc, argv, mediasize, extentsize, keepdirty); /* NOTREACHED */ PJDLOG_ABORT("What are we doing here?!"); break; case CMD_DUMP: /* Dump metadata from local component of the given resource. */ control_dump(argc, argv); /* NOTREACHED */ PJDLOG_ABORT("What are we doing here?!"); break; case CMD_ROLE: /* Change role for the given resources. */ if (argc < 2) usage(); nv = nv_alloc(); nv_add_uint8(nv, HASTCTL_CMD_SETROLE, "cmd"); if (strcmp(argv[0], "init") == 0) nv_add_uint8(nv, HAST_ROLE_INIT, "role"); else if (strcmp(argv[0], "primary") == 0) nv_add_uint8(nv, HAST_ROLE_PRIMARY, "role"); else if (strcmp(argv[0], "secondary") == 0) nv_add_uint8(nv, HAST_ROLE_SECONDARY, "role"); else usage(); for (ii = 0; ii < argc - 1; ii++) nv_add_string(nv, argv[ii + 1], "resource%d", ii); break; case CMD_LIST: case CMD_STATUS: /* Obtain status of the given resources. */ nv = nv_alloc(); nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd"); if (argc == 0) nv_add_string(nv, "all", "resource%d", 0); else { for (ii = 0; ii < argc; ii++) nv_add_string(nv, argv[ii], "resource%d", ii); } break; default: PJDLOG_ABORT("Impossible command!"); } /* Setup control connection... */ if (proto_client(NULL, cfg->hc_controladdr, &controlconn) == -1) { pjdlog_exit(EX_OSERR, "Unable to setup control connection to %s", cfg->hc_controladdr); } /* ...and connect to hastd. */ if (proto_connect(controlconn, HAST_TIMEOUT) == -1) { pjdlog_exit(EX_OSERR, "Unable to connect to hastd via %s", cfg->hc_controladdr); } if (drop_privs(NULL) != 0) exit(EX_CONFIG); /* Send the command to the server... */ if (hast_proto_send(NULL, controlconn, nv, NULL, 0) == -1) { pjdlog_exit(EX_UNAVAILABLE, "Unable to send command to hastd via %s", cfg->hc_controladdr); } nv_free(nv); /* ...and receive reply. */ if (hast_proto_recv_hdr(controlconn, &nv) == -1) { pjdlog_exit(EX_UNAVAILABLE, "cannot receive reply from hastd via %s", cfg->hc_controladdr); } error = nv_get_int16(nv, "error"); if (error != 0) { pjdlog_exitx(EX_SOFTWARE, "Error %d received from hastd.", error); } nv_set_error(nv, 0); switch (cmd) { case CMD_ROLE: error = control_set_role(nv, argv[0]); break; case CMD_LIST: error = control_list(nv); break; case CMD_STATUS: error = control_status(nv); break; default: PJDLOG_ABORT("Impossible command!"); } exit(error); } diff --git a/sbin/hastd/activemap.c b/sbin/hastd/activemap.c index 42f3d03b5482..d693d08d524e 100644 --- a/sbin/hastd/activemap.c +++ b/sbin/hastd/activemap.c @@ -1,700 +1,699 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include /* powerof2() */ #include #include #include #include #include #include #include #include #include "activemap.h" #ifndef PJDLOG_ASSERT #include #define PJDLOG_ASSERT(...) assert(__VA_ARGS__) #endif #define ACTIVEMAP_MAGIC 0xac71e4 struct activemap { int am_magic; /* Magic value. */ off_t am_mediasize; /* Media size in bytes. */ uint32_t am_extentsize; /* Extent size in bytes, must be power of 2. */ uint8_t am_extentshift;/* 2 ^ extentbits == extentsize */ int am_nextents; /* Number of extents. */ size_t am_mapsize; /* Bitmap size in bytes. */ uint16_t *am_memtab; /* An array that holds number of pending writes per extent. */ bitstr_t *am_diskmap; /* On-disk bitmap of dirty extents. */ bitstr_t *am_memmap; /* In-memory bitmap of dirty extents. */ size_t am_diskmapsize; /* Map size rounded up to sector size. */ uint64_t am_ndirty; /* Number of dirty regions. */ bitstr_t *am_syncmap; /* Bitmap of extents to sync. */ off_t am_syncoff; /* Next synchronization offset. */ TAILQ_HEAD(skeepdirty, keepdirty) am_keepdirty; /* List of extents that we keep dirty to reduce bitmap updates. */ int am_nkeepdirty; /* Number of am_keepdirty elements. */ int am_nkeepdirty_limit; /* Maximum number of am_keepdirty elements. */ }; struct keepdirty { int kd_extent; TAILQ_ENTRY(keepdirty) kd_next; }; /* * Helper function taken from sys/systm.h to calculate extentshift. */ static uint32_t bitcount32(uint32_t x) { x = (x & 0x55555555) + ((x & 0xaaaaaaaa) >> 1); x = (x & 0x33333333) + ((x & 0xcccccccc) >> 2); x = (x + (x >> 4)) & 0x0f0f0f0f; x = (x + (x >> 8)); x = (x + (x >> 16)) & 0x000000ff; return (x); } static __inline int off2ext(const struct activemap *amp, off_t offset) { int extent; PJDLOG_ASSERT(offset >= 0 && offset < amp->am_mediasize); extent = (offset >> amp->am_extentshift); PJDLOG_ASSERT(extent >= 0 && extent < amp->am_nextents); return (extent); } static __inline off_t ext2off(const struct activemap *amp, int extent) { off_t offset; PJDLOG_ASSERT(extent >= 0 && extent < amp->am_nextents); offset = ((off_t)extent << amp->am_extentshift); PJDLOG_ASSERT(offset >= 0 && offset < amp->am_mediasize); return (offset); } /* * Function calculates number of requests needed to synchronize the given * extent. */ static __inline int ext2reqs(const struct activemap *amp, int ext) { off_t left; if (ext < amp->am_nextents - 1) return (((amp->am_extentsize - 1) / MAXPHYS) + 1); PJDLOG_ASSERT(ext == amp->am_nextents - 1); left = amp->am_mediasize % amp->am_extentsize; if (left == 0) left = amp->am_extentsize; return (((left - 1) / MAXPHYS) + 1); } /* * Initialize activemap structure and allocate memory for internal needs. * Function returns 0 on success and -1 if any of the allocations failed. */ int activemap_init(struct activemap **ampp, uint64_t mediasize, uint32_t extentsize, uint32_t sectorsize, uint32_t keepdirty) { struct activemap *amp; PJDLOG_ASSERT(ampp != NULL); PJDLOG_ASSERT(mediasize > 0); PJDLOG_ASSERT(extentsize > 0); PJDLOG_ASSERT(powerof2(extentsize)); PJDLOG_ASSERT(sectorsize > 0); PJDLOG_ASSERT(powerof2(sectorsize)); PJDLOG_ASSERT(keepdirty > 0); amp = malloc(sizeof(*amp)); if (amp == NULL) return (-1); amp->am_mediasize = mediasize; amp->am_nkeepdirty_limit = keepdirty; amp->am_extentsize = extentsize; amp->am_extentshift = bitcount32(extentsize - 1); amp->am_nextents = ((mediasize - 1) / extentsize) + 1; amp->am_mapsize = bitstr_size(amp->am_nextents); amp->am_diskmapsize = roundup2(amp->am_mapsize, sectorsize); amp->am_ndirty = 0; amp->am_syncoff = -2; TAILQ_INIT(&->am_keepdirty); amp->am_nkeepdirty = 0; amp->am_memtab = calloc(amp->am_nextents, sizeof(amp->am_memtab[0])); amp->am_diskmap = calloc(1, amp->am_diskmapsize); amp->am_memmap = bit_alloc(amp->am_nextents); amp->am_syncmap = bit_alloc(amp->am_nextents); /* * Check to see if any of the allocations above failed. */ if (amp->am_memtab == NULL || amp->am_diskmap == NULL || amp->am_memmap == NULL || amp->am_syncmap == NULL) { if (amp->am_memtab != NULL) free(amp->am_memtab); if (amp->am_diskmap != NULL) free(amp->am_diskmap); if (amp->am_memmap != NULL) free(amp->am_memmap); if (amp->am_syncmap != NULL) free(amp->am_syncmap); amp->am_magic = 0; free(amp); errno = ENOMEM; return (-1); } amp->am_magic = ACTIVEMAP_MAGIC; *ampp = amp; return (0); } static struct keepdirty * keepdirty_find(struct activemap *amp, int extent) { struct keepdirty *kd; TAILQ_FOREACH(kd, &->am_keepdirty, kd_next) { if (kd->kd_extent == extent) break; } return (kd); } static bool keepdirty_add(struct activemap *amp, int extent) { struct keepdirty *kd; kd = keepdirty_find(amp, extent); if (kd != NULL) { /* * Only move element at the beginning. */ TAILQ_REMOVE(&->am_keepdirty, kd, kd_next); TAILQ_INSERT_HEAD(&->am_keepdirty, kd, kd_next); return (false); } /* * Add new element, but first remove the most unused one if * we have too many. */ if (amp->am_nkeepdirty >= amp->am_nkeepdirty_limit) { kd = TAILQ_LAST(&->am_keepdirty, skeepdirty); PJDLOG_ASSERT(kd != NULL); TAILQ_REMOVE(&->am_keepdirty, kd, kd_next); amp->am_nkeepdirty--; PJDLOG_ASSERT(amp->am_nkeepdirty > 0); } if (kd == NULL) kd = malloc(sizeof(*kd)); /* We can ignore allocation failure. */ if (kd != NULL) { kd->kd_extent = extent; amp->am_nkeepdirty++; TAILQ_INSERT_HEAD(&->am_keepdirty, kd, kd_next); } return (true); } static void keepdirty_fill(struct activemap *amp) { struct keepdirty *kd; TAILQ_FOREACH(kd, &->am_keepdirty, kd_next) bit_set(amp->am_diskmap, kd->kd_extent); } static void keepdirty_free(struct activemap *amp) { struct keepdirty *kd; while ((kd = TAILQ_FIRST(&->am_keepdirty)) != NULL) { TAILQ_REMOVE(&->am_keepdirty, kd, kd_next); amp->am_nkeepdirty--; free(kd); } PJDLOG_ASSERT(amp->am_nkeepdirty == 0); } /* * Function frees resources allocated by activemap_init() function. */ void activemap_free(struct activemap *amp) { PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); amp->am_magic = 0; keepdirty_free(amp); free(amp->am_memtab); free(amp->am_diskmap); free(amp->am_memmap); free(amp->am_syncmap); } /* * Function should be called before we handle write requests. It updates * internal structures and returns true if on-disk metadata should be updated. */ bool activemap_write_start(struct activemap *amp, off_t offset, off_t length) { bool modified; off_t end; int ext; PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); PJDLOG_ASSERT(length > 0); modified = false; end = offset + length - 1; for (ext = off2ext(amp, offset); ext <= off2ext(amp, end); ext++) { /* * If the number of pending writes is increased from 0, * we have to mark the extent as dirty also in on-disk bitmap. * By returning true we inform the caller that on-disk bitmap * was modified and has to be flushed to disk. */ if (amp->am_memtab[ext]++ == 0) { PJDLOG_ASSERT(!bit_test(amp->am_memmap, ext)); bit_set(amp->am_memmap, ext); amp->am_ndirty++; } if (keepdirty_add(amp, ext)) modified = true; } return (modified); } /* * Function should be called after receiving write confirmation. It updates * internal structures and returns true if on-disk metadata should be updated. */ bool activemap_write_complete(struct activemap *amp, off_t offset, off_t length) { bool modified; off_t end; int ext; PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); PJDLOG_ASSERT(length > 0); modified = false; end = offset + length - 1; for (ext = off2ext(amp, offset); ext <= off2ext(amp, end); ext++) { /* * If the number of pending writes goes down to 0, we have to * mark the extent as clean also in on-disk bitmap. * By returning true we inform the caller that on-disk bitmap * was modified and has to be flushed to disk. */ PJDLOG_ASSERT(amp->am_memtab[ext] > 0); PJDLOG_ASSERT(bit_test(amp->am_memmap, ext)); if (--amp->am_memtab[ext] == 0) { bit_clear(amp->am_memmap, ext); amp->am_ndirty--; if (keepdirty_find(amp, ext) == NULL) modified = true; } } return (modified); } /* * Function should be called after finishing synchronization of one extent. * It returns true if on-disk metadata should be updated. */ bool activemap_extent_complete(struct activemap *amp, int extent) { bool modified; int reqs; PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); PJDLOG_ASSERT(extent >= 0 && extent < amp->am_nextents); modified = false; reqs = ext2reqs(amp, extent); PJDLOG_ASSERT(amp->am_memtab[extent] >= reqs); amp->am_memtab[extent] -= reqs; PJDLOG_ASSERT(bit_test(amp->am_memmap, extent)); if (amp->am_memtab[extent] == 0) { bit_clear(amp->am_memmap, extent); amp->am_ndirty--; modified = true; } return (modified); } /* * Function returns number of dirty regions. */ uint64_t activemap_ndirty(const struct activemap *amp) { PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); return (amp->am_ndirty); } /* * Function compare on-disk bitmap and in-memory bitmap and returns true if * they differ and should be flushed to the disk. */ bool activemap_differ(const struct activemap *amp) { PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); return (memcmp(amp->am_diskmap, amp->am_memmap, amp->am_mapsize) != 0); } /* * Function returns number of bytes used by bitmap. */ size_t activemap_size(const struct activemap *amp) { PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); return (amp->am_mapsize); } /* * Function returns number of bytes needed for storing on-disk bitmap. * This is the same as activemap_size(), but rounded up to sector size. */ size_t activemap_ondisk_size(const struct activemap *amp) { PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); return (amp->am_diskmapsize); } /* * Function copies the given buffer read from disk to the internal bitmap. */ void activemap_copyin(struct activemap *amp, const unsigned char *buf, size_t size) { int ext; PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); PJDLOG_ASSERT(size >= amp->am_mapsize); memcpy(amp->am_diskmap, buf, amp->am_mapsize); memcpy(amp->am_memmap, buf, amp->am_mapsize); memcpy(amp->am_syncmap, buf, amp->am_mapsize); bit_ffs(amp->am_memmap, amp->am_nextents, &ext); if (ext == -1) { /* There are no dirty extents, so we can leave now. */ return; } /* * Set synchronization offset to the first dirty extent. */ activemap_sync_rewind(amp); /* * We have dirty extents and we want them to stay that way until * we synchronize, so we set number of pending writes to number * of requests needed to synchronize one extent. */ amp->am_ndirty = 0; for (; ext < amp->am_nextents; ext++) { if (bit_test(amp->am_memmap, ext)) { amp->am_memtab[ext] = ext2reqs(amp, ext); amp->am_ndirty++; } } } /* * Function merges the given bitmap with existing one. */ void activemap_merge(struct activemap *amp, const unsigned char *buf, size_t size) { bitstr_t *remmap = __DECONST(bitstr_t *, buf); int ext; PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); PJDLOG_ASSERT(size >= amp->am_mapsize); bit_ffs(remmap, amp->am_nextents, &ext); if (ext == -1) { /* There are no dirty extents, so we can leave now. */ return; } /* * We have dirty extents and we want them to stay that way until * we synchronize, so we set number of pending writes to number * of requests needed to synchronize one extent. */ for (; ext < amp->am_nextents; ext++) { /* Local extent already dirty. */ if (bit_test(amp->am_syncmap, ext)) continue; /* Remote extent isn't dirty. */ if (!bit_test(remmap, ext)) continue; bit_set(amp->am_syncmap, ext); bit_set(amp->am_memmap, ext); bit_set(amp->am_diskmap, ext); if (amp->am_memtab[ext] == 0) amp->am_ndirty++; amp->am_memtab[ext] = ext2reqs(amp, ext); } /* * Set synchronization offset to the first dirty extent. */ activemap_sync_rewind(amp); } /* * Function returns pointer to internal bitmap that should be written to disk. */ const unsigned char * activemap_bitmap(struct activemap *amp, size_t *sizep) { PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); if (sizep != NULL) *sizep = amp->am_diskmapsize; memcpy(amp->am_diskmap, amp->am_memmap, amp->am_mapsize); keepdirty_fill(amp); return ((const unsigned char *)amp->am_diskmap); } /* * Function calculates size needed to store bitmap on disk. */ size_t activemap_calc_ondisk_size(uint64_t mediasize, uint32_t extentsize, uint32_t sectorsize) { uint64_t nextents, mapsize; PJDLOG_ASSERT(mediasize > 0); PJDLOG_ASSERT(extentsize > 0); PJDLOG_ASSERT(powerof2(extentsize)); PJDLOG_ASSERT(sectorsize > 0); PJDLOG_ASSERT(powerof2(sectorsize)); nextents = ((mediasize - 1) / extentsize) + 1; mapsize = bitstr_size(nextents); return (roundup2(mapsize, sectorsize)); } /* * Set synchronization offset to the first dirty extent. */ void activemap_sync_rewind(struct activemap *amp) { int ext; PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); bit_ffs(amp->am_syncmap, amp->am_nextents, &ext); if (ext == -1) { /* There are no extents to synchronize. */ amp->am_syncoff = -2; return; } /* * Mark that we want to start synchronization from the beginning. */ amp->am_syncoff = -1; } /* * Return next offset of where we should synchronize. */ off_t activemap_sync_offset(struct activemap *amp, off_t *lengthp, int *syncextp) { off_t syncoff, left; int ext; PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); PJDLOG_ASSERT(lengthp != NULL); PJDLOG_ASSERT(syncextp != NULL); *syncextp = -1; if (amp->am_syncoff == -2) return (-1); if (amp->am_syncoff >= 0 && (amp->am_syncoff + MAXPHYS >= amp->am_mediasize || off2ext(amp, amp->am_syncoff) != off2ext(amp, amp->am_syncoff + MAXPHYS))) { /* * We are about to change extent, so mark previous one as clean. */ ext = off2ext(amp, amp->am_syncoff); bit_clear(amp->am_syncmap, ext); *syncextp = ext; amp->am_syncoff = -1; } if (amp->am_syncoff == -1) { /* * Let's find first extent to synchronize. */ bit_ffs(amp->am_syncmap, amp->am_nextents, &ext); if (ext == -1) { amp->am_syncoff = -2; return (-1); } amp->am_syncoff = ext2off(amp, ext); } else { /* * We don't change extent, so just increase offset. */ amp->am_syncoff += MAXPHYS; if (amp->am_syncoff >= amp->am_mediasize) { amp->am_syncoff = -2; return (-1); } } syncoff = amp->am_syncoff; left = ext2off(amp, off2ext(amp, syncoff)) + amp->am_extentsize - syncoff; if (syncoff + left > amp->am_mediasize) left = amp->am_mediasize - syncoff; if (left > MAXPHYS) left = MAXPHYS; PJDLOG_ASSERT(left >= 0 && left <= MAXPHYS); PJDLOG_ASSERT(syncoff >= 0 && syncoff < amp->am_mediasize); PJDLOG_ASSERT(syncoff + left >= 0 && syncoff + left <= amp->am_mediasize); *lengthp = left; return (syncoff); } /* * Mark extent(s) containing the given region for synchronization. * Most likely one of the components is unavailable. */ bool activemap_need_sync(struct activemap *amp, off_t offset, off_t length) { bool modified; off_t end; int ext; PJDLOG_ASSERT(amp->am_magic == ACTIVEMAP_MAGIC); modified = false; end = offset + length - 1; for (ext = off2ext(amp, offset); ext <= off2ext(amp, end); ext++) { if (bit_test(amp->am_syncmap, ext)) { /* Already marked for synchronization. */ PJDLOG_ASSERT(bit_test(amp->am_memmap, ext)); continue; } bit_set(amp->am_syncmap, ext); if (!bit_test(amp->am_memmap, ext)) { bit_set(amp->am_memmap, ext); amp->am_ndirty++; } amp->am_memtab[ext] += ext2reqs(amp, ext); modified = true; } return (modified); } void activemap_dump(const struct activemap *amp) { int bit; printf("M: "); for (bit = 0; bit < amp->am_nextents; bit++) printf("%d", bit_test(amp->am_memmap, bit) ? 1 : 0); printf("\n"); printf("D: "); for (bit = 0; bit < amp->am_nextents; bit++) printf("%d", bit_test(amp->am_diskmap, bit) ? 1 : 0); printf("\n"); printf("S: "); for (bit = 0; bit < amp->am_nextents; bit++) printf("%d", bit_test(amp->am_syncmap, bit) ? 1 : 0); printf("\n"); } diff --git a/sbin/hastd/activemap.h b/sbin/hastd/activemap.h index a1dbf70e51af..ff598f841909 100644 --- a/sbin/hastd/activemap.h +++ b/sbin/hastd/activemap.h @@ -1,69 +1,68 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _ACTIVEMAP_H_ #define _ACTIVEMAP_H_ #include #include struct activemap; int activemap_init(struct activemap **ampp, uint64_t mediasize, uint32_t extentsize, uint32_t sectorsize, uint32_t keepdirty); void activemap_free(struct activemap *amp); bool activemap_write_start(struct activemap *amp, off_t offset, off_t length); bool activemap_write_complete(struct activemap *amp, off_t offset, off_t length); bool activemap_extent_complete(struct activemap *amp, int extent); uint64_t activemap_ndirty(const struct activemap *amp); bool activemap_differ(const struct activemap *amp); size_t activemap_size(const struct activemap *amp); size_t activemap_ondisk_size(const struct activemap *amp); void activemap_copyin(struct activemap *amp, const unsigned char *buf, size_t size); void activemap_merge(struct activemap *amp, const unsigned char *buf, size_t size); const unsigned char *activemap_bitmap(struct activemap *amp, size_t *sizep); size_t activemap_calc_ondisk_size(uint64_t mediasize, uint32_t extentsize, uint32_t sectorsize); void activemap_sync_rewind(struct activemap *amp); off_t activemap_sync_offset(struct activemap *amp, off_t *lengthp, int *syncextp); bool activemap_need_sync(struct activemap *amp, off_t offset, off_t length); void activemap_dump(const struct activemap *amp); #endif /* !_ACTIVEMAP_H_ */ diff --git a/sbin/hastd/control.c b/sbin/hastd/control.c index 13a0c5e0af30..0a9befcc4358 100644 --- a/sbin/hastd/control.c +++ b/sbin/hastd/control.c @@ -1,521 +1,520 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "hast.h" #include "hastd.h" #include "hast_checksum.h" #include "hast_compression.h" #include "hast_proto.h" #include "hooks.h" #include "nv.h" #include "pjdlog.h" #include "proto.h" #include "subr.h" #include "control.h" void child_cleanup(struct hast_resource *res) { proto_close(res->hr_ctrl); res->hr_ctrl = NULL; if (res->hr_event != NULL) { proto_close(res->hr_event); res->hr_event = NULL; } if (res->hr_conn != NULL) { proto_close(res->hr_conn); res->hr_conn = NULL; } res->hr_workerpid = 0; } static void control_set_role_common(struct hastd_config *cfg, struct nv *nvout, uint8_t role, struct hast_resource *res, const char *name, unsigned int no) { int oldrole; /* Name is always needed. */ if (name != NULL) nv_add_string(nvout, name, "resource%u", no); if (res == NULL) { PJDLOG_ASSERT(cfg != NULL); PJDLOG_ASSERT(name != NULL); TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { if (strcmp(res->hr_name, name) == 0) break; } if (res == NULL) { nv_add_int16(nvout, EHAST_NOENTRY, "error%u", no); return; } } PJDLOG_ASSERT(res != NULL); /* Send previous role back. */ nv_add_string(nvout, role2str(res->hr_role), "role%u", no); /* Nothing changed, return here. */ if (role == res->hr_role) return; pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); pjdlog_info("Role changed to %s.", role2str(role)); /* Change role to the new one. */ oldrole = res->hr_role; res->hr_role = role; pjdlog_prefix_set("[%s] (%s) ", res->hr_name, role2str(res->hr_role)); /* * If previous role was primary or secondary we have to kill process * doing that work. */ if (res->hr_workerpid != 0) { if (kill(res->hr_workerpid, SIGTERM) == -1) { pjdlog_errno(LOG_WARNING, "Unable to kill worker process %u", (unsigned int)res->hr_workerpid); } else if (waitpid(res->hr_workerpid, NULL, 0) != res->hr_workerpid) { pjdlog_errno(LOG_WARNING, "Error while waiting for worker process %u", (unsigned int)res->hr_workerpid); } else { pjdlog_debug(1, "Worker process %u stopped.", (unsigned int)res->hr_workerpid); } child_cleanup(res); } /* Start worker process if we are changing to primary. */ if (role == HAST_ROLE_PRIMARY) hastd_primary(res); pjdlog_prefix_set("%s", ""); hook_exec(res->hr_exec, "role", res->hr_name, role2str(oldrole), role2str(res->hr_role), NULL); } void control_set_role(struct hast_resource *res, uint8_t role) { control_set_role_common(NULL, NULL, role, res, NULL, 0); } static void control_status_worker(struct hast_resource *res, struct nv *nvout, unsigned int no) { struct nv *cnvin, *cnvout; const char *str; int error; cnvin = NULL; /* * Prepare and send command to worker process. */ cnvout = nv_alloc(); nv_add_uint8(cnvout, CONTROL_STATUS, "cmd"); error = nv_error(cnvout); if (error != 0) { pjdlog_common(LOG_ERR, 0, error, "Unable to prepare control header"); goto end; } if (hast_proto_send(res, res->hr_ctrl, cnvout, NULL, 0) == -1) { error = errno; pjdlog_errno(LOG_ERR, "Unable to send control header"); goto end; } /* * Receive response. */ if (hast_proto_recv_hdr(res->hr_ctrl, &cnvin) == -1) { error = errno; pjdlog_errno(LOG_ERR, "Unable to receive control header"); goto end; } error = nv_get_int16(cnvin, "error"); if (error != 0) goto end; if ((str = nv_get_string(cnvin, "status")) == NULL) { error = ENOENT; pjdlog_errno(LOG_ERR, "Field 'status' is missing."); goto end; } nv_add_string(nvout, str, "status%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "dirty"), "dirty%u", no); nv_add_uint32(nvout, nv_get_uint32(cnvin, "extentsize"), "extentsize%u", no); nv_add_uint32(nvout, nv_get_uint32(cnvin, "keepdirty"), "keepdirty%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_read"), "stat_read%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_write"), "stat_write%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_delete"), "stat_delete%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_flush"), "stat_flush%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_activemap_update"), "stat_activemap_update%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_read_error"), "stat_read_error%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_write_error"), "stat_write_error%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_delete_error"), "stat_delete_error%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "stat_flush_error"), "stat_flush_error%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "idle_queue_size"), "idle_queue_size%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "local_queue_size"), "local_queue_size%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "send_queue_size"), "send_queue_size%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "recv_queue_size"), "recv_queue_size%u", no); nv_add_uint64(nvout, nv_get_uint64(cnvin, "done_queue_size"), "done_queue_size%u", no); end: if (cnvin != NULL) nv_free(cnvin); if (cnvout != NULL) nv_free(cnvout); if (error != 0) nv_add_int16(nvout, error, "error"); } static void control_status(struct hastd_config *cfg, struct nv *nvout, struct hast_resource *res, const char *name, unsigned int no) { PJDLOG_ASSERT(cfg != NULL); PJDLOG_ASSERT(nvout != NULL); PJDLOG_ASSERT(name != NULL); /* Name is always needed. */ nv_add_string(nvout, name, "resource%u", no); if (res == NULL) { TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { if (strcmp(res->hr_name, name) == 0) break; } if (res == NULL) { nv_add_int16(nvout, EHAST_NOENTRY, "error%u", no); return; } } PJDLOG_ASSERT(res != NULL); nv_add_string(nvout, res->hr_provname, "provname%u", no); nv_add_string(nvout, res->hr_localpath, "localpath%u", no); nv_add_string(nvout, res->hr_remoteaddr, "remoteaddr%u", no); if (res->hr_sourceaddr[0] != '\0') nv_add_string(nvout, res->hr_sourceaddr, "sourceaddr%u", no); switch (res->hr_replication) { case HAST_REPLICATION_FULLSYNC: nv_add_string(nvout, "fullsync", "replication%u", no); break; case HAST_REPLICATION_MEMSYNC: nv_add_string(nvout, "memsync", "replication%u", no); break; case HAST_REPLICATION_ASYNC: nv_add_string(nvout, "async", "replication%u", no); break; default: nv_add_string(nvout, "unknown", "replication%u", no); break; } nv_add_string(nvout, checksum_name(res->hr_checksum), "checksum%u", no); nv_add_string(nvout, compression_name(res->hr_compression), "compression%u", no); nv_add_string(nvout, role2str(res->hr_role), "role%u", no); nv_add_int32(nvout, res->hr_workerpid, "workerpid%u", no); switch (res->hr_role) { case HAST_ROLE_PRIMARY: PJDLOG_ASSERT(res->hr_workerpid != 0); /* FALLTHROUGH */ case HAST_ROLE_SECONDARY: if (res->hr_workerpid != 0) break; /* FALLTHROUGH */ default: return; } /* * If we are here, it means that we have a worker process, which we * want to ask some questions. */ control_status_worker(res, nvout, no); } void control_handle(struct hastd_config *cfg) { struct proto_conn *conn; struct nv *nvin, *nvout; unsigned int ii; const char *str; uint8_t cmd, role; int error; if (proto_accept(cfg->hc_controlconn, &conn) == -1) { pjdlog_errno(LOG_ERR, "Unable to accept control connection"); return; } cfg->hc_controlin = conn; nvin = nvout = NULL; role = HAST_ROLE_UNDEF; if (hast_proto_recv_hdr(conn, &nvin) == -1) { pjdlog_errno(LOG_ERR, "Unable to receive control header"); nvin = NULL; goto close; } /* Obtain command code. 0 means that nv_get_uint8() failed. */ cmd = nv_get_uint8(nvin, "cmd"); if (cmd == 0) { pjdlog_error("Control header is missing 'cmd' field."); goto close; } /* Allocate outgoing nv structure. */ nvout = nv_alloc(); if (nvout == NULL) { pjdlog_error("Unable to allocate header for control response."); goto close; } error = 0; str = nv_get_string(nvin, "resource0"); if (str == NULL) { pjdlog_error("Control header is missing 'resource0' field."); error = EHAST_INVALID; goto fail; } if (cmd == HASTCTL_CMD_SETROLE) { role = nv_get_uint8(nvin, "role"); switch (role) { case HAST_ROLE_INIT: case HAST_ROLE_PRIMARY: case HAST_ROLE_SECONDARY: break; default: pjdlog_error("Invalid role received (%hhu).", role); error = EHAST_INVALID; goto fail; } } if (strcmp(str, "all") == 0) { struct hast_resource *res; /* All configured resources. */ ii = 0; TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) { switch (cmd) { case HASTCTL_CMD_SETROLE: control_set_role_common(cfg, nvout, role, res, res->hr_name, ii++); break; case HASTCTL_CMD_STATUS: control_status(cfg, nvout, res, res->hr_name, ii++); break; default: pjdlog_error("Invalid command received (%hhu).", cmd); error = EHAST_UNIMPLEMENTED; goto fail; } } } else { /* Only selected resources. */ for (ii = 0; ; ii++) { str = nv_get_string(nvin, "resource%u", ii); if (str == NULL) break; switch (cmd) { case HASTCTL_CMD_SETROLE: control_set_role_common(cfg, nvout, role, NULL, str, ii); break; case HASTCTL_CMD_STATUS: control_status(cfg, nvout, NULL, str, ii); break; default: pjdlog_error("Invalid command received (%hhu).", cmd); error = EHAST_UNIMPLEMENTED; goto fail; } } } if (nv_error(nvout) != 0) goto close; fail: if (error != 0) nv_add_int16(nvout, error, "error"); if (hast_proto_send(NULL, conn, nvout, NULL, 0) == -1) pjdlog_errno(LOG_ERR, "Unable to send control response"); close: if (nvin != NULL) nv_free(nvin); if (nvout != NULL) nv_free(nvout); proto_close(conn); cfg->hc_controlin = NULL; } /* * Thread handles control requests from the parent. */ void * ctrl_thread(void *arg) { struct hast_resource *res = arg; struct nv *nvin, *nvout; uint8_t cmd; for (;;) { if (hast_proto_recv_hdr(res->hr_ctrl, &nvin) == -1) { if (sigexit_received) pthread_exit(NULL); pjdlog_errno(LOG_ERR, "Unable to receive control message"); kill(getpid(), SIGTERM); pthread_exit(NULL); } cmd = nv_get_uint8(nvin, "cmd"); if (cmd == 0) { pjdlog_error("Control message is missing 'cmd' field."); nv_free(nvin); continue; } nvout = nv_alloc(); switch (cmd) { case CONTROL_STATUS: if (res->hr_remotein != NULL && res->hr_remoteout != NULL) { nv_add_string(nvout, "complete", "status"); } else { nv_add_string(nvout, "degraded", "status"); } nv_add_uint32(nvout, (uint32_t)res->hr_extentsize, "extentsize"); if (res->hr_role == HAST_ROLE_PRIMARY) { nv_add_uint32(nvout, (uint32_t)res->hr_keepdirty, "keepdirty"); nv_add_uint64(nvout, (uint64_t)(activemap_ndirty(res->hr_amp) * res->hr_extentsize), "dirty"); } else { nv_add_uint32(nvout, (uint32_t)0, "keepdirty"); nv_add_uint64(nvout, (uint64_t)0, "dirty"); } nv_add_uint64(nvout, res->hr_stat_read, "stat_read"); nv_add_uint64(nvout, res->hr_stat_write, "stat_write"); nv_add_uint64(nvout, res->hr_stat_delete, "stat_delete"); nv_add_uint64(nvout, res->hr_stat_flush, "stat_flush"); nv_add_uint64(nvout, res->hr_stat_activemap_update, "stat_activemap_update"); nv_add_uint64(nvout, res->hr_stat_read_error, "stat_read_error"); nv_add_uint64(nvout, res->hr_stat_write_error + res->hr_stat_activemap_write_error, "stat_write_error"); nv_add_uint64(nvout, res->hr_stat_delete_error, "stat_delete_error"); nv_add_uint64(nvout, res->hr_stat_flush_error + res->hr_stat_activemap_flush_error, "stat_flush_error"); res->output_status_aux(nvout); nv_add_int16(nvout, 0, "error"); break; case CONTROL_RELOAD: /* * When parent receives SIGHUP and discovers that * something related to us has changes, it sends reload * message to us. */ PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY); primary_config_reload(res, nvin); nv_add_int16(nvout, 0, "error"); break; default: nv_add_int16(nvout, EINVAL, "error"); break; } nv_free(nvin); if (nv_error(nvout) != 0) { pjdlog_error("Unable to create answer on control message."); nv_free(nvout); continue; } if (hast_proto_send(NULL, res->hr_ctrl, nvout, NULL, 0) == -1) { pjdlog_errno(LOG_ERR, "Unable to send reply to control message"); } nv_free(nvout); } /* NOTREACHED */ return (NULL); } diff --git a/sbin/hastd/control.h b/sbin/hastd/control.h index 4e16334dc824..ad7fb7978597 100644 --- a/sbin/hastd/control.h +++ b/sbin/hastd/control.h @@ -1,49 +1,48 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _CONTROL_H_ #define _CONTROL_H_ #define CONTROL_STATUS 10 #define CONTROL_RELOAD 11 struct hastd_config; struct hast_resource; void child_cleanup(struct hast_resource *res); void control_set_role(struct hast_resource *res, uint8_t role); void control_handle(struct hastd_config *cfg); void *ctrl_thread(void *arg); #endif /* !_CONTROL_H_ */ diff --git a/sbin/hastd/ebuf.c b/sbin/hastd/ebuf.c index fdb5c442bfa5..1ec8dd672c35 100644 --- a/sbin/hastd/ebuf.c +++ b/sbin/hastd/ebuf.c @@ -1,262 +1,261 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "ebuf.h" #ifndef PJDLOG_ASSERT #include #define PJDLOG_ASSERT(...) assert(__VA_ARGS__) #endif #define EBUF_MAGIC 0xeb0f41c struct ebuf { /* Magic to assert the caller uses valid structure. */ int eb_magic; /* Address where we did the allocation. */ unsigned char *eb_start; /* Allocation end address. */ unsigned char *eb_end; /* Start of real data. */ unsigned char *eb_used; /* Size of real data. */ size_t eb_size; }; static int ebuf_head_extend(struct ebuf *eb, size_t size); static int ebuf_tail_extend(struct ebuf *eb, size_t size); struct ebuf * ebuf_alloc(size_t size) { struct ebuf *eb; size_t page_size; int rerrno; eb = malloc(sizeof(*eb)); if (eb == NULL) return (NULL); page_size = getpagesize(); size += page_size; eb->eb_start = malloc(size); if (eb->eb_start == NULL) { rerrno = errno; free(eb); errno = rerrno; return (NULL); } eb->eb_end = eb->eb_start + size; /* * We set start address for real data not at the first entry, because * we want to be able to add data at the front. */ eb->eb_used = eb->eb_start + page_size / 4; eb->eb_size = 0; eb->eb_magic = EBUF_MAGIC; return (eb); } void ebuf_free(struct ebuf *eb) { PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC); eb->eb_magic = 0; free(eb->eb_start); free(eb); } int ebuf_add_head(struct ebuf *eb, const void *data, size_t size) { PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC); if (size > (size_t)(eb->eb_used - eb->eb_start)) { /* * We can't add more entries at the front, so we have to extend * our buffer. */ if (ebuf_head_extend(eb, size) == -1) return (-1); } PJDLOG_ASSERT(size <= (size_t)(eb->eb_used - eb->eb_start)); eb->eb_size += size; eb->eb_used -= size; /* * If data is NULL the caller just wants to reserve place. */ if (data != NULL) bcopy(data, eb->eb_used, size); return (0); } int ebuf_add_tail(struct ebuf *eb, const void *data, size_t size) { PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC); if (size > (size_t)(eb->eb_end - (eb->eb_used + eb->eb_size))) { /* * We can't add more entries at the back, so we have to extend * our buffer. */ if (ebuf_tail_extend(eb, size) == -1) return (-1); } PJDLOG_ASSERT(size <= (size_t)(eb->eb_end - (eb->eb_used + eb->eb_size))); /* * If data is NULL the caller just wants to reserve space. */ if (data != NULL) bcopy(data, eb->eb_used + eb->eb_size, size); eb->eb_size += size; return (0); } void ebuf_del_head(struct ebuf *eb, size_t size) { PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC); PJDLOG_ASSERT(size <= eb->eb_size); eb->eb_used += size; eb->eb_size -= size; } void ebuf_del_tail(struct ebuf *eb, size_t size) { PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC); PJDLOG_ASSERT(size <= eb->eb_size); eb->eb_size -= size; } /* * Return pointer to the data and data size. */ void * ebuf_data(struct ebuf *eb, size_t *sizep) { PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC); if (sizep != NULL) *sizep = eb->eb_size; return (eb->eb_size > 0 ? eb->eb_used : NULL); } /* * Return data size. */ size_t ebuf_size(struct ebuf *eb) { PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC); return (eb->eb_size); } /* * Function adds size + (PAGE_SIZE / 4) bytes at the front of the buffer.. */ static int ebuf_head_extend(struct ebuf *eb, size_t size) { unsigned char *newstart, *newused; size_t newsize, page_size; PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC); page_size = getpagesize(); newsize = eb->eb_end - eb->eb_start + (page_size / 4) + size; newstart = malloc(newsize); if (newstart == NULL) return (-1); newused = newstart + (page_size / 4) + size + (eb->eb_used - eb->eb_start); bcopy(eb->eb_used, newused, eb->eb_size); eb->eb_start = newstart; eb->eb_used = newused; eb->eb_end = newstart + newsize; return (0); } /* * Function adds size + ((3 * PAGE_SIZE) / 4) bytes at the back. */ static int ebuf_tail_extend(struct ebuf *eb, size_t size) { unsigned char *newstart; size_t newsize, page_size; PJDLOG_ASSERT(eb != NULL && eb->eb_magic == EBUF_MAGIC); page_size = getpagesize(); newsize = eb->eb_end - eb->eb_start + size + ((3 * page_size) / 4); newstart = realloc(eb->eb_start, newsize); if (newstart == NULL) return (-1); eb->eb_used = newstart + (eb->eb_used - eb->eb_start); eb->eb_start = newstart; eb->eb_end = newstart + newsize; return (0); } diff --git a/sbin/hastd/ebuf.h b/sbin/hastd/ebuf.h index a79821735539..a4a2b30f4cb4 100644 --- a/sbin/hastd/ebuf.h +++ b/sbin/hastd/ebuf.h @@ -1,51 +1,50 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _EBUF_H_ #define _EBUF_H_ #include /* size_t */ struct ebuf; struct ebuf *ebuf_alloc(size_t size); void ebuf_free(struct ebuf *eb); int ebuf_add_head(struct ebuf *eb, const void *data, size_t size); int ebuf_add_tail(struct ebuf *eb, const void *data, size_t size); void ebuf_del_head(struct ebuf *eb, size_t size); void ebuf_del_tail(struct ebuf *eb, size_t size); void *ebuf_data(struct ebuf *eb, size_t *sizep); size_t ebuf_size(struct ebuf *eb); #endif /* !_EBUF_H_ */ diff --git a/sbin/hastd/hast_proto.h b/sbin/hastd/hast_proto.h index ab0a46494b45..0158aa4ccee6 100644 --- a/sbin/hastd/hast_proto.h +++ b/sbin/hastd/hast_proto.h @@ -1,46 +1,45 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _HAST_PROTO_H_ #define _HAST_PROTO_H_ #include /* size_t */ #include #include int hast_proto_send(const struct hast_resource *res, struct proto_conn *conn, struct nv *nv, const void *data, size_t size); int hast_proto_recv_hdr(const struct proto_conn *conn, struct nv **nvp); int hast_proto_recv_data(const struct hast_resource *res, struct proto_conn *conn, struct nv *nv, void *data, size_t size); #endif /* !_HAST_PROTO_H_ */ diff --git a/sbin/hastd/hastd.8 b/sbin/hastd/hastd.8 index fefd03f7dbe0..05a2d9b5e4e9 100644 --- a/sbin/hastd/hastd.8 +++ b/sbin/hastd/hastd.8 @@ -1,235 +1,234 @@ .\" Copyright (c) 2010 The FreeBSD Foundation -.\" All rights reserved. .\" .\" This software was developed by Pawel Jakub Dawidek under sponsorship from .\" the FreeBSD Foundation. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd December 21, 2019 .Dt HASTD 8 .Os .Sh NAME .Nm hastd .Nd "Highly Available Storage daemon" .Sh SYNOPSIS .Nm .Op Fl dFh .Op Fl c Ar config .Op Fl P Ar pidfile .Sh DESCRIPTION The .Nm daemon is responsible for managing highly available GEOM providers. .Pp .Nm allows the transparent storage of data on two physically separated machines connected over a TCP/IP network. Only one machine (cluster node) can actively use storage provided by .Nm . This machine is called primary. The .Nm daemon operates on block level, which makes it transparent to file systems and applications. .Pp There is one main .Nm daemon which starts new worker process as soon as a role for the given resource is changed to primary or as soon as a role for the given resource is changed to secondary and remote (primary) node will successfully connect to it. Every worker process gets a new process title (see .Xr setproctitle 3 ) , which describes its role and resource it controls. The exact format is: .Bd -literal -offset indent hastd: () .Ed .Pp If (and only if) .Nm operates in primary role for the given resource, a corresponding .Pa /dev/hast/ disk-like device (GEOM provider) is created. File systems and applications can use this provider to send I/O requests to. Every write, delete and flush operation .Dv ( BIO_WRITE , BIO_DELETE , BIO_FLUSH ) is sent to the local component and replicated on the remote (secondary) node if it is available. Read operations .Dv ( BIO_READ ) are handled locally unless an I/O error occurs or the local version of the data is not up-to-date yet (synchronization is in progress). .Pp The .Nm daemon uses the GEOM Gate class to receive I/O requests from the in-kernel GEOM infrastructure. The .Nm geom_gate.ko module is loaded automatically if the kernel was not compiled with the following option: .Bd -ragged -offset indent .Cd "options GEOM_GATE" .Ed .Pp The connection between two .Nm daemons is always initiated from the one running as primary to the one running as secondary. When the primary .Nm is unable to connect or the connection fails, it will try to re-establish the connection every few seconds. Once the connection is established, the primary .Nm will synchronize every extent that was modified during connection outage to the secondary .Nm . .Pp It is possible that in the case of a connection outage between the nodes the .Nm primary role for the given resource will be configured on both nodes. This in turn leads to incompatible data modifications. Such a condition is called a split-brain and cannot be automatically resolved by the .Nm daemon as this will lead most likely to data corruption or loss of important changes. Even though it cannot be fixed by .Nm itself, it will be detected and a further connection between independently modified nodes will not be possible. Once this situation is manually resolved by an administrator, the resource on one of the nodes can be initialized (erasing local data), which makes a connection to the remote node possible again. Connection of the freshly initialized component will trigger full resource synchronization. .Pp A .Nm daemon never picks its role automatically. The role has to be configured with the .Xr hastctl 8 control utility by additional software like .Nm ucarp or .Nm heartbeat that can reliably manage role separation and switch secondary node to primary role in case of the primary's failure. .Pp The .Nm daemon can be started with the following command line arguments: .Bl -tag -width ".Fl P Ar pidfile" .It Fl c Ar config Specify alternative location of the configuration file. The default location is .Pa /etc/hast.conf . .It Fl d Print or log debugging information. This option can be specified multiple times to raise the verbosity level. .It Fl F Start the .Nm daemon in the foreground. By default .Nm starts in the background. .It Fl h Print the .Nm usage message. .It Fl P Ar pidfile Specify alternative location of a file where main process PID will be stored. The default location is .Pa /var/run/hastd.pid . .El .Sh FILES .Bl -tag -width ".Pa /var/run/hastd.pid" -compact .It Pa /etc/hast.conf The configuration file for .Nm and .Xr hastctl 8 . .It Pa /var/run/hastctl Control socket used by the .Xr hastctl 8 control utility to communicate with .Nm . .It Pa /var/run/hastd.pid The default location of the .Nm PID file. .El .Sh EXIT STATUS Exit status is 0 on success, or one of the values described in .Xr sysexits 3 on failure. .Sh EXAMPLES Launch .Nm on both nodes. Set role for resource .Nm shared to primary on .Nm nodeA and to secondary on .Nm nodeB . Create file system on .Pa /dev/hast/shared provider and mount it. .Bd -literal -offset indent nodeB# hastd nodeB# hastctl role secondary shared nodeA# hastd nodeA# hastctl role primary shared nodeA# newfs -U /dev/hast/shared nodeA# mount -o noatime /dev/hast/shared /shared .Ed .Sh SEE ALSO .Xr sysexits 3 , .Xr geom 4 , .Xr hast.conf 5 , .Xr ggatec 8 , .Xr ggated 8 , .Xr ggatel 8 , .Xr hastctl 8 , .Xr mount 8 , .Xr newfs 8 , .Xr g_bio 9 .Sh HISTORY The .Nm utility appeared in .Fx 8.1 . .Sh AUTHORS The .Nm was developed by .An Pawel Jakub Dawidek Aq Mt pjd@FreeBSD.org under sponsorship of the FreeBSD Foundation. diff --git a/sbin/hastd/hastd.h b/sbin/hastd/hastd.h index 15ad243adf7c..a54ef316f37a 100644 --- a/sbin/hastd/hastd.h +++ b/sbin/hastd/hastd.h @@ -1,54 +1,53 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _HASTD_H_ #define _HASTD_H_ #include #include #include #include "hast.h" extern const char *cfgpath; extern bool sigexit_received; extern struct pidfh *pfh; void descriptors_cleanup(struct hast_resource *res); void descriptors_assert(const struct hast_resource *res, int pjdlogmode); void hastd_primary(struct hast_resource *res); void hastd_secondary(struct hast_resource *res, struct nv *nvin); void primary_config_reload(struct hast_resource *res, struct nv *nv); #endif /* !_HASTD_H_ */ diff --git a/sbin/hastd/metadata.c b/sbin/hastd/metadata.c index 2f9cbc5e0971..5318d9f6aee5 100644 --- a/sbin/hastd/metadata.c +++ b/sbin/hastd/metadata.c @@ -1,225 +1,224 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "metadata.h" int metadata_read(struct hast_resource *res, bool openrw) { unsigned char *buf; struct ebuf *eb; struct nv *nv; ssize_t done; const char *str; int rerrno; bool opened_here; opened_here = false; rerrno = 0; /* * Is this first metadata_read() call for this resource? */ if (res->hr_localfd == -1) { if (provinfo(res, openrw) == -1) { rerrno = errno; goto fail; } opened_here = true; pjdlog_debug(1, "Obtained info about %s.", res->hr_localpath); if (openrw) { if (flock(res->hr_localfd, LOCK_EX | LOCK_NB) == -1) { rerrno = errno; if (errno == EOPNOTSUPP) { pjdlog_warning("Unable to lock %s (operation not supported), but continuing.", res->hr_localpath); } else { pjdlog_errno(LOG_ERR, "Unable to lock %s", res->hr_localpath); goto fail; } } pjdlog_debug(1, "Locked %s.", res->hr_localpath); } } eb = ebuf_alloc(METADATA_SIZE); if (eb == NULL) { rerrno = errno; pjdlog_errno(LOG_ERR, "Unable to allocate memory to read metadata"); goto fail; } if (ebuf_add_tail(eb, NULL, METADATA_SIZE) == -1) { rerrno = errno; pjdlog_errno(LOG_ERR, "Unable to allocate memory to read metadata"); ebuf_free(eb); goto fail; } buf = ebuf_data(eb, NULL); PJDLOG_ASSERT(buf != NULL); done = pread(res->hr_localfd, buf, METADATA_SIZE, 0); if (done == -1 || done != METADATA_SIZE) { rerrno = errno; pjdlog_errno(LOG_ERR, "Unable to read metadata"); ebuf_free(eb); goto fail; } nv = nv_ntoh(eb); if (nv == NULL) { rerrno = errno; pjdlog_errno(LOG_ERR, "Metadata read from %s is invalid", res->hr_localpath); ebuf_free(eb); goto fail; } str = nv_get_string(nv, "resource"); if (str != NULL && strcmp(str, res->hr_name) != 0) { pjdlog_error("Provider %s is not part of resource %s.", res->hr_localpath, res->hr_name); nv_free(nv); goto fail; } res->hr_datasize = nv_get_uint64(nv, "datasize"); res->hr_extentsize = (int)nv_get_uint32(nv, "extentsize"); res->hr_keepdirty = (int)nv_get_uint32(nv, "keepdirty"); res->hr_localoff = nv_get_uint64(nv, "offset"); res->hr_resuid = nv_get_uint64(nv, "resuid"); if (res->hr_role != HAST_ROLE_PRIMARY) { /* Secondary or init role. */ res->hr_secondary_localcnt = nv_get_uint64(nv, "localcnt"); res->hr_secondary_remotecnt = nv_get_uint64(nv, "remotecnt"); } if (res->hr_role != HAST_ROLE_SECONDARY) { /* Primary or init role. */ res->hr_primary_localcnt = nv_get_uint64(nv, "localcnt"); res->hr_primary_remotecnt = nv_get_uint64(nv, "remotecnt"); } str = nv_get_string(nv, "prevrole"); if (str != NULL) { if (strcmp(str, "primary") == 0) res->hr_previous_role = HAST_ROLE_PRIMARY; else if (strcmp(str, "secondary") == 0) res->hr_previous_role = HAST_ROLE_SECONDARY; } if (nv_error(nv) != 0) { errno = rerrno = nv_error(nv); pjdlog_errno(LOG_ERR, "Unable to read metadata from %s", res->hr_localpath); nv_free(nv); goto fail; } nv_free(nv); return (0); fail: if (opened_here) { close(res->hr_localfd); res->hr_localfd = -1; } errno = rerrno; return (-1); } int metadata_write(struct hast_resource *res) { struct ebuf *eb; struct nv *nv; unsigned char *buf, *ptr; size_t size; ssize_t done; int ret; buf = calloc(1, METADATA_SIZE); if (buf == NULL) { pjdlog_error("Unable to allocate %zu bytes for metadata.", (size_t)METADATA_SIZE); return (-1); } ret = -1; nv = nv_alloc(); nv_add_string(nv, res->hr_name, "resource"); nv_add_uint64(nv, (uint64_t)res->hr_datasize, "datasize"); nv_add_uint32(nv, (uint32_t)res->hr_extentsize, "extentsize"); nv_add_uint32(nv, (uint32_t)res->hr_keepdirty, "keepdirty"); nv_add_uint64(nv, (uint64_t)res->hr_localoff, "offset"); nv_add_uint64(nv, res->hr_resuid, "resuid"); if (res->hr_role == HAST_ROLE_PRIMARY || res->hr_role == HAST_ROLE_INIT) { nv_add_uint64(nv, res->hr_primary_localcnt, "localcnt"); nv_add_uint64(nv, res->hr_primary_remotecnt, "remotecnt"); } else /* if (res->hr_role == HAST_ROLE_SECONDARY) */ { PJDLOG_ASSERT(res->hr_role == HAST_ROLE_SECONDARY); nv_add_uint64(nv, res->hr_secondary_localcnt, "localcnt"); nv_add_uint64(nv, res->hr_secondary_remotecnt, "remotecnt"); } nv_add_string(nv, role2str(res->hr_role), "prevrole"); if (nv_error(nv) != 0) { pjdlog_error("Unable to create metadata."); goto end; } res->hr_previous_role = res->hr_role; eb = nv_hton(nv); PJDLOG_ASSERT(eb != NULL); ptr = ebuf_data(eb, &size); PJDLOG_ASSERT(ptr != NULL); PJDLOG_ASSERT(size < METADATA_SIZE); bcopy(ptr, buf, size); done = pwrite(res->hr_localfd, buf, METADATA_SIZE, 0); if (done == -1 || done != METADATA_SIZE) { pjdlog_errno(LOG_ERR, "Unable to write metadata"); goto end; } ret = 0; end: free(buf); nv_free(nv); return (ret); } diff --git a/sbin/hastd/metadata.h b/sbin/hastd/metadata.h index b5215c1a85c9..6d8c593f0a46 100644 --- a/sbin/hastd/metadata.h +++ b/sbin/hastd/metadata.h @@ -1,48 +1,47 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _METADATA_H_ #define _METADATA_H_ #include #include /* * Maximum size of metadata. * XXX: We should take sector size into account. */ #define METADATA_SIZE 4096 int metadata_read(struct hast_resource *res, bool openrw); int metadata_write(struct hast_resource *res); #endif /* !_METADATA_H_ */ diff --git a/sbin/hastd/nv.c b/sbin/hastd/nv.c index 47499ce27294..0730e4f2a794 100644 --- a/sbin/hastd/nv.c +++ b/sbin/hastd/nv.c @@ -1,965 +1,964 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "nv.h" #ifndef PJDLOG_ASSERT #include #define PJDLOG_ASSERT(...) assert(__VA_ARGS__) #endif #ifndef PJDLOG_ABORT #define PJDLOG_ABORT(...) abort() #endif #define NV_TYPE_NONE 0 #define NV_TYPE_INT8 1 #define NV_TYPE_UINT8 2 #define NV_TYPE_INT16 3 #define NV_TYPE_UINT16 4 #define NV_TYPE_INT32 5 #define NV_TYPE_UINT32 6 #define NV_TYPE_INT64 7 #define NV_TYPE_UINT64 8 #define NV_TYPE_INT8_ARRAY 9 #define NV_TYPE_UINT8_ARRAY 10 #define NV_TYPE_INT16_ARRAY 11 #define NV_TYPE_UINT16_ARRAY 12 #define NV_TYPE_INT32_ARRAY 13 #define NV_TYPE_UINT32_ARRAY 14 #define NV_TYPE_INT64_ARRAY 15 #define NV_TYPE_UINT64_ARRAY 16 #define NV_TYPE_STRING 17 #define NV_TYPE_MASK 0x7f #define NV_TYPE_FIRST NV_TYPE_INT8 #define NV_TYPE_LAST NV_TYPE_STRING #define NV_ORDER_NETWORK 0x00 #define NV_ORDER_HOST 0x80 #define NV_ORDER_MASK 0x80 #define NV_MAGIC 0xaea1e struct nv { int nv_magic; int nv_error; struct ebuf *nv_ebuf; }; struct nvhdr { uint8_t nvh_type; uint8_t nvh_namesize; uint32_t nvh_dsize; char nvh_name[0]; } __packed; #define NVH_DATA(nvh) ((unsigned char *)nvh + NVH_HSIZE(nvh)) #define NVH_HSIZE(nvh) \ (sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8)) #define NVH_DSIZE(nvh) \ (((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ? \ (nvh)->nvh_dsize : \ le32toh((nvh)->nvh_dsize)) #define NVH_SIZE(nvh) (NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8)) #define NV_CHECK(nv) do { \ PJDLOG_ASSERT((nv) != NULL); \ PJDLOG_ASSERT((nv)->nv_magic == NV_MAGIC); \ } while (0) static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type, const char *name); static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type, const char *namefmt, va_list nameap); static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap); static void nv_swap(struct nvhdr *nvh, bool tohost); /* * Allocate and initialize new nv structure. * Return NULL in case of malloc(3) failure. */ struct nv * nv_alloc(void) { struct nv *nv; nv = malloc(sizeof(*nv)); if (nv == NULL) return (NULL); nv->nv_ebuf = ebuf_alloc(0); if (nv->nv_ebuf == NULL) { free(nv); return (NULL); } nv->nv_error = 0; nv->nv_magic = NV_MAGIC; return (nv); } /* * Free the given nv structure. */ void nv_free(struct nv *nv) { if (nv == NULL) return; NV_CHECK(nv); nv->nv_magic = 0; ebuf_free(nv->nv_ebuf); free(nv); } /* * Return error for the given nv structure. */ int nv_error(const struct nv *nv) { if (nv == NULL) return (ENOMEM); NV_CHECK(nv); return (nv->nv_error); } /* * Set error for the given nv structure and return previous error. */ int nv_set_error(struct nv *nv, int error) { int preverr; if (nv == NULL) return (ENOMEM); NV_CHECK(nv); preverr = nv->nv_error; nv->nv_error = error; return (preverr); } /* * Validate correctness of the entire nv structure and all its elements. * If extrap is not NULL, store number of extra bytes at the end of the buffer. */ int nv_validate(struct nv *nv, size_t *extrap) { struct nvhdr *nvh; unsigned char *data, *ptr; size_t dsize, size, vsize; int error; if (nv == NULL) { errno = ENOMEM; return (-1); } NV_CHECK(nv); PJDLOG_ASSERT(nv->nv_error == 0); /* TODO: Check that names are unique? */ error = 0; ptr = ebuf_data(nv->nv_ebuf, &size); while (size > 0) { /* * Zeros at the end of the buffer are acceptable. */ if (ptr[0] == '\0') break; /* * Minimum size at this point is size of nvhdr structure, one * character long name plus terminating '\0'. */ if (size < sizeof(*nvh) + 2) { error = EINVAL; break; } nvh = (struct nvhdr *)ptr; if (size < NVH_HSIZE(nvh)) { error = EINVAL; break; } if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') { error = EINVAL; break; } if (strlen(nvh->nvh_name) != (size_t)(nvh->nvh_namesize - 1)) { error = EINVAL; break; } if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST || (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) { error = EINVAL; break; } dsize = NVH_DSIZE(nvh); if (dsize == 0) { error = EINVAL; break; } if (size < NVH_SIZE(nvh)) { error = EINVAL; break; } vsize = 0; switch (nvh->nvh_type & NV_TYPE_MASK) { case NV_TYPE_INT8: case NV_TYPE_UINT8: if (vsize == 0) vsize = 1; /* FALLTHROUGH */ case NV_TYPE_INT16: case NV_TYPE_UINT16: if (vsize == 0) vsize = 2; /* FALLTHROUGH */ case NV_TYPE_INT32: case NV_TYPE_UINT32: if (vsize == 0) vsize = 4; /* FALLTHROUGH */ case NV_TYPE_INT64: case NV_TYPE_UINT64: if (vsize == 0) vsize = 8; if (dsize != vsize) { error = EINVAL; break; } break; case NV_TYPE_INT8_ARRAY: case NV_TYPE_UINT8_ARRAY: break; case NV_TYPE_INT16_ARRAY: case NV_TYPE_UINT16_ARRAY: if (vsize == 0) vsize = 2; /* FALLTHROUGH */ case NV_TYPE_INT32_ARRAY: case NV_TYPE_UINT32_ARRAY: if (vsize == 0) vsize = 4; /* FALLTHROUGH */ case NV_TYPE_INT64_ARRAY: case NV_TYPE_UINT64_ARRAY: if (vsize == 0) vsize = 8; if ((dsize % vsize) != 0) { error = EINVAL; break; } break; case NV_TYPE_STRING: data = NVH_DATA(nvh); if (data[dsize - 1] != '\0') { error = EINVAL; break; } if (strlen((char *)data) != dsize - 1) { error = EINVAL; break; } break; default: PJDLOG_ABORT("invalid condition"); } if (error != 0) break; ptr += NVH_SIZE(nvh); size -= NVH_SIZE(nvh); } if (error != 0) { errno = error; if (nv->nv_error == 0) nv->nv_error = error; return (-1); } if (extrap != NULL) *extrap = size; return (0); } /* * Convert the given nv structure to network byte order and return ebuf * structure. */ struct ebuf * nv_hton(struct nv *nv) { struct nvhdr *nvh; unsigned char *ptr; size_t size; NV_CHECK(nv); PJDLOG_ASSERT(nv->nv_error == 0); ptr = ebuf_data(nv->nv_ebuf, &size); while (size > 0) { /* * Minimum size at this point is size of nvhdr structure, * one character long name plus terminating '\0'. */ PJDLOG_ASSERT(size >= sizeof(*nvh) + 2); nvh = (struct nvhdr *)ptr; PJDLOG_ASSERT(NVH_SIZE(nvh) <= size); nv_swap(nvh, false); ptr += NVH_SIZE(nvh); size -= NVH_SIZE(nvh); } return (nv->nv_ebuf); } /* * Create nv structure based on ebuf received from the network. */ struct nv * nv_ntoh(struct ebuf *eb) { struct nv *nv; size_t extra; int rerrno; PJDLOG_ASSERT(eb != NULL); nv = malloc(sizeof(*nv)); if (nv == NULL) return (NULL); nv->nv_error = 0; nv->nv_ebuf = eb; nv->nv_magic = NV_MAGIC; if (nv_validate(nv, &extra) == -1) { rerrno = errno; nv->nv_magic = 0; free(nv); errno = rerrno; return (NULL); } /* * Remove extra zeros at the end of the buffer. */ ebuf_del_tail(eb, extra); return (nv); } #define NV_DEFINE_ADD(type, TYPE) \ void \ nv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...) \ { \ va_list nameap; \ \ va_start(nameap, namefmt); \ nv_addv(nv, (unsigned char *)&value, sizeof(value), \ NV_TYPE_##TYPE, namefmt, nameap); \ va_end(nameap); \ } NV_DEFINE_ADD(int8, INT8) NV_DEFINE_ADD(uint8, UINT8) NV_DEFINE_ADD(int16, INT16) NV_DEFINE_ADD(uint16, UINT16) NV_DEFINE_ADD(int32, INT32) NV_DEFINE_ADD(uint32, UINT32) NV_DEFINE_ADD(int64, INT64) NV_DEFINE_ADD(uint64, UINT64) #undef NV_DEFINE_ADD #define NV_DEFINE_ADD_ARRAY(type, TYPE) \ void \ nv_add_##type##_array(struct nv *nv, const type##_t *value, \ size_t nsize, const char *namefmt, ...) \ { \ va_list nameap; \ \ va_start(nameap, namefmt); \ nv_addv(nv, (const unsigned char *)value, \ sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt, \ nameap); \ va_end(nameap); \ } NV_DEFINE_ADD_ARRAY(int8, INT8) NV_DEFINE_ADD_ARRAY(uint8, UINT8) NV_DEFINE_ADD_ARRAY(int16, INT16) NV_DEFINE_ADD_ARRAY(uint16, UINT16) NV_DEFINE_ADD_ARRAY(int32, INT32) NV_DEFINE_ADD_ARRAY(uint32, UINT32) NV_DEFINE_ADD_ARRAY(int64, INT64) NV_DEFINE_ADD_ARRAY(uint64, UINT64) #undef NV_DEFINE_ADD_ARRAY void nv_add_string(struct nv *nv, const char *value, const char *namefmt, ...) { va_list nameap; size_t size; size = strlen(value) + 1; va_start(nameap, namefmt); nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING, namefmt, nameap); va_end(nameap); } void nv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...) { va_list valueap; va_start(valueap, valuefmt); nv_add_stringv(nv, name, valuefmt, valueap); va_end(valueap); } void nv_add_stringv(struct nv *nv, const char *name, const char *valuefmt, va_list valueap) { char *value; ssize_t size; size = vasprintf(&value, valuefmt, valueap); if (size == -1) { if (nv->nv_error == 0) nv->nv_error = ENOMEM; return; } size++; nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name); free(value); } #define NV_DEFINE_GET(type, TYPE) \ type##_t \ nv_get_##type(struct nv *nv, const char *namefmt, ...) \ { \ struct nvhdr *nvh; \ va_list nameap; \ type##_t value; \ \ va_start(nameap, namefmt); \ nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap); \ va_end(nameap); \ if (nvh == NULL) \ return (0); \ PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\ PJDLOG_ASSERT(sizeof(value) == nvh->nvh_dsize); \ bcopy(NVH_DATA(nvh), &value, sizeof(value)); \ \ return (value); \ } NV_DEFINE_GET(int8, INT8) NV_DEFINE_GET(uint8, UINT8) NV_DEFINE_GET(int16, INT16) NV_DEFINE_GET(uint16, UINT16) NV_DEFINE_GET(int32, INT32) NV_DEFINE_GET(uint32, UINT32) NV_DEFINE_GET(int64, INT64) NV_DEFINE_GET(uint64, UINT64) #undef NV_DEFINE_GET #define NV_DEFINE_GET_ARRAY(type, TYPE) \ const type##_t * \ nv_get_##type##_array(struct nv *nv, size_t *sizep, \ const char *namefmt, ...) \ { \ struct nvhdr *nvh; \ va_list nameap; \ \ va_start(nameap, namefmt); \ nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap); \ va_end(nameap); \ if (nvh == NULL) \ return (NULL); \ PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\ PJDLOG_ASSERT((nvh->nvh_dsize % sizeof(type##_t)) == 0); \ if (sizep != NULL) \ *sizep = nvh->nvh_dsize / sizeof(type##_t); \ return ((type##_t *)(void *)NVH_DATA(nvh)); \ } NV_DEFINE_GET_ARRAY(int8, INT8) NV_DEFINE_GET_ARRAY(uint8, UINT8) NV_DEFINE_GET_ARRAY(int16, INT16) NV_DEFINE_GET_ARRAY(uint16, UINT16) NV_DEFINE_GET_ARRAY(int32, INT32) NV_DEFINE_GET_ARRAY(uint32, UINT32) NV_DEFINE_GET_ARRAY(int64, INT64) NV_DEFINE_GET_ARRAY(uint64, UINT64) #undef NV_DEFINE_GET_ARRAY const char * nv_get_string(struct nv *nv, const char *namefmt, ...) { struct nvhdr *nvh; va_list nameap; char *str; va_start(nameap, namefmt); nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap); va_end(nameap); if (nvh == NULL) return (NULL); PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); PJDLOG_ASSERT(nvh->nvh_dsize >= 1); str = (char *)NVH_DATA(nvh); PJDLOG_ASSERT(str[nvh->nvh_dsize - 1] == '\0'); PJDLOG_ASSERT(strlen(str) == nvh->nvh_dsize - 1); return (str); } static bool nv_vexists(struct nv *nv, const char *namefmt, va_list nameap) { struct nvhdr *nvh; int snverror, serrno; if (nv == NULL) return (false); serrno = errno; snverror = nv->nv_error; nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap); errno = serrno; nv->nv_error = snverror; return (nvh != NULL); } bool nv_exists(struct nv *nv, const char *namefmt, ...) { va_list nameap; bool ret; va_start(nameap, namefmt); ret = nv_vexists(nv, namefmt, nameap); va_end(nameap); return (ret); } void nv_assert(struct nv *nv, const char *namefmt, ...) { va_list nameap; va_start(nameap, namefmt); PJDLOG_ASSERT(nv_vexists(nv, namefmt, nameap)); va_end(nameap); } /* * Dump content of the nv structure. */ void nv_dump(struct nv *nv) { struct nvhdr *nvh; unsigned char *data, *ptr; size_t dsize, size; unsigned int ii; bool swap; if (nv_validate(nv, NULL) == -1) { printf("error: %d\n", errno); return; } NV_CHECK(nv); PJDLOG_ASSERT(nv->nv_error == 0); ptr = ebuf_data(nv->nv_ebuf, &size); while (size > 0) { PJDLOG_ASSERT(size >= sizeof(*nvh) + 2); nvh = (struct nvhdr *)ptr; PJDLOG_ASSERT(size >= NVH_SIZE(nvh)); swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK); dsize = NVH_DSIZE(nvh); data = NVH_DATA(nvh); printf(" %s", nvh->nvh_name); switch (nvh->nvh_type & NV_TYPE_MASK) { case NV_TYPE_INT8: printf("(int8): %jd", (intmax_t)(*(int8_t *)data)); break; case NV_TYPE_UINT8: printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data)); break; case NV_TYPE_INT16: printf("(int16): %jd", swap ? (intmax_t)le16toh(*(int16_t *)(void *)data) : (intmax_t)*(int16_t *)(void *)data); break; case NV_TYPE_UINT16: printf("(uint16): %ju", swap ? (uintmax_t)le16toh(*(uint16_t *)(void *)data) : (uintmax_t)*(uint16_t *)(void *)data); break; case NV_TYPE_INT32: printf("(int32): %jd", swap ? (intmax_t)le32toh(*(int32_t *)(void *)data) : (intmax_t)*(int32_t *)(void *)data); break; case NV_TYPE_UINT32: printf("(uint32): %ju", swap ? (uintmax_t)le32toh(*(uint32_t *)(void *)data) : (uintmax_t)*(uint32_t *)(void *)data); break; case NV_TYPE_INT64: printf("(int64): %jd", swap ? (intmax_t)le64toh(*(int64_t *)(void *)data) : (intmax_t)*(int64_t *)(void *)data); break; case NV_TYPE_UINT64: printf("(uint64): %ju", swap ? (uintmax_t)le64toh(*(uint64_t *)(void *)data) : (uintmax_t)*(uint64_t *)(void *)data); break; case NV_TYPE_INT8_ARRAY: printf("(int8 array):"); for (ii = 0; ii < dsize; ii++) printf(" %jd", (intmax_t)((int8_t *)data)[ii]); break; case NV_TYPE_UINT8_ARRAY: printf("(uint8 array):"); for (ii = 0; ii < dsize; ii++) printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]); break; case NV_TYPE_INT16_ARRAY: printf("(int16 array):"); for (ii = 0; ii < dsize / 2; ii++) { printf(" %jd", swap ? (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) : (intmax_t)((int16_t *)(void *)data)[ii]); } break; case NV_TYPE_UINT16_ARRAY: printf("(uint16 array):"); for (ii = 0; ii < dsize / 2; ii++) { printf(" %ju", swap ? (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) : (uintmax_t)((uint16_t *)(void *)data)[ii]); } break; case NV_TYPE_INT32_ARRAY: printf("(int32 array):"); for (ii = 0; ii < dsize / 4; ii++) { printf(" %jd", swap ? (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) : (intmax_t)((int32_t *)(void *)data)[ii]); } break; case NV_TYPE_UINT32_ARRAY: printf("(uint32 array):"); for (ii = 0; ii < dsize / 4; ii++) { printf(" %ju", swap ? (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) : (uintmax_t)((uint32_t *)(void *)data)[ii]); } break; case NV_TYPE_INT64_ARRAY: printf("(int64 array):"); for (ii = 0; ii < dsize / 8; ii++) { printf(" %ju", swap ? (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : (uintmax_t)((uint64_t *)(void *)data)[ii]); } break; case NV_TYPE_UINT64_ARRAY: printf("(uint64 array):"); for (ii = 0; ii < dsize / 8; ii++) { printf(" %ju", swap ? (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : (uintmax_t)((uint64_t *)(void *)data)[ii]); } break; case NV_TYPE_STRING: printf("(string): %s", (char *)data); break; default: PJDLOG_ABORT("invalid condition"); } printf("\n"); ptr += NVH_SIZE(nvh); size -= NVH_SIZE(nvh); } } /* * Local routines below. */ static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type, const char *name) { static unsigned char align[7]; struct nvhdr *nvh; size_t namesize; if (nv == NULL) { errno = ENOMEM; return; } NV_CHECK(nv); namesize = strlen(name) + 1; nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8)); if (nvh == NULL) { if (nv->nv_error == 0) nv->nv_error = ENOMEM; return; } nvh->nvh_type = NV_ORDER_HOST | type; nvh->nvh_namesize = (uint8_t)namesize; nvh->nvh_dsize = (uint32_t)vsize; bcopy(name, nvh->nvh_name, namesize); /* Add header first. */ if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) == -1) { PJDLOG_ASSERT(errno != 0); if (nv->nv_error == 0) nv->nv_error = errno; free(nvh); return; } free(nvh); /* Add the actual data. */ if (ebuf_add_tail(nv->nv_ebuf, value, vsize) == -1) { PJDLOG_ASSERT(errno != 0); if (nv->nv_error == 0) nv->nv_error = errno; return; } /* Align the data (if needed). */ vsize = roundup2(vsize, 8) - vsize; if (vsize == 0) return; PJDLOG_ASSERT(vsize > 0 && vsize <= sizeof(align)); if (ebuf_add_tail(nv->nv_ebuf, align, vsize) == -1) { PJDLOG_ASSERT(errno != 0); if (nv->nv_error == 0) nv->nv_error = errno; return; } } static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type, const char *namefmt, va_list nameap) { char name[255]; size_t namesize; namesize = vsnprintf(name, sizeof(name), namefmt, nameap); PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name)); nv_add(nv, value, vsize, type, name); } static struct nvhdr * nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap) { char name[255]; struct nvhdr *nvh; unsigned char *ptr; size_t size, namesize; if (nv == NULL) { errno = ENOMEM; return (NULL); } NV_CHECK(nv); namesize = vsnprintf(name, sizeof(name), namefmt, nameap); PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name)); namesize++; ptr = ebuf_data(nv->nv_ebuf, &size); while (size > 0) { PJDLOG_ASSERT(size >= sizeof(*nvh) + 2); nvh = (struct nvhdr *)ptr; PJDLOG_ASSERT(size >= NVH_SIZE(nvh)); nv_swap(nvh, true); if (strcmp(nvh->nvh_name, name) == 0) { if (type != NV_TYPE_NONE && (nvh->nvh_type & NV_TYPE_MASK) != type) { errno = EINVAL; if (nv->nv_error == 0) nv->nv_error = EINVAL; return (NULL); } return (nvh); } ptr += NVH_SIZE(nvh); size -= NVH_SIZE(nvh); } errno = ENOENT; if (nv->nv_error == 0) nv->nv_error = ENOENT; return (NULL); } static void nv_swap(struct nvhdr *nvh, bool tohost) { unsigned char *data, *end, *p; size_t vsize; data = NVH_DATA(nvh); if (tohost) { if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST) return; nvh->nvh_dsize = le32toh(nvh->nvh_dsize); end = data + nvh->nvh_dsize; nvh->nvh_type &= ~NV_ORDER_MASK; nvh->nvh_type |= NV_ORDER_HOST; } else { if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK) return; end = data + nvh->nvh_dsize; nvh->nvh_dsize = htole32(nvh->nvh_dsize); nvh->nvh_type &= ~NV_ORDER_MASK; nvh->nvh_type |= NV_ORDER_NETWORK; } vsize = 0; switch (nvh->nvh_type & NV_TYPE_MASK) { case NV_TYPE_INT8: case NV_TYPE_UINT8: case NV_TYPE_INT8_ARRAY: case NV_TYPE_UINT8_ARRAY: break; case NV_TYPE_INT16: case NV_TYPE_UINT16: case NV_TYPE_INT16_ARRAY: case NV_TYPE_UINT16_ARRAY: if (vsize == 0) vsize = 2; /* FALLTHROUGH */ case NV_TYPE_INT32: case NV_TYPE_UINT32: case NV_TYPE_INT32_ARRAY: case NV_TYPE_UINT32_ARRAY: if (vsize == 0) vsize = 4; /* FALLTHROUGH */ case NV_TYPE_INT64: case NV_TYPE_UINT64: case NV_TYPE_INT64_ARRAY: case NV_TYPE_UINT64_ARRAY: if (vsize == 0) vsize = 8; for (p = data; p < end; p += vsize) { if (tohost) { switch (vsize) { case 2: *(uint16_t *)(void *)p = le16toh(*(uint16_t *)(void *)p); break; case 4: *(uint32_t *)(void *)p = le32toh(*(uint32_t *)(void *)p); break; case 8: *(uint64_t *)(void *)p = le64toh(*(uint64_t *)(void *)p); break; default: PJDLOG_ABORT("invalid condition"); } } else { switch (vsize) { case 2: *(uint16_t *)(void *)p = htole16(*(uint16_t *)(void *)p); break; case 4: *(uint32_t *)(void *)p = htole32(*(uint32_t *)(void *)p); break; case 8: *(uint64_t *)(void *)p = htole64(*(uint64_t *)(void *)p); break; default: PJDLOG_ABORT("invalid condition"); } } } break; case NV_TYPE_STRING: break; default: PJDLOG_ABORT("unrecognized type"); } } diff --git a/sbin/hastd/nv.h b/sbin/hastd/nv.h index d1214f2b29ec..657a66fe38eb 100644 --- a/sbin/hastd/nv.h +++ b/sbin/hastd/nv.h @@ -1,133 +1,132 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _NV_H_ #define _NV_H_ #include #include #include #include #include #include struct nv; struct nv *nv_alloc(void); void nv_free(struct nv *nv); int nv_error(const struct nv *nv); int nv_set_error(struct nv *nv, int error); int nv_validate(struct nv *nv, size_t *extrap); struct ebuf *nv_hton(struct nv *nv); struct nv *nv_ntoh(struct ebuf *eb); void nv_add_int8(struct nv *nv, int8_t value, const char *namefmt, ...) __printflike(3, 4); void nv_add_uint8(struct nv *nv, uint8_t value, const char *namefmt, ...) __printflike(3, 4); void nv_add_int16(struct nv *nv, int16_t value, const char *namefmt, ...) __printflike(3, 4); void nv_add_uint16(struct nv *nv, uint16_t value, const char *namefmt, ...) __printflike(3, 4); void nv_add_int32(struct nv *nv, int32_t value, const char *namefmt, ...) __printflike(3, 4); void nv_add_uint32(struct nv *nv, uint32_t value, const char *namefmt, ...) __printflike(3, 4); void nv_add_int64(struct nv *nv, int64_t value, const char *namefmt, ...) __printflike(3, 4); void nv_add_uint64(struct nv *nv, uint64_t value, const char *namefmt, ...) __printflike(3, 4); void nv_add_int8_array(struct nv *nv, const int8_t *value, size_t size, const char *namefmt, ...) __printflike(4, 5); void nv_add_uint8_array(struct nv *nv, const uint8_t *value, size_t size, const char *namefmt, ...) __printflike(4, 5); void nv_add_int16_array(struct nv *nv, const int16_t *value, size_t size, const char *namefmt, ...) __printflike(4, 5); void nv_add_uint16_array(struct nv *nv, const uint16_t *value, size_t size, const char *namefmt, ...) __printflike(4, 5); void nv_add_int32_array(struct nv *nv, const int32_t *value, size_t size, const char *namefmt, ...) __printflike(4, 5); void nv_add_uint32_array(struct nv *nv, const uint32_t *value, size_t size, const char *namefmt, ...) __printflike(4, 5); void nv_add_int64_array(struct nv *nv, const int64_t *value, size_t size, const char *namefmt, ...) __printflike(4, 5); void nv_add_uint64_array(struct nv *nv, const uint64_t *value, size_t size, const char *namefmt, ...) __printflike(4, 5); void nv_add_string(struct nv *nv, const char *value, const char *namefmt, ...) __printflike(3, 4); void nv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...) __printflike(3, 4); void nv_add_stringv(struct nv *nv, const char *name, const char *valuefmt, va_list valueap) __printflike(3, 0); int8_t nv_get_int8(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); uint8_t nv_get_uint8(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); int16_t nv_get_int16(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); uint16_t nv_get_uint16(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); int32_t nv_get_int32(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); uint32_t nv_get_uint32(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); int64_t nv_get_int64(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); uint64_t nv_get_uint64(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); const int8_t *nv_get_int8_array(struct nv *nv, size_t *sizep, const char *namefmt, ...) __printflike(3, 4); const uint8_t *nv_get_uint8_array(struct nv *nv, size_t *sizep, const char *namefmt, ...) __printflike(3, 4); const int16_t *nv_get_int16_array(struct nv *nv, size_t *sizep, const char *namefmt, ...) __printflike(3, 4); const uint16_t *nv_get_uint16_array(struct nv *nv, size_t *sizep, const char *namefmt, ...) __printflike(3, 4); const int32_t *nv_get_int32_array(struct nv *nv, size_t *sizep, const char *namefmt, ...) __printflike(3, 4); const uint32_t *nv_get_uint32_array(struct nv *nv, size_t *sizep, const char *namefmt, ...) __printflike(3, 4); const int64_t *nv_get_int64_array(struct nv *nv, size_t *sizep, const char *namefmt, ...) __printflike(3, 4); const uint64_t *nv_get_uint64_array(struct nv *nv, size_t *sizep, const char *namefmt, ...) __printflike(3, 4); const char *nv_get_string(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); bool nv_exists(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); void nv_assert(struct nv *nv, const char *namefmt, ...) __printflike(2, 3); void nv_dump(struct nv *nv); #endif /* !_NV_H_ */ diff --git a/sbin/hastd/proto.c b/sbin/hastd/proto.c index 93c9a064215d..70166271a747 100644 --- a/sbin/hastd/proto.c +++ b/sbin/hastd/proto.c @@ -1,445 +1,444 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "pjdlog.h" #include "proto.h" #include "proto_impl.h" #define PROTO_CONN_MAGIC 0x907041c struct proto_conn { int pc_magic; struct proto *pc_proto; void *pc_ctx; int pc_side; #define PROTO_SIDE_CLIENT 0 #define PROTO_SIDE_SERVER_LISTEN 1 #define PROTO_SIDE_SERVER_WORK 2 }; static TAILQ_HEAD(, proto) protos = TAILQ_HEAD_INITIALIZER(protos); void proto_register(struct proto *proto, bool isdefault) { static bool seen_default = false; if (!isdefault) TAILQ_INSERT_HEAD(&protos, proto, prt_next); else { PJDLOG_ASSERT(!seen_default); seen_default = true; TAILQ_INSERT_TAIL(&protos, proto, prt_next); } } static struct proto_conn * proto_alloc(struct proto *proto, int side) { struct proto_conn *conn; PJDLOG_ASSERT(proto != NULL); PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT || side == PROTO_SIDE_SERVER_LISTEN || side == PROTO_SIDE_SERVER_WORK); conn = malloc(sizeof(*conn)); if (conn != NULL) { conn->pc_proto = proto; conn->pc_side = side; conn->pc_magic = PROTO_CONN_MAGIC; } return (conn); } static void proto_free(struct proto_conn *conn) { PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT || conn->pc_side == PROTO_SIDE_SERVER_LISTEN || conn->pc_side == PROTO_SIDE_SERVER_WORK); PJDLOG_ASSERT(conn->pc_proto != NULL); bzero(conn, sizeof(*conn)); free(conn); } static int proto_common_setup(const char *srcaddr, const char *dstaddr, struct proto_conn **connp, int side) { struct proto *proto; struct proto_conn *conn; void *ctx; int ret; PJDLOG_ASSERT(side == PROTO_SIDE_CLIENT || side == PROTO_SIDE_SERVER_LISTEN); TAILQ_FOREACH(proto, &protos, prt_next) { if (side == PROTO_SIDE_CLIENT) { if (proto->prt_client == NULL) ret = -1; else ret = proto->prt_client(srcaddr, dstaddr, &ctx); } else /* if (side == PROTO_SIDE_SERVER_LISTEN) */ { if (proto->prt_server == NULL) ret = -1; else ret = proto->prt_server(dstaddr, &ctx); } /* * ret == 0 - success * ret == -1 - dstaddr is not for this protocol * ret > 0 - right protocol, but an error occurred */ if (ret >= 0) break; } if (proto == NULL) { /* Unrecognized address. */ errno = EINVAL; return (-1); } if (ret > 0) { /* An error occurred. */ errno = ret; return (-1); } conn = proto_alloc(proto, side); if (conn == NULL) { if (proto->prt_close != NULL) proto->prt_close(ctx); errno = ENOMEM; return (-1); } conn->pc_ctx = ctx; *connp = conn; return (0); } int proto_client(const char *srcaddr, const char *dstaddr, struct proto_conn **connp) { return (proto_common_setup(srcaddr, dstaddr, connp, PROTO_SIDE_CLIENT)); } int proto_connect(struct proto_conn *conn, int timeout) { int ret; PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_connect != NULL); PJDLOG_ASSERT(timeout >= -1); ret = conn->pc_proto->prt_connect(conn->pc_ctx, timeout); if (ret != 0) { errno = ret; return (-1); } return (0); } int proto_connect_wait(struct proto_conn *conn, int timeout) { int ret; PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_CLIENT); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_connect_wait != NULL); PJDLOG_ASSERT(timeout >= 0); ret = conn->pc_proto->prt_connect_wait(conn->pc_ctx, timeout); if (ret != 0) { errno = ret; return (-1); } return (0); } int proto_server(const char *addr, struct proto_conn **connp) { return (proto_common_setup(NULL, addr, connp, PROTO_SIDE_SERVER_LISTEN)); } int proto_accept(struct proto_conn *conn, struct proto_conn **newconnp) { struct proto_conn *newconn; int ret; PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_side == PROTO_SIDE_SERVER_LISTEN); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_accept != NULL); newconn = proto_alloc(conn->pc_proto, PROTO_SIDE_SERVER_WORK); if (newconn == NULL) return (-1); ret = conn->pc_proto->prt_accept(conn->pc_ctx, &newconn->pc_ctx); if (ret != 0) { proto_free(newconn); errno = ret; return (-1); } *newconnp = newconn; return (0); } int proto_send(const struct proto_conn *conn, const void *data, size_t size) { int ret; PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_send != NULL); ret = conn->pc_proto->prt_send(conn->pc_ctx, data, size, -1); if (ret != 0) { errno = ret; return (-1); } return (0); } int proto_recv(const struct proto_conn *conn, void *data, size_t size) { int ret; PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_recv != NULL); ret = conn->pc_proto->prt_recv(conn->pc_ctx, data, size, NULL); if (ret != 0) { errno = ret; return (-1); } return (0); } int proto_connection_send(const struct proto_conn *conn, struct proto_conn *mconn) { const char *protoname; int ret, fd; PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_send != NULL); PJDLOG_ASSERT(mconn != NULL); PJDLOG_ASSERT(mconn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(mconn->pc_proto != NULL); fd = proto_descriptor(mconn); PJDLOG_ASSERT(fd >= 0); protoname = mconn->pc_proto->prt_name; PJDLOG_ASSERT(protoname != NULL); ret = conn->pc_proto->prt_send(conn->pc_ctx, (const unsigned char *)protoname, strlen(protoname) + 1, fd); proto_close(mconn); if (ret != 0) { errno = ret; return (-1); } return (0); } int proto_connection_recv(const struct proto_conn *conn, bool client, struct proto_conn **newconnp) { char protoname[128]; struct proto *proto; struct proto_conn *newconn; int ret, fd; PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_recv != NULL); PJDLOG_ASSERT(newconnp != NULL); bzero(protoname, sizeof(protoname)); ret = conn->pc_proto->prt_recv(conn->pc_ctx, (unsigned char *)protoname, sizeof(protoname) - 1, &fd); if (ret != 0) { errno = ret; return (-1); } PJDLOG_ASSERT(fd >= 0); TAILQ_FOREACH(proto, &protos, prt_next) { if (strcmp(proto->prt_name, protoname) == 0) break; } if (proto == NULL) { errno = EINVAL; return (-1); } newconn = proto_alloc(proto, client ? PROTO_SIDE_CLIENT : PROTO_SIDE_SERVER_WORK); if (newconn == NULL) return (-1); PJDLOG_ASSERT(newconn->pc_proto->prt_wrap != NULL); ret = newconn->pc_proto->prt_wrap(fd, client, &newconn->pc_ctx); if (ret != 0) { proto_free(newconn); errno = ret; return (-1); } *newconnp = newconn; return (0); } int proto_descriptor(const struct proto_conn *conn) { PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_descriptor != NULL); return (conn->pc_proto->prt_descriptor(conn->pc_ctx)); } bool proto_address_match(const struct proto_conn *conn, const char *addr) { PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_address_match != NULL); return (conn->pc_proto->prt_address_match(conn->pc_ctx, addr)); } void proto_local_address(const struct proto_conn *conn, char *addr, size_t size) { PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_local_address != NULL); conn->pc_proto->prt_local_address(conn->pc_ctx, addr, size); } void proto_remote_address(const struct proto_conn *conn, char *addr, size_t size) { PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_remote_address != NULL); conn->pc_proto->prt_remote_address(conn->pc_ctx, addr, size); } int proto_timeout(const struct proto_conn *conn, int timeout) { struct timeval tv; int fd; PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_proto != NULL); fd = proto_descriptor(conn); if (fd == -1) return (-1); tv.tv_sec = timeout; tv.tv_usec = 0; if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) return (-1); if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) return (-1); return (0); } void proto_close(struct proto_conn *conn) { PJDLOG_ASSERT(conn != NULL); PJDLOG_ASSERT(conn->pc_magic == PROTO_CONN_MAGIC); PJDLOG_ASSERT(conn->pc_proto != NULL); PJDLOG_ASSERT(conn->pc_proto->prt_close != NULL); conn->pc_proto->prt_close(conn->pc_ctx); proto_free(conn); } diff --git a/sbin/hastd/proto.h b/sbin/hastd/proto.h index a7cecbe67479..729daa518d9d 100644 --- a/sbin/hastd/proto.h +++ b/sbin/hastd/proto.h @@ -1,61 +1,60 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _PROTO_H_ #define _PROTO_H_ #include /* bool */ #include /* size_t */ struct proto_conn; int proto_client(const char *srcaddr, const char *dstaddr, struct proto_conn **connp); int proto_connect(struct proto_conn *conn, int timeout); int proto_connect_wait(struct proto_conn *conn, int timeout); int proto_server(const char *addr, struct proto_conn **connp); int proto_accept(struct proto_conn *conn, struct proto_conn **newconnp); int proto_send(const struct proto_conn *conn, const void *data, size_t size); int proto_recv(const struct proto_conn *conn, void *data, size_t size); int proto_connection_send(const struct proto_conn *conn, struct proto_conn *mconn); int proto_connection_recv(const struct proto_conn *conn, bool client, struct proto_conn **newconnp); int proto_descriptor(const struct proto_conn *conn); bool proto_address_match(const struct proto_conn *conn, const char *addr); void proto_local_address(const struct proto_conn *conn, char *addr, size_t size); void proto_remote_address(const struct proto_conn *conn, char *addr, size_t size); int proto_timeout(const struct proto_conn *conn, int timeout); void proto_close(struct proto_conn *conn); #endif /* !_PROTO_H_ */ diff --git a/sbin/hastd/proto_impl.h b/sbin/hastd/proto_impl.h index 4f84f90c6f23..0a0074545f38 100644 --- a/sbin/hastd/proto_impl.h +++ b/sbin/hastd/proto_impl.h @@ -1,79 +1,78 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _PROTO_IMPL_H_ #define _PROTO_IMPL_H_ #include #include /* bool */ #include /* size_t */ #define __constructor __attribute__((constructor)) typedef int prt_client_t(const char *, const char *, void **); typedef int prt_connect_t(void *, int); typedef int prt_connect_wait_t(void *, int); typedef int prt_server_t(const char *, void **); typedef int prt_accept_t(void *, void **); typedef int prt_wrap_t(int, bool, void **); typedef int prt_send_t(void *, const unsigned char *, size_t, int); typedef int prt_recv_t(void *, unsigned char *, size_t, int *); typedef int prt_descriptor_t(const void *); typedef bool prt_address_match_t(const void *, const char *); typedef void prt_local_address_t(const void *, char *, size_t); typedef void prt_remote_address_t(const void *, char *, size_t); typedef void prt_close_t(void *); struct proto { const char *prt_name; prt_client_t *prt_client; prt_connect_t *prt_connect; prt_connect_wait_t *prt_connect_wait; prt_server_t *prt_server; prt_accept_t *prt_accept; prt_wrap_t *prt_wrap; prt_send_t *prt_send; prt_recv_t *prt_recv; prt_descriptor_t *prt_descriptor; prt_address_match_t *prt_address_match; prt_local_address_t *prt_local_address; prt_remote_address_t *prt_remote_address; prt_close_t *prt_close; TAILQ_ENTRY(proto) prt_next; }; void proto_register(struct proto *proto, bool isdefault); int proto_common_send(int sock, const unsigned char *data, size_t size, int fd); int proto_common_recv(int sock, unsigned char *data, size_t size, int *fdp); #endif /* !_PROTO_IMPL_H_ */ diff --git a/sbin/hastd/proto_socketpair.c b/sbin/hastd/proto_socketpair.c index 2d3d61693e1b..59ac9553ff6e 100644 --- a/sbin/hastd/proto_socketpair.c +++ b/sbin/hastd/proto_socketpair.c @@ -1,236 +1,235 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "pjdlog.h" #include "proto_impl.h" #define SP_CTX_MAGIC 0x50c3741 struct sp_ctx { int sp_magic; int sp_fd[2]; int sp_side; #define SP_SIDE_UNDEF 0 #define SP_SIDE_CLIENT 1 #define SP_SIDE_SERVER 2 }; static void sp_close(void *ctx); static int sp_client(const char *srcaddr, const char *dstaddr, void **ctxp) { struct sp_ctx *spctx; int ret; if (strcmp(dstaddr, "socketpair://") != 0) return (-1); PJDLOG_ASSERT(srcaddr == NULL); spctx = malloc(sizeof(*spctx)); if (spctx == NULL) return (errno); if (socketpair(PF_UNIX, SOCK_STREAM, 0, spctx->sp_fd) == -1) { ret = errno; free(spctx); return (ret); } spctx->sp_side = SP_SIDE_UNDEF; spctx->sp_magic = SP_CTX_MAGIC; *ctxp = spctx; return (0); } static int sp_send(void *ctx, const unsigned char *data, size_t size, int fd) { struct sp_ctx *spctx = ctx; int sock; PJDLOG_ASSERT(spctx != NULL); PJDLOG_ASSERT(spctx->sp_magic == SP_CTX_MAGIC); switch (spctx->sp_side) { case SP_SIDE_UNDEF: /* * If the first operation done by the caller is proto_send(), * we assume this is the client. */ /* FALLTHROUGH */ spctx->sp_side = SP_SIDE_CLIENT; /* Close other end. */ close(spctx->sp_fd[1]); spctx->sp_fd[1] = -1; case SP_SIDE_CLIENT: PJDLOG_ASSERT(spctx->sp_fd[0] >= 0); sock = spctx->sp_fd[0]; break; case SP_SIDE_SERVER: PJDLOG_ASSERT(spctx->sp_fd[1] >= 0); sock = spctx->sp_fd[1]; break; default: PJDLOG_ABORT("Invalid socket side (%d).", spctx->sp_side); } /* Someone is just trying to decide about side. */ if (data == NULL) return (0); return (proto_common_send(sock, data, size, fd)); } static int sp_recv(void *ctx, unsigned char *data, size_t size, int *fdp) { struct sp_ctx *spctx = ctx; int fd; PJDLOG_ASSERT(spctx != NULL); PJDLOG_ASSERT(spctx->sp_magic == SP_CTX_MAGIC); switch (spctx->sp_side) { case SP_SIDE_UNDEF: /* * If the first operation done by the caller is proto_recv(), * we assume this is the server. */ /* FALLTHROUGH */ spctx->sp_side = SP_SIDE_SERVER; /* Close other end. */ close(spctx->sp_fd[0]); spctx->sp_fd[0] = -1; case SP_SIDE_SERVER: PJDLOG_ASSERT(spctx->sp_fd[1] >= 0); fd = spctx->sp_fd[1]; break; case SP_SIDE_CLIENT: PJDLOG_ASSERT(spctx->sp_fd[0] >= 0); fd = spctx->sp_fd[0]; break; default: PJDLOG_ABORT("Invalid socket side (%d).", spctx->sp_side); } /* Someone is just trying to decide about side. */ if (data == NULL) return (0); return (proto_common_recv(fd, data, size, fdp)); } static int sp_descriptor(const void *ctx) { const struct sp_ctx *spctx = ctx; PJDLOG_ASSERT(spctx != NULL); PJDLOG_ASSERT(spctx->sp_magic == SP_CTX_MAGIC); PJDLOG_ASSERT(spctx->sp_side == SP_SIDE_CLIENT || spctx->sp_side == SP_SIDE_SERVER); switch (spctx->sp_side) { case SP_SIDE_CLIENT: PJDLOG_ASSERT(spctx->sp_fd[0] >= 0); return (spctx->sp_fd[0]); case SP_SIDE_SERVER: PJDLOG_ASSERT(spctx->sp_fd[1] >= 0); return (spctx->sp_fd[1]); } PJDLOG_ABORT("Invalid socket side (%d).", spctx->sp_side); } static void sp_close(void *ctx) { struct sp_ctx *spctx = ctx; PJDLOG_ASSERT(spctx != NULL); PJDLOG_ASSERT(spctx->sp_magic == SP_CTX_MAGIC); switch (spctx->sp_side) { case SP_SIDE_UNDEF: PJDLOG_ASSERT(spctx->sp_fd[0] >= 0); close(spctx->sp_fd[0]); spctx->sp_fd[0] = -1; PJDLOG_ASSERT(spctx->sp_fd[1] >= 0); close(spctx->sp_fd[1]); spctx->sp_fd[1] = -1; break; case SP_SIDE_CLIENT: PJDLOG_ASSERT(spctx->sp_fd[0] >= 0); close(spctx->sp_fd[0]); spctx->sp_fd[0] = -1; PJDLOG_ASSERT(spctx->sp_fd[1] == -1); break; case SP_SIDE_SERVER: PJDLOG_ASSERT(spctx->sp_fd[1] >= 0); close(spctx->sp_fd[1]); spctx->sp_fd[1] = -1; PJDLOG_ASSERT(spctx->sp_fd[0] == -1); break; default: PJDLOG_ABORT("Invalid socket side (%d).", spctx->sp_side); } spctx->sp_magic = 0; free(spctx); } static struct proto sp_proto = { .prt_name = "socketpair", .prt_client = sp_client, .prt_send = sp_send, .prt_recv = sp_recv, .prt_descriptor = sp_descriptor, .prt_close = sp_close }; static __constructor void sp_ctor(void) { proto_register(&sp_proto, false); } diff --git a/sbin/hastd/proto_uds.c b/sbin/hastd/proto_uds.c index 5bf4c64fe5f6..acb61a360180 100644 --- a/sbin/hastd/proto_uds.c +++ b/sbin/hastd/proto_uds.c @@ -1,361 +1,360 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include /* UDS - UNIX Domain Socket */ #include #include #include #include #include #include #include #include #include #include "pjdlog.h" #include "proto_impl.h" #define UDS_CTX_MAGIC 0xd541c struct uds_ctx { int uc_magic; struct sockaddr_un uc_sun; int uc_fd; int uc_side; #define UDS_SIDE_CLIENT 0 #define UDS_SIDE_SERVER_LISTEN 1 #define UDS_SIDE_SERVER_WORK 2 pid_t uc_owner; }; static void uds_close(void *ctx); static int uds_addr(const char *addr, struct sockaddr_un *sunp) { if (addr == NULL) return (-1); if (strncasecmp(addr, "uds://", 6) == 0) addr += 6; else if (strncasecmp(addr, "unix://", 7) == 0) addr += 7; else if (addr[0] == '/' && /* If it starts from /... */ strstr(addr, "://") == NULL)/* ...and there is no prefix... */ ; /* ...we assume its us. */ else return (-1); sunp->sun_family = AF_UNIX; if (strlcpy(sunp->sun_path, addr, sizeof(sunp->sun_path)) >= sizeof(sunp->sun_path)) { return (ENAMETOOLONG); } sunp->sun_len = SUN_LEN(sunp); return (0); } static int uds_common_setup(const char *addr, void **ctxp, int side) { struct uds_ctx *uctx; int ret; uctx = malloc(sizeof(*uctx)); if (uctx == NULL) return (errno); /* Parse given address. */ if ((ret = uds_addr(addr, &uctx->uc_sun)) != 0) { free(uctx); return (ret); } uctx->uc_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (uctx->uc_fd == -1) { ret = errno; free(uctx); return (ret); } uctx->uc_side = side; uctx->uc_owner = 0; uctx->uc_magic = UDS_CTX_MAGIC; *ctxp = uctx; return (0); } static int uds_client(const char *srcaddr, const char *dstaddr, void **ctxp) { int ret; ret = uds_common_setup(dstaddr, ctxp, UDS_SIDE_CLIENT); if (ret != 0) return (ret); PJDLOG_ASSERT(srcaddr == NULL); return (0); } static int uds_connect(void *ctx, int timeout) { struct uds_ctx *uctx = ctx; PJDLOG_ASSERT(uctx != NULL); PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC); PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT); PJDLOG_ASSERT(uctx->uc_fd >= 0); PJDLOG_ASSERT(timeout >= -1); if (connect(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun, sizeof(uctx->uc_sun)) == -1) { return (errno); } return (0); } static int uds_connect_wait(void *ctx, int timeout) { struct uds_ctx *uctx = ctx; PJDLOG_ASSERT(uctx != NULL); PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC); PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_CLIENT); PJDLOG_ASSERT(uctx->uc_fd >= 0); PJDLOG_ASSERT(timeout >= 0); return (0); } static int uds_server(const char *addr, void **ctxp) { struct uds_ctx *uctx; int ret; ret = uds_common_setup(addr, ctxp, UDS_SIDE_SERVER_LISTEN); if (ret != 0) return (ret); uctx = *ctxp; (void)unlink(uctx->uc_sun.sun_path); if (bind(uctx->uc_fd, (struct sockaddr *)&uctx->uc_sun, sizeof(uctx->uc_sun)) == -1) { ret = errno; uds_close(uctx); return (ret); } uctx->uc_owner = getpid(); if (listen(uctx->uc_fd, 8) == -1) { ret = errno; uds_close(uctx); return (ret); } return (0); } static int uds_accept(void *ctx, void **newctxp) { struct uds_ctx *uctx = ctx; struct uds_ctx *newuctx; socklen_t fromlen; int ret; PJDLOG_ASSERT(uctx != NULL); PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC); PJDLOG_ASSERT(uctx->uc_side == UDS_SIDE_SERVER_LISTEN); PJDLOG_ASSERT(uctx->uc_fd >= 0); newuctx = malloc(sizeof(*newuctx)); if (newuctx == NULL) return (errno); fromlen = sizeof(newuctx->uc_sun); newuctx->uc_fd = accept(uctx->uc_fd, (struct sockaddr *)&newuctx->uc_sun, &fromlen); if (newuctx->uc_fd == -1) { ret = errno; free(newuctx); return (ret); } newuctx->uc_side = UDS_SIDE_SERVER_WORK; newuctx->uc_magic = UDS_CTX_MAGIC; *newctxp = newuctx; return (0); } static int uds_send(void *ctx, const unsigned char *data, size_t size, int fd) { struct uds_ctx *uctx = ctx; PJDLOG_ASSERT(uctx != NULL); PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC); PJDLOG_ASSERT(uctx->uc_fd >= 0); return (proto_common_send(uctx->uc_fd, data, size, fd)); } static int uds_recv(void *ctx, unsigned char *data, size_t size, int *fdp) { struct uds_ctx *uctx = ctx; PJDLOG_ASSERT(uctx != NULL); PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC); PJDLOG_ASSERT(uctx->uc_fd >= 0); return (proto_common_recv(uctx->uc_fd, data, size, fdp)); } static int uds_descriptor(const void *ctx) { const struct uds_ctx *uctx = ctx; PJDLOG_ASSERT(uctx != NULL); PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC); return (uctx->uc_fd); } static void uds_local_address(const void *ctx, char *addr, size_t size) { const struct uds_ctx *uctx = ctx; struct sockaddr_un sun; socklen_t sunlen; PJDLOG_ASSERT(uctx != NULL); PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC); PJDLOG_ASSERT(addr != NULL); sunlen = sizeof(sun); if (getsockname(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) == -1) { PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); return; } PJDLOG_ASSERT(sun.sun_family == AF_UNIX); if (sun.sun_path[0] == '\0') { PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); return; } PJDLOG_VERIFY(snprintf(addr, size, "uds://%s", sun.sun_path) < (ssize_t)size); } static void uds_remote_address(const void *ctx, char *addr, size_t size) { const struct uds_ctx *uctx = ctx; struct sockaddr_un sun; socklen_t sunlen; PJDLOG_ASSERT(uctx != NULL); PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC); PJDLOG_ASSERT(addr != NULL); sunlen = sizeof(sun); if (getpeername(uctx->uc_fd, (struct sockaddr *)&sun, &sunlen) == -1) { PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); return; } PJDLOG_ASSERT(sun.sun_family == AF_UNIX); if (sun.sun_path[0] == '\0') { PJDLOG_VERIFY(strlcpy(addr, "N/A", size) < size); return; } snprintf(addr, size, "uds://%s", sun.sun_path); } static void uds_close(void *ctx) { struct uds_ctx *uctx = ctx; PJDLOG_ASSERT(uctx != NULL); PJDLOG_ASSERT(uctx->uc_magic == UDS_CTX_MAGIC); if (uctx->uc_fd >= 0) close(uctx->uc_fd); /* * Unlink the socket only if we are the owner and this is descriptor * we listen on. */ if (uctx->uc_side == UDS_SIDE_SERVER_LISTEN && uctx->uc_owner == getpid()) { PJDLOG_ASSERT(uctx->uc_sun.sun_path[0] != '\0'); if (unlink(uctx->uc_sun.sun_path) == -1) { pjdlog_errno(LOG_WARNING, "Unable to unlink socket file %s", uctx->uc_sun.sun_path); } } uctx->uc_owner = 0; uctx->uc_magic = 0; free(uctx); } static struct proto uds_proto = { .prt_name = "uds", .prt_client = uds_client, .prt_connect = uds_connect, .prt_connect_wait = uds_connect_wait, .prt_server = uds_server, .prt_accept = uds_accept, .prt_send = uds_send, .prt_recv = uds_recv, .prt_descriptor = uds_descriptor, .prt_local_address = uds_local_address, .prt_remote_address = uds_remote_address, .prt_close = uds_close }; static __constructor void uds_ctor(void) { proto_register(&uds_proto, false); } diff --git a/sbin/hastd/rangelock.c b/sbin/hastd/rangelock.c index 33aee39651a4..26025d1169ac 100644 --- a/sbin/hastd/rangelock.c +++ b/sbin/hastd/rangelock.c @@ -1,141 +1,140 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include "rangelock.h" #ifndef PJDLOG_ASSERT #include #define PJDLOG_ASSERT(...) assert(__VA_ARGS__) #endif #define RANGELOCKS_MAGIC 0x94310c struct rangelocks { int rls_magic; /* Magic value. */ TAILQ_HEAD(, rlock) rls_locks; /* List of locked ranges. */ }; struct rlock { off_t rl_start; off_t rl_end; TAILQ_ENTRY(rlock) rl_next; }; int rangelock_init(struct rangelocks **rlsp) { struct rangelocks *rls; PJDLOG_ASSERT(rlsp != NULL); rls = malloc(sizeof(*rls)); if (rls == NULL) return (-1); TAILQ_INIT(&rls->rls_locks); rls->rls_magic = RANGELOCKS_MAGIC; *rlsp = rls; return (0); } void rangelock_free(struct rangelocks *rls) { struct rlock *rl; PJDLOG_ASSERT(rls->rls_magic == RANGELOCKS_MAGIC); rls->rls_magic = 0; while ((rl = TAILQ_FIRST(&rls->rls_locks)) != NULL) { TAILQ_REMOVE(&rls->rls_locks, rl, rl_next); free(rl); } free(rls); } int rangelock_add(struct rangelocks *rls, off_t offset, off_t length) { struct rlock *rl; PJDLOG_ASSERT(rls->rls_magic == RANGELOCKS_MAGIC); rl = malloc(sizeof(*rl)); if (rl == NULL) return (-1); rl->rl_start = offset; rl->rl_end = offset + length; TAILQ_INSERT_TAIL(&rls->rls_locks, rl, rl_next); return (0); } void rangelock_del(struct rangelocks *rls, off_t offset, off_t length) { struct rlock *rl; PJDLOG_ASSERT(rls->rls_magic == RANGELOCKS_MAGIC); TAILQ_FOREACH(rl, &rls->rls_locks, rl_next) { if (rl->rl_start == offset && rl->rl_end == offset + length) break; } PJDLOG_ASSERT(rl != NULL); TAILQ_REMOVE(&rls->rls_locks, rl, rl_next); free(rl); } bool rangelock_islocked(struct rangelocks *rls, off_t offset, off_t length) { struct rlock *rl; off_t end; PJDLOG_ASSERT(rls->rls_magic == RANGELOCKS_MAGIC); end = offset + length; TAILQ_FOREACH(rl, &rls->rls_locks, rl_next) { if (rl->rl_start < end && rl->rl_end > offset) break; } return (rl != NULL); } diff --git a/sbin/hastd/rangelock.h b/sbin/hastd/rangelock.h index d01dbd755b40..4847887b6b7a 100644 --- a/sbin/hastd/rangelock.h +++ b/sbin/hastd/rangelock.h @@ -1,46 +1,45 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _RANGELOCK_H_ #define _RANGELOCK_H_ #include #include struct rangelocks; int rangelock_init(struct rangelocks **rlsp); void rangelock_free(struct rangelocks *rls); int rangelock_add(struct rangelocks *rls, off_t offset, off_t length); void rangelock_del(struct rangelocks *rls, off_t offset, off_t length); bool rangelock_islocked(struct rangelocks *rls, off_t offset, off_t length); #endif /* !_RANGELOCK_H_ */ diff --git a/sbin/hastd/synch.h b/sbin/hastd/synch.h index 9e03fcb8ca74..32463f836bd0 100644 --- a/sbin/hastd/synch.h +++ b/sbin/hastd/synch.h @@ -1,195 +1,194 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _SYNCH_H_ #define _SYNCH_H_ #include #include #include #include #include #include #ifndef PJDLOG_ASSERT #include #define PJDLOG_ASSERT(...) assert(__VA_ARGS__) #endif static __inline void mtx_init(pthread_mutex_t *lock) __requires_unlocked(*lock) { int error; error = pthread_mutex_init(lock, NULL); PJDLOG_ASSERT(error == 0); } static __inline void mtx_destroy(pthread_mutex_t *lock) __requires_unlocked(*lock) { int error; error = pthread_mutex_destroy(lock); PJDLOG_ASSERT(error == 0); } static __inline void mtx_lock(pthread_mutex_t *lock) __locks_exclusive(*lock) { int error; error = pthread_mutex_lock(lock); PJDLOG_ASSERT(error == 0); } static __inline bool mtx_trylock(pthread_mutex_t *lock) __trylocks_exclusive(true, *lock) { int error; error = pthread_mutex_trylock(lock); PJDLOG_ASSERT(error == 0 || error == EBUSY); return (error == 0); } static __inline void mtx_unlock(pthread_mutex_t *lock) __unlocks(*lock) { int error; error = pthread_mutex_unlock(lock); PJDLOG_ASSERT(error == 0); } static __inline bool mtx_owned(pthread_mutex_t *lock) { return (pthread_mutex_isowned_np(lock) != 0); } static __inline void rw_init(pthread_rwlock_t *lock) __requires_unlocked(*lock) { int error; error = pthread_rwlock_init(lock, NULL); PJDLOG_ASSERT(error == 0); } static __inline void rw_destroy(pthread_rwlock_t *lock) __requires_unlocked(*lock) { int error; error = pthread_rwlock_destroy(lock); PJDLOG_ASSERT(error == 0); } static __inline void rw_rlock(pthread_rwlock_t *lock) __locks_shared(*lock) { int error; error = pthread_rwlock_rdlock(lock); PJDLOG_ASSERT(error == 0); } static __inline void rw_wlock(pthread_rwlock_t *lock) __locks_exclusive(*lock) { int error; error = pthread_rwlock_wrlock(lock); PJDLOG_ASSERT(error == 0); } static __inline void rw_unlock(pthread_rwlock_t *lock) __unlocks(*lock) { int error; error = pthread_rwlock_unlock(lock); PJDLOG_ASSERT(error == 0); } static __inline void cv_init(pthread_cond_t *cv) { pthread_condattr_t attr; int error; error = pthread_condattr_init(&attr); PJDLOG_ASSERT(error == 0); error = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); PJDLOG_ASSERT(error == 0); error = pthread_cond_init(cv, &attr); PJDLOG_ASSERT(error == 0); error = pthread_condattr_destroy(&attr); PJDLOG_ASSERT(error == 0); } static __inline void cv_wait(pthread_cond_t *cv, pthread_mutex_t *lock) __requires_exclusive(*lock) { int error; error = pthread_cond_wait(cv, lock); PJDLOG_ASSERT(error == 0); } static __inline bool cv_timedwait(pthread_cond_t *cv, pthread_mutex_t *lock, int timeout) __requires_exclusive(*lock) { struct timespec ts; int error; if (timeout == 0) { cv_wait(cv, lock); return (false); } error = clock_gettime(CLOCK_MONOTONIC, &ts); PJDLOG_ASSERT(error == 0); ts.tv_sec += timeout; error = pthread_cond_timedwait(cv, lock, &ts); PJDLOG_ASSERT(error == 0 || error == ETIMEDOUT); return (error == ETIMEDOUT); } static __inline void cv_signal(pthread_cond_t *cv) { int error; error = pthread_cond_signal(cv); PJDLOG_ASSERT(error == 0); } static __inline void cv_broadcast(pthread_cond_t *cv) { int error; error = pthread_cond_broadcast(cv); PJDLOG_ASSERT(error == 0); } #endif /* !_SYNCH_H_ */ diff --git a/share/examples/hast/ucarp.sh b/share/examples/hast/ucarp.sh index 73253a295f53..67c7aecea44f 100755 --- a/share/examples/hast/ucarp.sh +++ b/share/examples/hast/ucarp.sh @@ -1,70 +1,69 @@ #!/bin/sh # # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2010 The FreeBSD Foundation -# All rights reserved. # # This software was developed by Pawel Jakub Dawidek under sponsorship from # the FreeBSD Foundation. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # Shared IP address, unused for now. addr="10.99.0.3" # Password for UCARP communication. pass="password" # First node IP and interface for UCARP communication. nodea_srcip="10.99.0.1" nodea_ifnet="bge0" # Second node IP and interface for UCARP communication. nodeb_srcip="10.99.0.2" nodeb_ifnet="em3" export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin vhid="1" upscript="/root/hast/sbin/hastd/vip-up.sh" downscript="/root/hast/sbin/hastd/vip-down.sh" ifconfig "${nodea_ifnet}" 2>/dev/null | grep -q "inet ${nodea_srcip} " if [ $? -eq 0 ]; then srcip="${nodea_srcip}" ifnet="${nodea_ifnet}" node="node A" fi ifconfig "${nodeb_ifnet}" 2>/dev/null | grep -q "inet ${nodeb_srcip} " if [ $? -eq 0 ]; then if [ -n "${srcip}" -o -n "${ifnet}" ]; then echo "Unable to determine which node is this (both match)." >/dev/stderr exit 1 fi srcip="${nodeb_srcip}" ifnet="${nodeb_ifnet}" node="node B" fi if [ -z "${srcip}" -o -z "${ifnet}" ]; then echo "Unable to determine which node is this (none match)." >/dev/stderr exit 1 fi ucarp -i ${ifnet} -s ${srcip} -v ${vhid} -a ${addr} -p ${pass} -u "${upscript}" -d "${downscript}" diff --git a/share/examples/hast/ucarp_down.sh b/share/examples/hast/ucarp_down.sh index 133d35c59d5b..df78de156b1e 100755 --- a/share/examples/hast/ucarp_down.sh +++ b/share/examples/hast/ucarp_down.sh @@ -1,99 +1,98 @@ #!/bin/sh # # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2010 The FreeBSD Foundation -# All rights reserved. # # This software was developed by Pawel Jakub Dawidek under sponsorship from # the FreeBSD Foundation. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # Resource name as defined in /etc/hast.conf. resource="test" # Supported file system types: UFS, ZFS fstype="UFS" # ZFS pool name. Required only when fstype == ZFS. pool="test" # File system mount point. Required only when fstype == UFS. mountpoint="/mnt/test" # Name of HAST provider as defined in /etc/hast.conf. # Required only when fstype == UFS. device="/dev/hast/${resource}" export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin # KIll UP script if it still runs in the background. sig="TERM" for i in `jot 30`; do pgid=`pgrep -f ucarp_up.sh | head -1` [ -n "${pgid}" ] || break kill -${sig} -- -${pgid} sig="KILL" sleep 1 done if [ -n "${pgid}" ]; then logger -p local0.error -t hast "UCARP UP process for resource ${resource} is still running after 30 seconds." exit 1 fi logger -p local0.debug -t hast "UCARP UP is not running." case "${fstype}" in UFS) mount | egrep -q "^${device} on " if [ $? -eq 0 ]; then # Forcibly unmount file system. out=`umount -f "${mountpoint}" 2>&1` if [ $? -ne 0 ]; then logger -p local0.error -t hast "Unable to unmount file system for resource ${resource}: ${out}." exit 1 fi logger -p local0.debug -t hast "File system for resource ${resource} unmounted." fi ;; ZFS) zpool list | egrep -q "^${pool} " if [ $? -eq 0 ]; then # Forcibly export file pool. out=`zpool export -f "${pool}" 2>&1` if [ $? -ne 0 ]; then logger -p local0.error -t hast "Unable to export pool for resource ${resource}: ${out}." exit 1 fi logger -p local0.debug -t hast "ZFS pool for resource ${resource} exported." fi ;; esac # Change role to secondary for our resource. out=`hastctl role secondary "${resource}" 2>&1` if [ $? -ne 0 ]; then logger -p local0.error -t hast "Unable to change to role to secondary for resource ${resource}: ${out}." exit 1 fi logger -p local0.debug -t hast "Role for resource ${resource} changed to secondary." logger -p local0.info -t hast "Successfully switched to secondary for resource ${resource}." exit 0 diff --git a/share/examples/hast/ucarp_up.sh b/share/examples/hast/ucarp_up.sh index 9f22b4205909..62dee23c12c7 100755 --- a/share/examples/hast/ucarp_up.sh +++ b/share/examples/hast/ucarp_up.sh @@ -1,106 +1,105 @@ #!/bin/sh # # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2010 The FreeBSD Foundation -# All rights reserved. # # This software was developed by Pawel Jakub Dawidek under sponsorship from # the FreeBSD Foundation. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # Resource name as defined in /etc/hast.conf. resource="test" # Supported file system types: UFS, ZFS fstype="UFS" # ZFS pool name. Required only when fstype == ZFS. pool="test" # File system mount point. Required only when fstype == UFS. mountpoint="/mnt/test" # Name of HAST provider as defined in /etc/hast.conf. device="/dev/hast/${resource}" export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin # If there is secondary worker process, it means that remote primary process is # still running. We have to wait for it to terminate. for i in `jot 30`; do pgrep -f "hastd: ${resource} \(secondary\)" >/dev/null 2>&1 || break sleep 1 done if pgrep -f "hastd: ${resource} \(secondary\)" >/dev/null 2>&1; then logger -p local0.error -t hast "Secondary process for resource ${resource} is still running after 30 seconds." exit 1 fi logger -p local0.debug -t hast "Secondary process in not running." # Change role to primary for our resource. out=`hastctl role primary "${resource}" 2>&1` if [ $? -ne 0 ]; then logger -p local0.error -t hast "Unable to change to role to primary for resource ${resource}: ${out}." exit 1 fi # Wait few seconds for provider to appear. for i in `jot 50`; do [ -c "${device}" ] && break sleep 0.1 done if [ ! -c "${device}" ]; then logger -p local0.error -t hast "Device ${device} didn't appear." exit 1 fi logger -p local0.debug -t hast "Role for resource ${resource} changed to primary." case "${fstype}" in UFS) # Check the file system. fsck -y -t ufs "${device}" >/dev/null 2>&1 if [ $? -ne 0 ]; then logger -p local0.error -t hast "File system check for resource ${resource} failed." exit 1 fi logger -p local0.debug -t hast "File system check for resource ${resource} finished." # Mount the file system. out=`mount -t ufs "${device}" "${mountpoint}" 2>&1` if [ $? -ne 0 ]; then logger -p local0.error -t hast "File system mount for resource ${resource} failed: ${out}." exit 1 fi logger -p local0.debug -t hast "File system for resource ${resource} mounted." ;; ZFS) # Import ZFS pool. Do it forcibly as it remembers hostid of # the other cluster node. out=`zpool import -f "${pool}" 2>&1` if [ $? -ne 0 ]; then logger -p local0.error -t hast "ZFS pool import for resource ${resource} failed: ${out}." exit 1 fi logger -p local0.debug -t hast "ZFS pool for resource ${resource} imported." ;; esac logger -p local0.info -t hast "Successfully switched to primary for resource ${resource}." exit 0 diff --git a/share/examples/kld/khelp/h_example.c b/share/examples/kld/khelp/h_example.c index fea7826a1fb4..92f0c885bbce 100644 --- a/share/examples/kld/khelp/h_example.c +++ b/share/examples/kld/khelp/h_example.c @@ -1,153 +1,152 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010-2011 The FreeBSD Foundation - * All rights reserved. * * This software was developed at the Centre for Advanced Internet * Architectures, Swinburne University of Technology, Melbourne, Australia by * Lawrence Stewart under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This example Khelp module uses the helper hook points available in the TCP * stack to calculate a per-connection count of inbound and outbound packets * when the connection is in the established state. The code is verbosely * documented in an attempt to explain how everything fits together. */ #include #include #include #include #include #include #include #include #include #include /* * Function prototype for our helper hook (man 9 hhook) compatible hook * function. */ static int example_hook(int hhook_type, int hhook_id, void *udata, void *ctx_data, void *hdata, struct osd *hosd); /* * Our per-connection persistent data storage struct. */ struct example { uint32_t est_in_count; uint32_t est_out_count; }; /* * Fill in the required bits of our module's struct helper (defined in * ). * * - Our helper will be storing persistent state for each TCP connection, so we * request the use the Object Specific Data (OSD) feature from the framework by * setting the HELPER_NEEDS_OSD flag. * * - Our helper is related to the TCP subsystem, so tell the Khelp framework * this by setting an appropriate class for the module. When a new TCP * connection is created, the Khelp framework takes care of associating helper * modules of the appropriate class with the new connection. */ struct helper example_helper = { .h_flags = HELPER_NEEDS_OSD, .h_classes = HELPER_CLASS_TCP }; /* * Set which helper hook points our module wants to hook by creating an array of * hookinfo structs (defined in ). We hook the TCP established * inbound/outbound hook points (TCP hhook points are defined in * ) with our example_hook() function. We don't require a user * data pointer to be passed to our hook function when called, so we set it to * NULL. */ struct hookinfo example_hooks[] = { { .hook_type = HHOOK_TYPE_TCP, .hook_id = HHOOK_TCP_EST_IN, .hook_udata = NULL, .hook_func = &example_hook }, { .hook_type = HHOOK_TYPE_TCP, .hook_id = HHOOK_TCP_EST_OUT, .hook_udata = NULL, .hook_func = &example_hook } }; /* * Very simple helper hook function. Here's a quick run through the arguments: * * - hhook_type and hhook_id are useful if you use a single function with many * hook points and want to know which hook point called the function. * * - udata will be NULL, because we didn't elect to pass a pointer in either of * the hookinfo structs we instantiated above in the example_hooks array. * * - ctx_data contains context specific data from the hook point call site. The * data type passed is subsystem dependent. In the case of TCP, the hook points * pass a pointer to a "struct tcp_hhook_data" (defined in ). * * - hdata is a pointer to the persistent per-object storage for our module. The * pointer is allocated automagically by the Khelp framework when the connection * is created, and comes from a dedicated UMA zone. It will never be NULL. * * - hosd can be used with the Khelp framework's khelp_get_osd() function to * access data belonging to a different Khelp module. */ static int example_hook(int hhook_type, int hhook_id, void *udata, void *ctx_data, void *hdata, struct osd *hosd) { struct example *data; data = hdata; if (hhook_id == HHOOK_TCP_EST_IN) data->est_in_count++; else if (hhook_id == HHOOK_TCP_EST_OUT) data->est_out_count++; return (0); } /* * We use a convenient macro which handles registering our module with the Khelp * framework. Note that Khelp modules which set the HELPER_NEEDS_OSD flag (i.e. * require persistent per-object storage) must use the KHELP_DECLARE_MOD_UMA() * macro. If you don't require per-object storage, use the KHELP_DECLARE_MOD() * macro instead. */ KHELP_DECLARE_MOD_UMA(example, &example_helper, example_hooks, 1, sizeof(struct example), NULL, NULL); diff --git a/tests/sys/fs/fusefs/unlink.cc b/tests/sys/fs/fusefs/unlink.cc index db54e286d85d..1d8a371649ee 100644 --- a/tests/sys/fs/fusefs/unlink.cc +++ b/tests/sys/fs/fusefs/unlink.cc @@ -1,238 +1,237 @@ /*- * Copyright (c) 2019 The FreeBSD Foundation - * All rights reserved. * * This software was developed by BFF Storage Systems, LLC under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ extern "C" { #include #include } #include "mockfs.hh" #include "utils.hh" using namespace testing; class Unlink: public FuseTest { public: void expect_lookup(const char *relpath, uint64_t ino, int times, int nlink=1) { EXPECT_LOOKUP(FUSE_ROOT_ID, relpath) .Times(times) .WillRepeatedly(Invoke( ReturnImmediate([=](auto in __unused, auto& out) { SET_OUT_HEADER_LEN(out, entry); out.body.entry.attr.mode = S_IFREG | 0644; out.body.entry.nodeid = ino; out.body.entry.attr.nlink = nlink; out.body.entry.attr_valid = UINT64_MAX; out.body.entry.attr.size = 0; }))); } }; /* * Unlinking a multiply linked file should update its ctime and nlink. This * could be handled simply by invalidating the attributes, necessitating a new * GETATTR, but we implement it in-kernel for efficiency's sake. */ TEST_F(Unlink, attr_cache) { const char FULLPATH0[] = "mountpoint/some_file.txt"; const char RELPATH0[] = "some_file.txt"; const char FULLPATH1[] = "mountpoint/other_file.txt"; const char RELPATH1[] = "other_file.txt"; uint64_t ino = 42; struct stat sb_old, sb_new; int fd1; expect_lookup(RELPATH0, ino, 1, 2); expect_lookup(RELPATH1, ino, 1, 2); expect_open(ino, 0, 1); expect_unlink(1, RELPATH0, 0); fd1 = open(FULLPATH1, O_RDONLY); ASSERT_LE(0, fd1) << strerror(errno); ASSERT_EQ(0, fstat(fd1, &sb_old)) << strerror(errno); ASSERT_EQ(0, unlink(FULLPATH0)) << strerror(errno); ASSERT_EQ(0, fstat(fd1, &sb_new)) << strerror(errno); EXPECT_NE(sb_old.st_ctime, sb_new.st_ctime); EXPECT_EQ(1u, sb_new.st_nlink); leak(fd1); } /* * A successful unlink should clear the parent directory's attribute cache, * because the fuse daemon should update its mtime and ctime */ TEST_F(Unlink, parent_attr_cache) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; struct stat sb; uint64_t ino = 42; Sequence seq; /* Use nlink=2 so we don't get a FUSE_FORGET */ expect_lookup(RELPATH, ino, 1, 2); EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_UNLINK && 0 == strcmp(RELPATH, in.body.unlink) && in.header.nodeid == FUSE_ROOT_ID); }, Eq(true)), _) ).InSequence(seq) .WillOnce(Invoke(ReturnErrno(0))); EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_GETATTR && in.header.nodeid == FUSE_ROOT_ID); }, Eq(true)), _) ).InSequence(seq) .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { SET_OUT_HEADER_LEN(out, attr); out.body.attr.attr.ino = FUSE_ROOT_ID; out.body.attr.attr.mode = S_IFDIR | 0755; out.body.attr.attr_valid = UINT64_MAX; }))); ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno); } TEST_F(Unlink, eperm) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; uint64_t ino = 42; expect_lookup(RELPATH, ino, 1); expect_unlink(1, RELPATH, EPERM); ASSERT_NE(0, unlink(FULLPATH)); ASSERT_EQ(EPERM, errno); } /* * Unlinking a file should expire its entry cache, even if it's multiply linked */ TEST_F(Unlink, entry_cache) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; uint64_t ino = 42; expect_lookup(RELPATH, ino, 2, 2); expect_unlink(1, RELPATH, 0); ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); } /* * Unlink a multiply-linked file. There should be no FUSE_FORGET because the * file is still linked. */ TEST_F(Unlink, multiply_linked) { const char FULLPATH0[] = "mountpoint/some_file.txt"; const char RELPATH0[] = "some_file.txt"; const char FULLPATH1[] = "mountpoint/other_file.txt"; const char RELPATH1[] = "other_file.txt"; uint64_t ino = 42; expect_lookup(RELPATH0, ino, 1, 2); expect_unlink(1, RELPATH0, 0); EXPECT_CALL(*m_mock, process( ResultOf([=](auto in) { return (in.header.opcode == FUSE_FORGET && in.header.nodeid == ino); }, Eq(true)), _) ).Times(0); expect_lookup(RELPATH1, ino, 1, 1); ASSERT_EQ(0, unlink(FULLPATH0)) << strerror(errno); /* * The final syscall simply ensures that no FUSE_FORGET was ever sent, * by scheduling an arbitrary different operation after a FUSE_FORGET * would've been sent. */ ASSERT_EQ(0, access(FULLPATH1, F_OK)) << strerror(errno); } TEST_F(Unlink, ok) { const char FULLPATH[] = "mountpoint/some_file.txt"; const char RELPATH[] = "some_file.txt"; uint64_t ino = 42; sem_t sem; ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); expect_lookup(RELPATH, ino, 1); expect_unlink(1, RELPATH, 0); expect_forget(ino, 1, &sem); ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); sem_wait(&sem); sem_destroy(&sem); } /* Unlink an open file */ TEST_F(Unlink, open_but_deleted) { const char FULLPATH0[] = "mountpoint/some_file.txt"; const char RELPATH0[] = "some_file.txt"; const char FULLPATH1[] = "mountpoint/other_file.txt"; const char RELPATH1[] = "other_file.txt"; uint64_t ino = 42; int fd; expect_lookup(RELPATH0, ino, 2); expect_open(ino, 0, 1); expect_unlink(1, RELPATH0, 0); expect_lookup(RELPATH1, ino, 1, 1); fd = open(FULLPATH0, O_RDWR); ASSERT_LE(0, fd) << strerror(errno); ASSERT_EQ(0, unlink(FULLPATH0)) << strerror(errno); /* * The final syscall simply ensures that no FUSE_FORGET was ever sent, * by scheduling an arbitrary different operation after a FUSE_FORGET * would've been sent. */ ASSERT_EQ(0, access(FULLPATH1, F_OK)) << strerror(errno); leak(fd); } diff --git a/tests/sys/kern/kern_copyin.c b/tests/sys/kern/kern_copyin.c index 6ad195d5d055..e677081a5c70 100644 --- a/tests/sys/kern/kern_copyin.c +++ b/tests/sys/kern/kern_copyin.c @@ -1,156 +1,155 @@ /*- * Copyright (c) 2015, 2020 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int scratch_file; static int copyin_checker(uintptr_t uaddr, size_t len) { ssize_t ret; ret = write(scratch_file, (const void *)uaddr, len); return (ret == -1 ? errno : 0); } #if __SIZEOF_POINTER__ == 8 /* * A slightly more direct path to calling copyin(), but without the ability * to specify a length. */ static int copyin_checker2(uintptr_t uaddr) { int ret; ret = fcntl(scratch_file, F_GETLK, (const void *)uaddr); return (ret == -1 ? errno : 0); } #endif static int get_vm_layout(struct kinfo_vm_layout *kvm) { size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_VM_LAYOUT; mib[3] = getpid(); len = sizeof(*kvm); return (sysctl(mib, nitems(mib), kvm, &len, NULL, 0)); } #define FMAX ULONG_MAX #if __SIZEOF_POINTER__ == 8 /* PR 257193 */ #define ADDR_SIGNED 0x800000c000000000 #endif ATF_TC_WITHOUT_HEAD(kern_copyin); ATF_TC_BODY(kern_copyin, tc) { char template[] = "copyin.XXXXXX"; struct kinfo_vm_layout kvm; uintptr_t maxuser; long page_size; void *addr; int error; addr = MAP_FAILED; error = get_vm_layout(&kvm); ATF_REQUIRE(error == 0); page_size = sysconf(_SC_PAGESIZE); ATF_REQUIRE(page_size != (long)-1); maxuser = kvm.kvm_max_user_addr; scratch_file = mkstemp(template); ATF_REQUIRE(scratch_file != -1); unlink(template); /* * Since the shared page address can be randomized we need to make * sure that something is mapped at the top of the user address space. * Otherwise reading bytes from maxuser-X will fail rendering this test * useless. */ if (kvm.kvm_shp_addr + kvm.kvm_shp_size < maxuser) { addr = mmap((void *)(maxuser - page_size), page_size, PROT_READ, MAP_ANON | MAP_FIXED, -1, 0); ATF_REQUIRE(addr != MAP_FAILED); } ATF_CHECK(copyin_checker(0, 0) == 0); ATF_CHECK(copyin_checker(maxuser - 10, 9) == 0); ATF_CHECK(copyin_checker(maxuser - 10, 10) == 0); ATF_CHECK(copyin_checker(maxuser - 10, 11) == EFAULT); ATF_CHECK(copyin_checker(maxuser - 1, 1) == 0); ATF_CHECK(copyin_checker(maxuser, 0) == 0); ATF_CHECK(copyin_checker(maxuser, 1) == EFAULT); ATF_CHECK(copyin_checker(maxuser, 2) == EFAULT); ATF_CHECK(copyin_checker(maxuser + 1, 0) == 0); ATF_CHECK(copyin_checker(maxuser + 1, 2) == EFAULT); ATF_CHECK(copyin_checker(FMAX - 10, 9) == EFAULT); ATF_CHECK(copyin_checker(FMAX - 10, 10) == EFAULT); ATF_CHECK(copyin_checker(FMAX - 10, 11) == EFAULT); #if __SIZEOF_POINTER__ == 8 ATF_CHECK(copyin_checker(ADDR_SIGNED, 1) == EFAULT); ATF_CHECK(copyin_checker2(ADDR_SIGNED) == EFAULT); #endif if (addr != MAP_FAILED) munmap(addr, PAGE_SIZE); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, kern_copyin); return (atf_no_error()); } diff --git a/tools/regression/capsicum/syscalls/cap_fcntls_limit.c b/tools/regression/capsicum/syscalls/cap_fcntls_limit.c index d26f3e3b7c36..b76b120a1650 100644 --- a/tools/regression/capsicum/syscalls/cap_fcntls_limit.c +++ b/tools/regression/capsicum/syscalls/cap_fcntls_limit.c @@ -1,543 +1,542 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "misc.h" static void fcntl_tests_0(int fd) { uint32_t fcntlrights; fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_ALL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); CHECK(fcntl(fd, F_SETFL, 0) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); errno = 0; CHECK(cap_fcntls_limit(fd, ~CAP_FCNTL_ALL) == -1); CHECK(errno == EINVAL); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); CHECK(fcntl(fd, F_SETFL, 0) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFL) == O_RDWR); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(cap_fcntls_limit(fd, 0) == 0); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); } static void fcntl_tests_1(int fd) { uint32_t fcntlrights; cap_rights_t rights; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); CAP_ALL(&rights); cap_rights_clear(&rights, CAP_FCNTL); CHECK(cap_rights_limit(fd, &rights) == 0); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); } static void fcntl_tests_2(int fd) { uint32_t fcntlrights; cap_rights_t rights; CAP_ALL(&rights); cap_rights_clear(&rights, CAP_FCNTL); CHECK(cap_rights_limit(fd, &rights) == 0); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); } static void fcntl_tests_send_0(int sock) { int fd; CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(cap_fcntls_limit(fd, 0) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); } static void fcntl_tests_recv_0(int sock) { uint32_t fcntlrights; int fd; CHECK(descriptor_recv(sock, &fd) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_ALL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); CHECK(fcntl(fd, F_SETFL, 0) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); CHECK(fcntl(fd, F_SETFL, 0) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFL) == O_RDWR); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); CHECK(close(fd) == 0); } int main(void) { int fd, pfd, sp[2]; pid_t pid; printf("1..870\n"); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); fcntl_tests_0(fd); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); fcntl_tests_1(fd); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); fcntl_tests_2(fd); CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = fork()) >= 0); if (pid == 0) { fcntl_tests_0(fd); CHECK(close(fd) == 0); exit(0); } else { CHECK(waitpid(pid, NULL, 0) == pid); fcntl_tests_0(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = fork()) >= 0); if (pid == 0) { sleep(1); fcntl_tests_0(fd); CHECK(close(fd) == 0); exit(0); } else { fcntl_tests_0(fd); CHECK(waitpid(pid, NULL, 0) == pid); } CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = pdfork(&pfd, 0)) >= 0); if (pid == 0) { fcntl_tests_1(fd); exit(0); } else { CHECK(pdwait(pfd) == 0); /* It fails with EBADF, which I believe is a bug. CHECK(close(pfd) == 0); */ fcntl_tests_1(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = pdfork(&pfd, 0)) >= 0); if (pid == 0) { sleep(1); fcntl_tests_1(fd); exit(0); } else { fcntl_tests_1(fd); CHECK(pdwait(pfd) == 0); /* It fails with EBADF, which I believe is a bug. CHECK(close(pfd) == 0); */ } CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = fork()) >= 0); if (pid == 0) { fcntl_tests_2(fd); exit(0); } else { CHECK(waitpid(pid, NULL, 0) == pid); fcntl_tests_2(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = fork()) >= 0); if (pid == 0) { sleep(1); fcntl_tests_2(fd); exit(0); } else { fcntl_tests_2(fd); CHECK(waitpid(pid, NULL, 0) == pid); } CHECK(close(fd) == 0); /* Send descriptors from parent to child. */ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); CHECK((pid = fork()) >= 0); if (pid == 0) { CHECK(close(sp[0]) == 0); fcntl_tests_recv_0(sp[1]); CHECK(close(sp[1]) == 0); exit(0); } else { CHECK(close(sp[1]) == 0); fcntl_tests_send_0(sp[0]); CHECK(waitpid(pid, NULL, 0) == pid); CHECK(close(sp[0]) == 0); } /* Send descriptors from child to parent. */ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); CHECK((pid = fork()) >= 0); if (pid == 0) { CHECK(close(sp[0]) == 0); fcntl_tests_send_0(sp[1]); CHECK(close(sp[1]) == 0); exit(0); } else { CHECK(close(sp[1]) == 0); fcntl_tests_recv_0(sp[0]); CHECK(waitpid(pid, NULL, 0) == pid); CHECK(close(sp[0]) == 0); } exit(0); } diff --git a/tools/regression/capsicum/syscalls/cap_getmode.c b/tools/regression/capsicum/syscalls/cap_getmode.c index 72596b615877..8d72199ab5f6 100644 --- a/tools/regression/capsicum/syscalls/cap_getmode.c +++ b/tools/regression/capsicum/syscalls/cap_getmode.c @@ -1,164 +1,163 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "misc.h" int main(void) { unsigned int mode; pid_t pid; int pfd; printf("1..27\n"); mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are not in capability mode. */ CHECK(mode == 0); /* Expect EFAULT. */ errno = 0; CHECK(cap_getmode(NULL) == -1); CHECK(errno == EFAULT); errno = 0; CHECK(cap_getmode((void *)(uintptr_t)0xdeadc0de) == -1); CHECK(errno == EFAULT); /* If parent is not in capability mode, child after fork() also won't be. */ pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are not in capability mode. */ CHECK(mode == 0); exit(0); default: if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid() failed"); } /* If parent is not in capability mode, child after pdfork() also won't be. */ pid = pdfork(&pfd, 0); switch (pid) { case -1: err(1, "pdfork() failed"); case 0: mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are not in capability mode. */ CHECK(mode == 0); exit(0); default: if (pdwait(pfd) == -1) err(1, "pdwait() failed"); close(pfd); } /* In capability mode... */ CHECK(cap_enter() == 0); mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are in capability mode. */ CHECK(mode == 1); /* Expect EFAULT. */ errno = 0; CHECK(cap_getmode(NULL) == -1); CHECK(errno == EFAULT); errno = 0; CHECK(cap_getmode((void *)(uintptr_t)0xdeadc0de) == -1); CHECK(errno == EFAULT); /* If parent is in capability mode, child after fork() also will be. */ pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are in capability mode. */ CHECK(mode == 1); exit(0); default: /* * wait(2) and friends are not permitted in the capability mode, * so we can only just wait for a while. */ sleep(1); } /* If parent is in capability mode, child after pdfork() also will be. */ pid = pdfork(&pfd, 0); switch (pid) { case -1: err(1, "pdfork() failed"); case 0: mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are in capability mode. */ CHECK(mode == 1); exit(0); default: if (pdwait(pfd) == -1) err(1, "pdwait() failed"); close(pfd); } exit(0); } diff --git a/tools/regression/capsicum/syscalls/cap_ioctls_limit.c b/tools/regression/capsicum/syscalls/cap_ioctls_limit.c index 68bdefb417ce..c8cf03207e57 100644 --- a/tools/regression/capsicum/syscalls/cap_ioctls_limit.c +++ b/tools/regression/capsicum/syscalls/cap_ioctls_limit.c @@ -1,467 +1,466 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" static void ioctl_tests_0(int fd) { unsigned long cmds[2]; CHECK(cap_ioctls_get(fd, NULL, 0) == CAP_IOCTLS_ALL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(ioctl(fd, FIONCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == 0); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == nitems(cmds)); CHECK((cmds[0] == FIOCLEX && cmds[1] == FIONCLEX) || (cmds[0] == FIONCLEX && cmds[1] == FIOCLEX)); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, 1) == nitems(cmds)); CHECK(cmds[0] == FIOCLEX || cmds[0] == FIONCLEX); CHECK(cmds[1] == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(ioctl(fd, FIONCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == 0); cmds[0] = FIOCLEX; CHECK(cap_ioctls_limit(fd, cmds, 1) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); CHECK(cmds[0] == FIOCLEX); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1); CHECK(errno == ENOTCAPABLE); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); CHECK(cmds[0] == FIOCLEX); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(cap_ioctls_limit(fd, NULL, 0) == 0); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); cmds[0] = FIOCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, 1) == -1); CHECK(errno == ENOTCAPABLE); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(ioctl(fd, FIOCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); } static void ioctl_tests_1(int fd) { unsigned long cmds[2]; cap_rights_t rights; cmds[0] = FIOCLEX; CHECK(cap_ioctls_limit(fd, cmds, 1) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); CHECK(cmds[0] == FIOCLEX); CHECK(cmds[1] == 0); CAP_ALL(&rights); cap_rights_clear(&rights, CAP_IOCTL); CHECK(cap_rights_limit(fd, &rights) == 0); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1); CHECK(errno == ENOTCAPABLE); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); cmds[0] = FIOCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, 1) == -1); CHECK(errno == ENOTCAPABLE); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(ioctl(fd, FIOCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); } static void ioctl_tests_2(int fd) { unsigned long cmds[2]; cap_rights_t rights; CAP_ALL(&rights); cap_rights_clear(&rights, CAP_IOCTL); CHECK(cap_rights_limit(fd, &rights) == 0); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1); CHECK(errno == ENOTCAPABLE); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); cmds[0] = FIOCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, 1) == -1); CHECK(errno == ENOTCAPABLE); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(ioctl(fd, FIOCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); } static void ioctl_tests_send_0(int sock) { unsigned long cmds[2]; int fd; CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); cmds[0] = FIOCLEX; CHECK(cap_ioctls_limit(fd, cmds, 1) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(cap_ioctls_limit(fd, NULL, 0) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); } static void ioctl_tests_recv_0(int sock) { unsigned long cmds[2]; int fd; CHECK(descriptor_recv(sock, &fd) == 0); CHECK(cap_ioctls_get(fd, NULL, 0) == CAP_IOCTLS_ALL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(ioctl(fd, FIONCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == nitems(cmds)); CHECK((cmds[0] == FIOCLEX && cmds[1] == FIONCLEX) || (cmds[0] == FIONCLEX && cmds[1] == FIOCLEX)); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(ioctl(fd, FIONCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); CHECK(cmds[0] == FIOCLEX); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(ioctl(fd, FIOCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(close(fd) == 0); } int main(void) { int fd, pfd, sp[2]; pid_t pid; printf("1..607\n"); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); ioctl_tests_0(fd); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); ioctl_tests_1(fd); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); ioctl_tests_2(fd); CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: ioctl_tests_0(fd); CHECK(close(fd) == 0); exit(0); default: if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid() failed"); ioctl_tests_0(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: sleep(1); ioctl_tests_0(fd); CHECK(close(fd) == 0); exit(0); default: ioctl_tests_0(fd); if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid() failed"); } CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = pdfork(&pfd, 0); switch (pid) { case -1: err(1, "pdfork() failed"); case 0: ioctl_tests_1(fd); exit(0); default: if (pdwait(pfd) == -1) err(1, "pdwait() failed"); close(pfd); ioctl_tests_1(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = pdfork(&pfd, 0); switch (pid) { case -1: err(1, "pdfork() failed"); case 0: sleep(1); ioctl_tests_1(fd); exit(0); default: ioctl_tests_1(fd); if (pdwait(pfd) == -1) err(1, "pdwait() failed"); close(pfd); } CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: ioctl_tests_2(fd); exit(0); default: if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid() failed"); ioctl_tests_2(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: sleep(1); ioctl_tests_2(fd); exit(0); default: ioctl_tests_2(fd); if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid() failed"); } CHECK(close(fd) == 0); /* Send descriptors from parent to child. */ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); CHECK((pid = fork()) >= 0); if (pid == 0) { CHECK(close(sp[0]) == 0); ioctl_tests_recv_0(sp[1]); CHECK(close(sp[1]) == 0); exit(0); } else { CHECK(close(sp[1]) == 0); ioctl_tests_send_0(sp[0]); CHECK(waitpid(pid, NULL, 0) == pid); CHECK(close(sp[0]) == 0); } /* Send descriptors from child to parent. */ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); CHECK((pid = fork()) >= 0); if (pid == 0) { CHECK(close(sp[0]) == 0); ioctl_tests_send_0(sp[1]); CHECK(close(sp[1]) == 0); exit(0); } else { CHECK(close(sp[1]) == 0); ioctl_tests_recv_0(sp[0]); CHECK(waitpid(pid, NULL, 0) == pid); CHECK(close(sp[0]) == 0); } exit(0); } diff --git a/tools/regression/capsicum/syscalls/misc.c b/tools/regression/capsicum/syscalls/misc.c index 704fd70b83ff..3549c1b3bf3c 100644 --- a/tools/regression/capsicum/syscalls/misc.c +++ b/tools/regression/capsicum/syscalls/misc.c @@ -1,125 +1,124 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "misc.h" int pdwait(int pfd) { fd_set fdset; FD_ZERO(&fdset); FD_SET(pfd, &fdset); return (select(pfd + 1, NULL, &fdset, NULL, NULL) == -1 ? -1 : 0); } int descriptor_send(int sock, int fd) { unsigned char ctrl[CMSG_SPACE(sizeof(fd))]; struct msghdr msg; struct cmsghdr *cmsg; assert(sock >= 0); assert(fd >= 0); bzero(&msg, sizeof(msg)); bzero(&ctrl, sizeof(ctrl)); msg.msg_iov = NULL; msg.msg_iovlen = 0; msg.msg_control = ctrl; msg.msg_controllen = sizeof(ctrl); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd)); if (sendmsg(sock, &msg, 0) == -1) return (errno); return (0); } int descriptor_recv(int sock, int *fdp) { unsigned char ctrl[CMSG_SPACE(sizeof(*fdp))]; struct msghdr msg; struct cmsghdr *cmsg; struct iovec iov; int val; assert(sock >= 0); assert(fdp != NULL); bzero(&msg, sizeof(msg)); bzero(&ctrl, sizeof(ctrl)); #if 1 /* * This doesn't really make sense, as we don't plan to receive any * data, but if no buffer is provided and recv(2) returns 0 without * control message. Must be kernel bug. */ iov.iov_base = &val; iov.iov_len = sizeof(val); msg.msg_iov = &iov; msg.msg_iovlen = 1; #else msg.msg_iov = NULL; msg.msg_iovlen = 0; #endif msg.msg_control = ctrl; msg.msg_controllen = sizeof(ctrl); if (recvmsg(sock, &msg, 0) == -1) return (errno); cmsg = CMSG_FIRSTHDR(&msg); if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { return (EINVAL); } bcopy(CMSG_DATA(cmsg), fdp, sizeof(*fdp)); return (0); } diff --git a/tools/regression/capsicum/syscalls/misc.h b/tools/regression/capsicum/syscalls/misc.h index 842500aff14d..22a0ab8094db 100644 --- a/tools/regression/capsicum/syscalls/misc.h +++ b/tools/regression/capsicum/syscalls/misc.h @@ -1,60 +1,59 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _MISC_H_ #define _MISC_H_ #define OK() do { \ int _serrno = errno; \ printf("ok # line %u\n", __LINE__); \ fflush(stdout); \ errno = _serrno; \ } while (0) #define NOK() do { \ int _serrno = errno; \ printf("not ok # line %u\n", __LINE__); \ fflush(stdout); \ errno = _serrno; \ } while (0) #define CHECK(cond) do { \ if ((cond)) \ OK(); \ else \ NOK(); \ } while (0) /* * This can be removed once pdwait4(2) is implemented. */ int pdwait(int pfd); int descriptor_send(int sock, int fd); int descriptor_recv(int sock, int *fdp); #endif /* !_MISC_H_ */ diff --git a/tools/test/popss/popss.c b/tools/test/popss/popss.c index 004ccb29c546..bb66f050beb1 100644 --- a/tools/test/popss/popss.c +++ b/tools/test/popss/popss.c @@ -1,182 +1,181 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: popss.c,v 1.28 2018/05/09 21:35:29 kostik Exp kostik $ * * cc -m32 -Wall -Wextra -O2 -g -o popss popss.c * Use as "popss ", where instruction is one of * bound, into, int1, int3, int80, syscall, sysenter. */ #include #include #include #include #include #include #include #include #include #include static u_long *stk; #define ITERATIONS 4 static void setup(pid_t child) { struct reg r; struct dbreg dbr; int error, i, status; error = waitpid(child, &status, WTRAPPED | WEXITED); if (error == -1) err(1, "waitpid 1"); error = ptrace(PT_GETREGS, child, (caddr_t)&r, 0); if (error == -1) err(1, "ptrace PT_GETREGS"); printf("child %d stopped eip %#x esp %#x\n", child, r.r_eip, r.r_esp); error = ptrace(PT_GETDBREGS, child, (caddr_t)&dbr, 0); if (error != 0) err(1, "ptrace PT_GETDBREGS"); dbr.dr[7] &= ~DBREG_DR7_MASK(0); dbr.dr[7] |= DBREG_DR7_SET(0, DBREG_DR7_LEN_4, DBREG_DR7_RDWR, DBREG_DR7_LOCAL_ENABLE | DBREG_DR7_GLOBAL_ENABLE); dbr.dr[0] = (uintptr_t)stk; error = ptrace(PT_SETDBREGS, child, (caddr_t)&dbr, 0); if (error != 0) err(1, "ptrace PT_SETDBREGS"); error = ptrace(PT_CONTINUE, child, (caddr_t)1, 0); if (error != 0) err(1, "ptrace PT_CONTINUE fire"); for (i = 0; i < ITERATIONS; i++) { error = waitpid(child, &status, WTRAPPED | WEXITED); if (error == -1) err(1, "waitpid 2"); if (WIFEXITED(status)) break; error = ptrace(PT_GETREGS, child, (caddr_t)&r, 0); if (error == -1) err(1, "ptrace PT_GETREGS"); error = ptrace(PT_GETDBREGS, child, (caddr_t)&dbr, 0); if (error != 0) err(1, "ptrace PT_GETDBREGS"); printf("child %d stopped eip %#x esp %#x dr0 %#x " "dr6 %#x dr7 %#x\n", child, r.r_eip, r.r_esp, dbr.dr[0], dbr.dr[6], dbr.dr[7]); error = ptrace(PT_CONTINUE, child, (caddr_t)1, 0); if (error == -1) err(1, "ptrace PT_CONTINUE tail"); } if (i == ITERATIONS) { kill(child, SIGKILL); ptrace(PT_DETACH, child, NULL, 0); } } static u_long tmpstk[1024 * 128]; static u_int read_ss(void) { u_int res; __asm volatile("movl\t%%ss,%0" : "=r" (res)); return (res); } #define PROLOGUE "int3;movl\t%0,%%esp;popl\t%%ss;" static void act(const char *cmd) { int error; static const int boundx[2] = {0, 1}; printf("child pid %d, stk at %p\n", getpid(), stk); *stk = read_ss(); error = ptrace(PT_TRACE_ME, 0, NULL, 0); if (error != 0) err(1, "ptrace PT_TRACE_ME"); if (strcmp(cmd, "bound") == 0) { /* XXX BOUND args order clang ias bug */ __asm volatile("int3;movl\t$11,%%eax;" "movl\t%0,%%esp;popl\t%%ss;bound\t%1,%%eax" : : "r" (stk), "m" (boundx) : "memory"); } else if (strcmp(cmd, "int1") == 0) { __asm volatile(PROLOGUE ".byte 0xf1" : : "r" (stk) : "memory"); } else if (strcmp(cmd, "int3") == 0) { __asm volatile(PROLOGUE "int3" : : "r" (stk) : "memory"); } else if (strcmp(cmd, "into") == 0) { __asm volatile("int3;movl\t$0x80000000,%%eax;" "addl\t%%eax,%%eax;movl\t%0,%%esp;popl\t%%ss;into" : : "r" (stk) : "memory"); } else if (strcmp(cmd, "int80") == 0) { __asm volatile(PROLOGUE "int\t$0x80" : : "r" (stk) : "memory"); } else if (strcmp(cmd, "syscall") == 0) { __asm volatile(PROLOGUE "syscall" : : "r" (stk) : "memory"); } else if (strcmp(cmd, "sysenter") == 0) { __asm volatile(PROLOGUE "sysenter" : : "r" (stk) : "memory"); } else { fprintf(stderr, "unknown instruction\n"); exit(1); } printf("ho\n"); } int main(int argc, char *argv[]) { int child; if (argc != 2) { printf( "Usage: popss [bound|int1|int3|into|int80|syscall|sysenter]\n"); exit(1); } stk = &tmpstk[nitems(tmpstk) - 1]; child = fork(); if (child == -1) err(1, "fork"); if (child == 0) act(argv[1]); else setup(child); } diff --git a/tools/test/vm86/vm86_test.c b/tools/test/vm86/vm86_test.c index 1057fc5c83d6..cbdb2c68a6df 100644 --- a/tools/test/vm86/vm86_test.c +++ b/tools/test/vm86/vm86_test.c @@ -1,121 +1,120 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include /* $Id: vm86_test.c,v 1.10 2018/05/12 11:35:58 kostik Exp kostik $ */ #include #include #include #include #include #include #include #include #include #include #include #include static u_int gs; static void sig_handler(int signo, siginfo_t *si __unused, void *ucp) { ucontext_t *uc; mcontext_t *mc; uc = ucp; mc = &uc->uc_mcontext; /* * Reload pointer to the TLS base, so that malloc inside * printf() works. */ load_gs(gs); printf("sig %d %%eax %#x %%ecx %#x %%eip %#x\n", signo, mc->mc_eax, mc->mc_ecx, mc->mc_eip); exit(0); } extern char vm86_code_start[], vm86_code_end[]; int main(void) { ucontext_t uc; struct sigaction sa; struct vm86_init_args va; stack_t ssa; char *vm86_code; gs = rgs(); memset(&ssa, 0, sizeof(ssa)); ssa.ss_size = PAGE_SIZE * 128; ssa.ss_sp = mmap(NULL, ssa.ss_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON, -1, 0); if (ssa.ss_sp == MAP_FAILED) err(1, "mmap sigstack"); if (sigaltstack(&ssa, NULL) == -1) err(1, "sigaltstack"); memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = sig_handler; sa.sa_flags = SA_SIGINFO | SA_ONSTACK; if (sigaction(SIGBUS, &sa, NULL) == -1) err(1, "sigaction SIGBUS"); if (sigaction(SIGSEGV, &sa, NULL) == -1) err(1, "sigaction SIGSEGV"); if (sigaction(SIGILL, &sa, NULL) == -1) err(1, "sigaction SIGILL"); vm86_code = mmap((void *)0x10000, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_FIXED, -1, 0); if (vm86_code == MAP_FAILED) err(1, "mmap"); memcpy(vm86_code, vm86_code_start, vm86_code_end - vm86_code_start); memset(&va, 0, sizeof(va)); if (i386_vm86(VM86_INIT, &va) == -1) err(1, "VM86_INIT"); memset(&uc, 0, sizeof(uc)); uc.uc_mcontext.mc_ecx = 0x2345; uc.uc_mcontext.mc_eflags = PSL_VM | PSL_USER; uc.uc_mcontext.mc_cs = uc.uc_mcontext.mc_ds = uc.uc_mcontext.mc_es = uc.uc_mcontext.mc_ss = (uintptr_t)vm86_code >> 4; uc.uc_mcontext.mc_esp = 0xfffe; sigreturn(&uc); } diff --git a/tools/test/vm86/vm86_test_asm.s b/tools/test/vm86/vm86_test_asm.s index 22e5cf70b7f7..0d76d2e77b1e 100644 --- a/tools/test/vm86/vm86_test_asm.s +++ b/tools/test/vm86/vm86_test_asm.s @@ -1,40 +1,39 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * $Id: vm86_test_asm.s,v 1.2 2018/05/12 11:39:00 kostik Exp kostik $ */ .text .code16 .globl vm86_code_start, vm86_code_end vm86_code_start: 1: inc %ax loop 1b ud2 vm86_code_end: diff --git a/tools/tools/vt/mkkfont/mkkfont.c b/tools/tools/vt/mkkfont/mkkfont.c index 3f92f926c029..ec4675cfcf1b 100644 --- a/tools/tools/vt/mkkfont/mkkfont.c +++ b/tools/tools/vt/mkkfont/mkkfont.c @@ -1,164 +1,163 @@ /*- * Copyright (c) 2009 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Ed Schouten under sponsorship from the * FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include static int print_glyphs(struct font_header *fh) { unsigned int gbytes, glyph_count, j, k, total; uint8_t *gbuf; gbytes = howmany(fh->fh_width, 8) * fh->fh_height; glyph_count = be32toh(fh->fh_glyph_count); printf("\nstatic uint8_t font_bytes[%u * %u] = {", glyph_count, gbytes); total = glyph_count * gbytes; gbuf = malloc(total); if (fread(gbuf, total, 1, stdin) != 1) { perror("glyph"); return (1); } for (j = 0; j < total; j += 12) { for (k = 0; k < 12 && k < total - j; k++) { printf(k == 0 ? "\n\t" : " "); printf("0x%02hhx,", gbuf[j + k]); } } free(gbuf); printf("\n};\n"); return (0); } static const char *map_names[4] = { "normal", "normal_right", "bold", "bold_right" }; static int print_mappings(struct font_header *fh, int map_index) { vfnt_map_t fm; unsigned int nmappings, i, col = 0; nmappings = be32toh(fh->fh_map_count[map_index]); if (nmappings == 0) return (0); printf("\nstatic vfnt_map_t font_mapping_%s[%u] = {", map_names[map_index], nmappings); for (i = 0; i < nmappings; i++) { if (fread(&fm, sizeof fm, 1, stdin) != 1) { perror("mapping"); return (1); } printf(col == 0 ? "\n\t" : " "); printf("{ 0x%04x, 0x%04x, 0x%02x },", be32toh(fm.vfm_src), be16toh(fm.vfm_dst), be16toh(fm.vfm_len)); col = (col + 1) % 2; } printf("\n};\n"); return (0); } static int print_info(struct font_header *fh) { unsigned int i; printf( "\nstruct vt_font vt_font_default = {\n" "\t.vf_width\t\t= %u,\n" "\t.vf_height\t\t= %u,\n" "\t.vf_bytes\t\t= font_bytes,\n", fh->fh_width, fh->fh_height); printf("\t.vf_map\t\t\t= {\n"); for (i = 0; i < 4; i++) { if (fh->fh_map_count[i] > 0) printf("\t\t\t\t font_mapping_%s,\n", map_names[i]); else printf("\t\t\t\t NULL,\n"); } printf("\t\t\t\t },\n"); printf("\t.vf_map_count\t\t= { %u, %u, %u, %u },\n", be32toh(fh->fh_map_count[0]), be32toh(fh->fh_map_count[1]), be32toh(fh->fh_map_count[2]), be32toh(fh->fh_map_count[3])); printf("\t.vf_refcount\t\t= 1,\n};\n"); return (0); } int main(int argc __unused, char *argv[] __unused) { struct font_header fh; unsigned int i; if (fread(&fh, sizeof fh, 1, stdin) != 1) { perror("font_header"); return (1); } if (memcmp(fh.fh_magic, "VFNT0002", 8) != 0) { fprintf(stderr, "Bad magic\n"); return (1); } printf("#include \n"); if (print_glyphs(&fh) != 0) return (1); for (i = 0; i < 4; i++) if (print_mappings(&fh, i) != 0) return (1); if (print_info(&fh) != 0) return (1); return (0); } diff --git a/usr.bin/proccontrol/proccontrol.1 b/usr.bin/proccontrol/proccontrol.1 index 5cb5d584f480..7ab917e4a61f 100644 --- a/usr.bin/proccontrol/proccontrol.1 +++ b/usr.bin/proccontrol/proccontrol.1 @@ -1,141 +1,140 @@ .\" Copyright (c) 2019 The FreeBSD Foundation, Inc. -.\" All rights reserved. .\" .\" This documentation was written by .\" Konstantin Belousov under sponsorship .\" from the FreeBSD Foundation. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .Dd October 5, 2023 .Dt PROCCONTROL 1 .Os .Sh NAME .Nm proccontrol .Nd Control some process execution aspects .Sh SYNOPSIS .Nm .Fl m Ar mode .Op Fl s Ar control .Op Fl q .Fl p Ar pid | command .Sh DESCRIPTION The .Nm command modifies the execution parameter of existing process specified by the .Ar pid argument, or starts execution of the new program .Ar command with the execution parameter set for it. .Pp Which execution parameter is changed, selected by the mandatory parameter .Ar mode . Possible values for .Ar mode are: .Bl -tag -width trapcap .It Ar aslr Control the Address Space Layout Randomization. Only applicable to the new process spawned. .It Ar trace Control the permission for debuggers to attach. Note that process is only allowed to enable tracing for itself, not for any other process. .It Ar trapcap Controls the signalling of capability mode access violations. .It Ar protmax Controls the implicit PROT_MAX application for .Xr mmap 2 . .It Ar nonewprivs Controls disabling the setuid and sgid bits for .Xr execve 2 . .It Ar wxmap Controls the write exclusive execute mode for mappings. .It Ar kpti Controls the KPTI enable, AMD64 only. .It Ar la48 Control limiting usermode process address space to 48 bits of address, AMD64 only, on machines capable of 57-bit addressing. .El .Pp The .Ar control specifies if the selected .Ar mode should be enabled or disabled. Possible values are .Ar enable and .Ar disable , with the default value being .Ar enable if not specified. See .Xr procctl 2 for detailed description of each mode effects and interaction with other process control facilities. .Pp The .Fl q switch makes the utility query and print the current setting for the selected mode. The .Fl q requires the query target process specification with .Fl p . .Sh EXIT STATUS .Ex -std .Sh EXAMPLES .Bl -bullet .It To disable debuggers attachment to the process 1020, execute .Dl "proccontrol -m trace -s disable -p 1020" .It To execute the .Xr uniq 1 program in a mode where capability access violations cause .Dv SIGTRAP delivery, do .Dl "proccontrol -m trapcap uniq" .It To query the current ASLR enablement mode for the running process 1020, do .Dl "proccontrol -m aslr -q -p 1020" .El .Sh SEE ALSO .Xr kill 2 , .Xr procctl 2 , .Xr ptrace 2 , .Xr mitigations 7 .Sh HISTORY The .Nm command appeared in .Fx 10.0 . .Sh AUTHORS The .Nm command and this manual page were written by .An Konstantin Belousov Aq Mt kib@freebsd.org under sponsorship from The FreeBSD Foundation. diff --git a/usr.bin/proccontrol/proccontrol.c b/usr.bin/proccontrol/proccontrol.c index 5b48ef2916f8..4b7543d63793 100644 --- a/usr.bin/proccontrol/proccontrol.c +++ b/usr.bin/proccontrol/proccontrol.c @@ -1,407 +1,406 @@ /*- * Copyright (c) 2016 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include enum { MODE_ASLR, MODE_INVALID, MODE_TRACE, MODE_TRAPCAP, MODE_PROTMAX, MODE_STACKGAP, MODE_NO_NEW_PRIVS, MODE_WXMAP, #ifdef PROC_KPTI_CTL MODE_KPTI, #endif #ifdef PROC_LA_CTL MODE_LA57, MODE_LA48, #endif }; static pid_t str2pid(const char *str) { pid_t res; char *tail; res = strtol(str, &tail, 0); if (*tail != '\0') { warnx("non-numeric pid"); return (-1); } return (res); } #ifdef PROC_KPTI_CTL #define KPTI_USAGE "|kpti" #else #define KPTI_USAGE #endif #ifdef PROC_LA_CTL #define LA_USAGE "|la48|la57" #else #define LA_USAGE #endif static void __dead2 usage(void) { fprintf(stderr, "Usage: proccontrol -m (aslr|protmax|trace|trapcap|" "stackgap|nonewprivs|wxmap"KPTI_USAGE LA_USAGE") [-q] " "[-s (enable|disable)] [-p pid | command]\n"); exit(1); } int main(int argc, char *argv[]) { int arg, ch, error, mode; pid_t pid; bool enable, do_command, query; mode = MODE_INVALID; enable = true; pid = -1; query = false; while ((ch = getopt(argc, argv, "m:qs:p:")) != -1) { switch (ch) { case 'm': if (strcmp(optarg, "aslr") == 0) mode = MODE_ASLR; else if (strcmp(optarg, "protmax") == 0) mode = MODE_PROTMAX; else if (strcmp(optarg, "trace") == 0) mode = MODE_TRACE; else if (strcmp(optarg, "trapcap") == 0) mode = MODE_TRAPCAP; else if (strcmp(optarg, "stackgap") == 0) mode = MODE_STACKGAP; else if (strcmp(optarg, "nonewprivs") == 0) mode = MODE_NO_NEW_PRIVS; else if (strcmp(optarg, "wxmap") == 0) mode = MODE_WXMAP; #ifdef PROC_KPTI_CTL else if (strcmp(optarg, "kpti") == 0) mode = MODE_KPTI; #endif #ifdef PROC_LA_CTL else if (strcmp(optarg, "la57") == 0) mode = MODE_LA57; else if (strcmp(optarg, "la48") == 0) mode = MODE_LA48; #endif else usage(); break; case 's': if (strcmp(optarg, "enable") == 0) enable = true; else if (strcmp(optarg, "disable") == 0) enable = false; else usage(); break; case 'p': pid = str2pid(optarg); break; case 'q': query = true; break; case '?': default: usage(); break; } } argc -= optind; argv += optind; do_command = argc != 0; if (do_command) { if (pid != -1 || query) usage(); pid = getpid(); } else if (pid == -1) { pid = getpid(); } if (query) { switch (mode) { case MODE_ASLR: error = procctl(P_PID, pid, PROC_ASLR_STATUS, &arg); break; case MODE_TRACE: error = procctl(P_PID, pid, PROC_TRACE_STATUS, &arg); break; case MODE_TRAPCAP: error = procctl(P_PID, pid, PROC_TRAPCAP_STATUS, &arg); break; case MODE_PROTMAX: error = procctl(P_PID, pid, PROC_PROTMAX_STATUS, &arg); break; case MODE_STACKGAP: error = procctl(P_PID, pid, PROC_STACKGAP_STATUS, &arg); break; case MODE_NO_NEW_PRIVS: error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_STATUS, &arg); break; case MODE_WXMAP: error = procctl(P_PID, pid, PROC_WXMAP_STATUS, &arg); break; #ifdef PROC_KPTI_CTL case MODE_KPTI: error = procctl(P_PID, pid, PROC_KPTI_STATUS, &arg); break; #endif #ifdef PROC_LA_CTL case MODE_LA57: case MODE_LA48: error = procctl(P_PID, pid, PROC_LA_STATUS, &arg); break; #endif default: usage(); break; } if (error != 0) err(1, "procctl status"); switch (mode) { case MODE_ASLR: switch (arg & ~PROC_ASLR_ACTIVE) { case PROC_ASLR_FORCE_ENABLE: printf("force enabled"); break; case PROC_ASLR_FORCE_DISABLE: printf("force disabled"); break; case PROC_ASLR_NOFORCE: printf("not forced"); break; } if ((arg & PROC_ASLR_ACTIVE) != 0) printf(", active\n"); else printf(", not active\n"); break; case MODE_TRACE: if (arg == -1) printf("disabled\n"); else if (arg == 0) printf("enabled, no debugger\n"); else printf("enabled, traced by %d\n", arg); break; case MODE_TRAPCAP: switch (arg) { case PROC_TRAPCAP_CTL_ENABLE: printf("enabled\n"); break; case PROC_TRAPCAP_CTL_DISABLE: printf("disabled\n"); break; } break; case MODE_PROTMAX: switch (arg & ~PROC_PROTMAX_ACTIVE) { case PROC_PROTMAX_FORCE_ENABLE: printf("force enabled"); break; case PROC_PROTMAX_FORCE_DISABLE: printf("force disabled"); break; case PROC_PROTMAX_NOFORCE: printf("not forced"); break; } if ((arg & PROC_PROTMAX_ACTIVE) != 0) printf(", active\n"); else printf(", not active\n"); break; case MODE_STACKGAP: switch (arg & (PROC_STACKGAP_ENABLE | PROC_STACKGAP_DISABLE)) { case PROC_STACKGAP_ENABLE: printf("enabled\n"); break; case PROC_STACKGAP_DISABLE: printf("disabled\n"); break; } switch (arg & (PROC_STACKGAP_ENABLE_EXEC | PROC_STACKGAP_DISABLE_EXEC)) { case PROC_STACKGAP_ENABLE_EXEC: printf("enabled after exec\n"); break; case PROC_STACKGAP_DISABLE_EXEC: printf("disabled after exec\n"); break; } break; case MODE_NO_NEW_PRIVS: switch (arg) { case PROC_NO_NEW_PRIVS_ENABLE: printf("enabled\n"); break; case PROC_NO_NEW_PRIVS_DISABLE: printf("disabled\n"); break; } break; case MODE_WXMAP: if ((arg & PROC_WX_MAPPINGS_PERMIT) != 0) printf("enabled"); else printf("disabled"); if ((arg & PROC_WX_MAPPINGS_DISALLOW_EXEC) != 0) printf(", disabled on exec"); if ((arg & PROC_WXORX_ENFORCE) != 0) printf(", wxorx enforced"); printf("\n"); break; #ifdef PROC_KPTI_CTL case MODE_KPTI: switch (arg & ~PROC_KPTI_STATUS_ACTIVE) { case PROC_KPTI_CTL_ENABLE_ON_EXEC: printf("enabled"); break; case PROC_KPTI_CTL_DISABLE_ON_EXEC: printf("disabled"); break; } if ((arg & PROC_KPTI_STATUS_ACTIVE) != 0) printf(", active\n"); else printf(", not active\n"); break; #endif #ifdef PROC_LA_CTL case MODE_LA57: case MODE_LA48: switch (arg & ~(PROC_LA_STATUS_LA48 | PROC_LA_STATUS_LA57)) { case PROC_LA_CTL_LA48_ON_EXEC: printf("la48 on exec"); break; case PROC_LA_CTL_LA57_ON_EXEC: printf("la57 on exec"); break; case PROC_LA_CTL_DEFAULT_ON_EXEC: printf("default on exec"); break; } if ((arg & PROC_LA_STATUS_LA48) != 0) printf(", la48 active\n"); else if ((arg & PROC_LA_STATUS_LA57) != 0) printf(", la57 active\n"); break; #endif } } else { switch (mode) { case MODE_ASLR: arg = enable ? PROC_ASLR_FORCE_ENABLE : PROC_ASLR_FORCE_DISABLE; error = procctl(P_PID, pid, PROC_ASLR_CTL, &arg); break; case MODE_TRACE: arg = enable ? PROC_TRACE_CTL_ENABLE : PROC_TRACE_CTL_DISABLE; error = procctl(P_PID, pid, PROC_TRACE_CTL, &arg); break; case MODE_TRAPCAP: arg = enable ? PROC_TRAPCAP_CTL_ENABLE : PROC_TRAPCAP_CTL_DISABLE; error = procctl(P_PID, pid, PROC_TRAPCAP_CTL, &arg); break; case MODE_PROTMAX: arg = enable ? PROC_PROTMAX_FORCE_ENABLE : PROC_PROTMAX_FORCE_DISABLE; error = procctl(P_PID, pid, PROC_PROTMAX_CTL, &arg); break; case MODE_STACKGAP: arg = enable ? PROC_STACKGAP_ENABLE_EXEC : (PROC_STACKGAP_DISABLE | PROC_STACKGAP_DISABLE_EXEC); error = procctl(P_PID, pid, PROC_STACKGAP_CTL, &arg); break; case MODE_NO_NEW_PRIVS: arg = enable ? PROC_NO_NEW_PRIVS_ENABLE : PROC_NO_NEW_PRIVS_DISABLE; error = procctl(P_PID, pid, PROC_NO_NEW_PRIVS_CTL, &arg); break; case MODE_WXMAP: arg = enable ? PROC_WX_MAPPINGS_PERMIT : PROC_WX_MAPPINGS_DISALLOW_EXEC; error = procctl(P_PID, pid, PROC_WXMAP_CTL, &arg); break; #ifdef PROC_KPTI_CTL case MODE_KPTI: arg = enable ? PROC_KPTI_CTL_ENABLE_ON_EXEC : PROC_KPTI_CTL_DISABLE_ON_EXEC; error = procctl(P_PID, pid, PROC_KPTI_CTL, &arg); break; #endif #ifdef PROC_LA_CTL case MODE_LA57: arg = enable ? PROC_LA_CTL_LA57_ON_EXEC : PROC_LA_CTL_DEFAULT_ON_EXEC; error = procctl(P_PID, pid, PROC_LA_CTL, &arg); break; case MODE_LA48: arg = enable ? PROC_LA_CTL_LA48_ON_EXEC : PROC_LA_CTL_DEFAULT_ON_EXEC; error = procctl(P_PID, pid, PROC_LA_CTL, &arg); break; #endif default: usage(); break; } if (error != 0) err(1, "procctl ctl"); if (do_command) { error = execvp(argv[0], argv); err(1, "exec"); } } exit(0); }