1c501d73cSMariusz Zaborski /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 328b6f7c8SMariusz Zaborski * 41608c46eSMark Johnston * Copyright (c) 2013, 2018 The FreeBSD Foundation 5c501d73cSMariusz Zaborski * 6c501d73cSMariusz Zaborski * This software was developed by Pawel Jakub Dawidek under sponsorship from 7c501d73cSMariusz Zaborski * the FreeBSD Foundation. 8c501d73cSMariusz Zaborski * 91608c46eSMark Johnston * Portions of this software were developed by Mark Johnston 101608c46eSMark Johnston * under sponsorship from the FreeBSD Foundation. 111608c46eSMark Johnston * 12c501d73cSMariusz Zaborski * Redistribution and use in source and binary forms, with or without 13c501d73cSMariusz Zaborski * modification, are permitted provided that the following conditions 14c501d73cSMariusz Zaborski * are met: 15c501d73cSMariusz Zaborski * 1. Redistributions of source code must retain the above copyright 16c501d73cSMariusz Zaborski * notice, this list of conditions and the following disclaimer. 17c501d73cSMariusz Zaborski * 2. Redistributions in binary form must reproduce the above copyright 18c501d73cSMariusz Zaborski * notice, this list of conditions and the following disclaimer in the 19c501d73cSMariusz Zaborski * documentation and/or other materials provided with the distribution. 20c501d73cSMariusz Zaborski * 21c501d73cSMariusz Zaborski * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 22c501d73cSMariusz Zaborski * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23c501d73cSMariusz Zaborski * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24c501d73cSMariusz Zaborski * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 25c501d73cSMariusz Zaborski * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26c501d73cSMariusz Zaborski * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27c501d73cSMariusz Zaborski * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28c501d73cSMariusz Zaborski * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29c501d73cSMariusz Zaborski * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30c501d73cSMariusz Zaborski * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31c501d73cSMariusz Zaborski * SUCH DAMAGE. 32c501d73cSMariusz Zaborski */ 33c501d73cSMariusz Zaborski 341608c46eSMark Johnston #include <sys/param.h> 351608c46eSMark Johnston #include <sys/cnv.h> 361608c46eSMark Johnston #include <sys/dnv.h> 37c501d73cSMariusz Zaborski #include <sys/nv.h> 381608c46eSMark Johnston #include <sys/sysctl.h> 39c501d73cSMariusz Zaborski 40c501d73cSMariusz Zaborski #include <assert.h> 41c501d73cSMariusz Zaborski #include <errno.h> 42c501d73cSMariusz Zaborski #include <stdlib.h> 43c501d73cSMariusz Zaborski #include <string.h> 44c501d73cSMariusz Zaborski 45c501d73cSMariusz Zaborski #include <libcasper.h> 46c501d73cSMariusz Zaborski #include <libcasper_service.h> 47c501d73cSMariusz Zaborski 48c501d73cSMariusz Zaborski #include "cap_sysctl.h" 49c501d73cSMariusz Zaborski 501608c46eSMark Johnston /* 511608c46eSMark Johnston * Limit interface. 521608c46eSMark Johnston */ 531608c46eSMark Johnston 541608c46eSMark Johnston struct cap_sysctl_limit { 551608c46eSMark Johnston cap_channel_t *chan; 561608c46eSMark Johnston nvlist_t *nv; 571608c46eSMark Johnston }; 581608c46eSMark Johnston 591608c46eSMark Johnston cap_sysctl_limit_t * 601608c46eSMark Johnston cap_sysctl_limit_init(cap_channel_t *chan) 61c501d73cSMariusz Zaborski { 621608c46eSMark Johnston cap_sysctl_limit_t *limit; 631608c46eSMark Johnston int error; 641608c46eSMark Johnston 651608c46eSMark Johnston limit = malloc(sizeof(*limit)); 661608c46eSMark Johnston if (limit != NULL) { 671608c46eSMark Johnston limit->chan = chan; 681608c46eSMark Johnston limit->nv = nvlist_create(NV_FLAG_NO_UNIQUE); 691608c46eSMark Johnston if (limit->nv == NULL) { 701608c46eSMark Johnston error = errno; 711608c46eSMark Johnston free(limit); 721608c46eSMark Johnston limit = NULL; 731608c46eSMark Johnston errno = error; 741608c46eSMark Johnston } 751608c46eSMark Johnston } 761608c46eSMark Johnston return (limit); 771608c46eSMark Johnston } 781608c46eSMark Johnston 791608c46eSMark Johnston cap_sysctl_limit_t * 801608c46eSMark Johnston cap_sysctl_limit_name(cap_sysctl_limit_t *limit, const char *name, int flags) 811608c46eSMark Johnston { 821608c46eSMark Johnston nvlist_t *lnv; 831608c46eSMark Johnston size_t mibsz; 841608c46eSMark Johnston int error, mib[CTL_MAXNAME]; 851608c46eSMark Johnston 861608c46eSMark Johnston lnv = nvlist_create(0); 871608c46eSMark Johnston if (lnv == NULL) { 881608c46eSMark Johnston error = errno; 891608c46eSMark Johnston if (limit->nv != NULL) 901608c46eSMark Johnston nvlist_destroy(limit->nv); 911608c46eSMark Johnston free(limit); 921608c46eSMark Johnston errno = error; 931608c46eSMark Johnston return (NULL); 941608c46eSMark Johnston } 951608c46eSMark Johnston nvlist_add_string(lnv, "name", name); 961608c46eSMark Johnston nvlist_add_number(lnv, "operation", flags); 971608c46eSMark Johnston 981608c46eSMark Johnston mibsz = nitems(mib); 991608c46eSMark Johnston error = cap_sysctlnametomib(limit->chan, name, mib, &mibsz); 1001608c46eSMark Johnston if (error == 0) 1011608c46eSMark Johnston nvlist_add_binary(lnv, "mib", mib, mibsz * sizeof(int)); 1021608c46eSMark Johnston 1031608c46eSMark Johnston nvlist_move_nvlist(limit->nv, "limit", lnv); 1041608c46eSMark Johnston return (limit); 1051608c46eSMark Johnston } 1061608c46eSMark Johnston 1071608c46eSMark Johnston cap_sysctl_limit_t * 1082750f1b9SConrad Meyer cap_sysctl_limit_mib(cap_sysctl_limit_t *limit, const int *mibp, u_int miblen, 1091608c46eSMark Johnston int flags) 1101608c46eSMark Johnston { 1111608c46eSMark Johnston nvlist_t *lnv; 1121608c46eSMark Johnston int error; 1131608c46eSMark Johnston 1141608c46eSMark Johnston lnv = nvlist_create(0); 1151608c46eSMark Johnston if (lnv == NULL) { 1161608c46eSMark Johnston error = errno; 1171608c46eSMark Johnston if (limit->nv != NULL) 1181608c46eSMark Johnston nvlist_destroy(limit->nv); 1191608c46eSMark Johnston free(limit); 1201608c46eSMark Johnston errno = error; 1211608c46eSMark Johnston return (NULL); 1221608c46eSMark Johnston } 1231608c46eSMark Johnston nvlist_add_binary(lnv, "mib", mibp, miblen * sizeof(int)); 1241608c46eSMark Johnston nvlist_add_number(lnv, "operation", flags); 1251608c46eSMark Johnston nvlist_add_nvlist(limit->nv, "limit", lnv); 1261608c46eSMark Johnston return (limit); 1271608c46eSMark Johnston } 1281608c46eSMark Johnston 1291608c46eSMark Johnston int 1301608c46eSMark Johnston cap_sysctl_limit(cap_sysctl_limit_t *limit) 1311608c46eSMark Johnston { 1321608c46eSMark Johnston cap_channel_t *chan; 1331608c46eSMark Johnston nvlist_t *lnv; 1341608c46eSMark Johnston 1351608c46eSMark Johnston chan = limit->chan; 1361608c46eSMark Johnston lnv = limit->nv; 1371608c46eSMark Johnston free(limit); 1381608c46eSMark Johnston 1391608c46eSMark Johnston /* cap_limit_set(3) will always free the nvlist. */ 1401608c46eSMark Johnston return (cap_limit_set(chan, lnv)); 1411608c46eSMark Johnston } 1421608c46eSMark Johnston 1431608c46eSMark Johnston /* 1441608c46eSMark Johnston * Service interface. 1451608c46eSMark Johnston */ 1461608c46eSMark Johnston 1471608c46eSMark Johnston static int 1481608c46eSMark Johnston do_sysctl(cap_channel_t *chan, nvlist_t *nvl, void *oldp, size_t *oldlenp, 1491608c46eSMark Johnston const void *newp, size_t newlen) 1501608c46eSMark Johnston { 151c501d73cSMariusz Zaborski const uint8_t *retoldp; 152c501d73cSMariusz Zaborski size_t oldlen; 1531608c46eSMark Johnston int error; 1541608c46eSMark Johnston uint8_t operation; 155c501d73cSMariusz Zaborski 156c501d73cSMariusz Zaborski operation = 0; 1571608c46eSMark Johnston if (oldlenp != NULL) 158c501d73cSMariusz Zaborski operation |= CAP_SYSCTL_READ; 159c501d73cSMariusz Zaborski if (newp != NULL) 160c501d73cSMariusz Zaborski operation |= CAP_SYSCTL_WRITE; 161c501d73cSMariusz Zaborski nvlist_add_number(nvl, "operation", (uint64_t)operation); 162c501d73cSMariusz Zaborski if (oldp == NULL && oldlenp != NULL) 163c501d73cSMariusz Zaborski nvlist_add_null(nvl, "justsize"); 164c501d73cSMariusz Zaborski else if (oldlenp != NULL) 165c501d73cSMariusz Zaborski nvlist_add_number(nvl, "oldlen", (uint64_t)*oldlenp); 166c501d73cSMariusz Zaborski if (newp != NULL) 167c501d73cSMariusz Zaborski nvlist_add_binary(nvl, "newp", newp, newlen); 1681608c46eSMark Johnston 1694fc0a279SMariusz Zaborski nvl = cap_xfer_nvlist(chan, nvl); 170c501d73cSMariusz Zaborski if (nvl == NULL) 171c501d73cSMariusz Zaborski return (-1); 1721608c46eSMark Johnston error = (int)dnvlist_get_number(nvl, "error", 0); 1731608c46eSMark Johnston if (error != 0) { 174c501d73cSMariusz Zaborski nvlist_destroy(nvl); 1751608c46eSMark Johnston errno = error; 176c501d73cSMariusz Zaborski return (-1); 177c501d73cSMariusz Zaborski } 178c501d73cSMariusz Zaborski 179c501d73cSMariusz Zaborski if (oldp == NULL && oldlenp != NULL) { 180c501d73cSMariusz Zaborski *oldlenp = (size_t)nvlist_get_number(nvl, "oldlen"); 181c501d73cSMariusz Zaborski } else if (oldp != NULL) { 182c501d73cSMariusz Zaborski retoldp = nvlist_get_binary(nvl, "oldp", &oldlen); 183c501d73cSMariusz Zaborski memcpy(oldp, retoldp, oldlen); 184c501d73cSMariusz Zaborski if (oldlenp != NULL) 185c501d73cSMariusz Zaborski *oldlenp = oldlen; 186c501d73cSMariusz Zaborski } 1871608c46eSMark Johnston 188c501d73cSMariusz Zaborski nvlist_destroy(nvl); 189c501d73cSMariusz Zaborski 190c501d73cSMariusz Zaborski return (0); 191c501d73cSMariusz Zaborski } 192c501d73cSMariusz Zaborski 1931608c46eSMark Johnston int 1941608c46eSMark Johnston cap_sysctl(cap_channel_t *chan, const int *name, u_int namelen, void *oldp, 1951608c46eSMark Johnston size_t *oldlenp, const void *newp, size_t newlen) 1961608c46eSMark Johnston { 1971608c46eSMark Johnston nvlist_t *req; 1981608c46eSMark Johnston 1991608c46eSMark Johnston req = nvlist_create(0); 2001608c46eSMark Johnston nvlist_add_string(req, "cmd", "sysctl"); 2011608c46eSMark Johnston nvlist_add_binary(req, "mib", name, (size_t)namelen * sizeof(int)); 2021608c46eSMark Johnston return (do_sysctl(chan, req, oldp, oldlenp, newp, newlen)); 2031608c46eSMark Johnston } 2041608c46eSMark Johnston 2051608c46eSMark Johnston int 2061608c46eSMark Johnston cap_sysctlbyname(cap_channel_t *chan, const char *name, void *oldp, 2071608c46eSMark Johnston size_t *oldlenp, const void *newp, size_t newlen) 2081608c46eSMark Johnston { 2091608c46eSMark Johnston nvlist_t *req; 2101608c46eSMark Johnston 2111608c46eSMark Johnston req = nvlist_create(0); 2121608c46eSMark Johnston nvlist_add_string(req, "cmd", "sysctlbyname"); 2131608c46eSMark Johnston nvlist_add_string(req, "name", name); 2141608c46eSMark Johnston return (do_sysctl(chan, req, oldp, oldlenp, newp, newlen)); 2151608c46eSMark Johnston } 2161608c46eSMark Johnston 2171608c46eSMark Johnston int 2181608c46eSMark Johnston cap_sysctlnametomib(cap_channel_t *chan, const char *name, int *mibp, 2191608c46eSMark Johnston size_t *sizep) 2201608c46eSMark Johnston { 2211608c46eSMark Johnston nvlist_t *req; 2221608c46eSMark Johnston const void *mib; 2231608c46eSMark Johnston size_t mibsz; 2241608c46eSMark Johnston int error; 2251608c46eSMark Johnston 2261608c46eSMark Johnston req = nvlist_create(0); 2271608c46eSMark Johnston nvlist_add_string(req, "cmd", "sysctlnametomib"); 2281608c46eSMark Johnston nvlist_add_string(req, "name", name); 2291608c46eSMark Johnston nvlist_add_number(req, "operation", 0); 2301608c46eSMark Johnston nvlist_add_number(req, "size", (uint64_t)*sizep); 2311608c46eSMark Johnston 2321608c46eSMark Johnston req = cap_xfer_nvlist(chan, req); 2331608c46eSMark Johnston if (req == NULL) 2341608c46eSMark Johnston return (-1); 2351608c46eSMark Johnston error = (int)dnvlist_get_number(req, "error", 0); 2361608c46eSMark Johnston if (error != 0) { 2371608c46eSMark Johnston nvlist_destroy(req); 2381608c46eSMark Johnston errno = error; 2391608c46eSMark Johnston return (-1); 2401608c46eSMark Johnston } 2411608c46eSMark Johnston 2421608c46eSMark Johnston mib = nvlist_get_binary(req, "mib", &mibsz); 2431608c46eSMark Johnston *sizep = mibsz / sizeof(int); 2441608c46eSMark Johnston 2451608c46eSMark Johnston memcpy(mibp, mib, mibsz); 2461608c46eSMark Johnston 2471608c46eSMark Johnston nvlist_destroy(req); 2481608c46eSMark Johnston 2491608c46eSMark Johnston return (0); 2501608c46eSMark Johnston } 2511608c46eSMark Johnston 252c501d73cSMariusz Zaborski /* 2531608c46eSMark Johnston * Service implementation. 2541608c46eSMark Johnston */ 2551608c46eSMark Johnston 2561608c46eSMark Johnston /* 2571608c46eSMark Johnston * Validate a sysctl description. This must consist of an nvlist with either a 2581608c46eSMark Johnston * binary "mib" field or a string "name", and an operation. 259c501d73cSMariusz Zaborski */ 260c501d73cSMariusz Zaborski static int 2611608c46eSMark Johnston sysctl_valid(const nvlist_t *nvl, bool limit) 262c501d73cSMariusz Zaborski { 263c501d73cSMariusz Zaborski const char *name; 264c501d73cSMariusz Zaborski void *cookie; 265c501d73cSMariusz Zaborski int type; 2661608c46eSMark Johnston size_t size; 2671608c46eSMark Johnston unsigned int field, fields; 268c501d73cSMariusz Zaborski 269c501d73cSMariusz Zaborski /* NULL nvl is of course invalid. */ 270c501d73cSMariusz Zaborski if (nvl == NULL) 271c501d73cSMariusz Zaborski return (EINVAL); 272c501d73cSMariusz Zaborski if (nvlist_error(nvl) != 0) 273c501d73cSMariusz Zaborski return (nvlist_error(nvl)); 274c501d73cSMariusz Zaborski 275c501d73cSMariusz Zaborski #define HAS_NAME 0x01 2761608c46eSMark Johnston #define HAS_MIB 0x02 2771608c46eSMark Johnston #define HAS_ID (HAS_NAME | HAS_MIB) 2781608c46eSMark Johnston #define HAS_OPERATION 0x04 279c501d73cSMariusz Zaborski 280c501d73cSMariusz Zaborski fields = 0; 281c501d73cSMariusz Zaborski cookie = NULL; 282c501d73cSMariusz Zaborski while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { 2831608c46eSMark Johnston if ((strcmp(name, "name") == 0 && type == NV_TYPE_STRING) || 2841608c46eSMark Johnston (strcmp(name, "mib") == 0 && type == NV_TYPE_BINARY)) { 2851608c46eSMark Johnston if (strcmp(name, "mib") == 0) { 2861608c46eSMark Johnston /* A MIB must be an array of integers. */ 2871608c46eSMark Johnston (void)cnvlist_get_binary(cookie, &size); 2881608c46eSMark Johnston if (size % sizeof(int) != 0) 289c501d73cSMariusz Zaborski return (EINVAL); 2901608c46eSMark Johnston field = HAS_MIB; 2911608c46eSMark Johnston } else 2921608c46eSMark Johnston field = HAS_NAME; 2931608c46eSMark Johnston 2941608c46eSMark Johnston /* 2951608c46eSMark Johnston * A limit may contain both a name and a MIB identifier. 2961608c46eSMark Johnston */ 2971608c46eSMark Johnston if ((fields & field) != 0 || 2981608c46eSMark Johnston (!limit && (fields & HAS_ID) != 0)) 299c501d73cSMariusz Zaborski return (EINVAL); 3001608c46eSMark Johnston fields |= field; 301c501d73cSMariusz Zaborski } else if (strcmp(name, "operation") == 0) { 3021608c46eSMark Johnston uint64_t mask, operation; 303c501d73cSMariusz Zaborski 304c501d73cSMariusz Zaborski if (type != NV_TYPE_NUMBER) 305c501d73cSMariusz Zaborski return (EINVAL); 3061608c46eSMark Johnston 3071608c46eSMark Johnston operation = cnvlist_get_number(cookie); 3081608c46eSMark Johnston 309c501d73cSMariusz Zaborski /* 3101608c46eSMark Johnston * Requests can only include the RDWR flags; limits may 3111608c46eSMark Johnston * also include the RECURSIVE flag. 312c501d73cSMariusz Zaborski */ 3131608c46eSMark Johnston mask = limit ? (CAP_SYSCTL_RDWR | 3141608c46eSMark Johnston CAP_SYSCTL_RECURSIVE) : CAP_SYSCTL_RDWR; 315b3bec79dSKyle Evans if ((operation & ~mask) != 0 || 3161608c46eSMark Johnston (operation & CAP_SYSCTL_RDWR) == 0) 317c501d73cSMariusz Zaborski return (EINVAL); 318c501d73cSMariusz Zaborski /* Only one 'operation' can be present. */ 319c501d73cSMariusz Zaborski if ((fields & HAS_OPERATION) != 0) 320c501d73cSMariusz Zaborski return (EINVAL); 321c501d73cSMariusz Zaborski fields |= HAS_OPERATION; 3221608c46eSMark Johnston } else if (limit) 323c501d73cSMariusz Zaborski return (EINVAL); 324c501d73cSMariusz Zaborski } 325c501d73cSMariusz Zaborski 3261608c46eSMark Johnston if ((fields & HAS_OPERATION) == 0 || (fields & HAS_ID) == 0) 327c501d73cSMariusz Zaborski return (EINVAL); 328c501d73cSMariusz Zaborski 329c501d73cSMariusz Zaborski #undef HAS_OPERATION 3301608c46eSMark Johnston #undef HAS_ID 3311608c46eSMark Johnston #undef HAS_MIB 332c501d73cSMariusz Zaborski #undef HAS_NAME 333c501d73cSMariusz Zaborski 334c501d73cSMariusz Zaborski return (0); 335c501d73cSMariusz Zaborski } 336c501d73cSMariusz Zaborski 337c501d73cSMariusz Zaborski static bool 3381608c46eSMark Johnston sysctl_allowed(const nvlist_t *limits, const nvlist_t *req) 339c501d73cSMariusz Zaborski { 3401608c46eSMark Johnston const nvlist_t *limit; 3411608c46eSMark Johnston uint64_t op, reqop; 3421608c46eSMark Johnston const char *lname, *name, *reqname; 343c501d73cSMariusz Zaborski void *cookie; 3441608c46eSMark Johnston size_t lsize, reqsize; 3451608c46eSMark Johnston const int *lmib, *reqmib; 346c501d73cSMariusz Zaborski int type; 347c501d73cSMariusz Zaborski 348c501d73cSMariusz Zaborski if (limits == NULL) 349c501d73cSMariusz Zaborski return (true); 350c501d73cSMariusz Zaborski 3511608c46eSMark Johnston reqmib = dnvlist_get_binary(req, "mib", &reqsize, NULL, 0); 3521608c46eSMark Johnston reqname = dnvlist_get_string(req, "name", NULL); 3531608c46eSMark Johnston reqop = nvlist_get_number(req, "operation"); 3541608c46eSMark Johnston 355c501d73cSMariusz Zaborski cookie = NULL; 356c501d73cSMariusz Zaborski while ((name = nvlist_next(limits, &type, &cookie)) != NULL) { 3571608c46eSMark Johnston assert(type == NV_TYPE_NVLIST); 358c501d73cSMariusz Zaborski 3591608c46eSMark Johnston limit = cnvlist_get_nvlist(cookie); 3601608c46eSMark Johnston op = nvlist_get_number(limit, "operation"); 3611608c46eSMark Johnston if ((reqop & op) != reqop) 362c501d73cSMariusz Zaborski continue; 363c501d73cSMariusz Zaborski 3641608c46eSMark Johnston if (reqname != NULL) { 3651608c46eSMark Johnston lname = dnvlist_get_string(limit, "name", NULL); 3661608c46eSMark Johnston if (lname == NULL) 3671608c46eSMark Johnston continue; 3681608c46eSMark Johnston if ((op & CAP_SYSCTL_RECURSIVE) == 0) { 3691608c46eSMark Johnston if (strcmp(lname, reqname) != 0) 370c501d73cSMariusz Zaborski continue; 371c501d73cSMariusz Zaborski } else { 372c501d73cSMariusz Zaborski size_t namelen; 373c501d73cSMariusz Zaborski 3741608c46eSMark Johnston namelen = strlen(lname); 3751608c46eSMark Johnston if (strncmp(lname, reqname, namelen) != 0) 376c501d73cSMariusz Zaborski continue; 3771608c46eSMark Johnston if (reqname[namelen] != '.' && 3781608c46eSMark Johnston reqname[namelen] != '\0') 3791608c46eSMark Johnston continue; 3801608c46eSMark Johnston } 3811608c46eSMark Johnston } else { 3821608c46eSMark Johnston lmib = dnvlist_get_binary(limit, "mib", &lsize, NULL, 0); 3831608c46eSMark Johnston if (lmib == NULL) 3841608c46eSMark Johnston continue; 3851608c46eSMark Johnston if (lsize > reqsize || ((op & CAP_SYSCTL_RECURSIVE) == 0 && 3861608c46eSMark Johnston lsize < reqsize)) 3871608c46eSMark Johnston continue; 3881608c46eSMark Johnston if (memcmp(lmib, reqmib, lsize) != 0) 389c501d73cSMariusz Zaborski continue; 390c501d73cSMariusz Zaborski } 391c501d73cSMariusz Zaborski 392c501d73cSMariusz Zaborski return (true); 393c501d73cSMariusz Zaborski } 394c501d73cSMariusz Zaborski 395c501d73cSMariusz Zaborski return (false); 396c501d73cSMariusz Zaborski } 397c501d73cSMariusz Zaborski 398c501d73cSMariusz Zaborski static int 399c501d73cSMariusz Zaborski sysctl_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits) 400c501d73cSMariusz Zaborski { 4011608c46eSMark Johnston const nvlist_t *nvl; 402c501d73cSMariusz Zaborski const char *name; 403c501d73cSMariusz Zaborski void *cookie; 4041608c46eSMark Johnston int error, type; 405c501d73cSMariusz Zaborski 406c501d73cSMariusz Zaborski cookie = NULL; 407c501d73cSMariusz Zaborski while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) { 4081608c46eSMark Johnston if (strcmp(name, "limit") != 0 || type != NV_TYPE_NVLIST) 409c501d73cSMariusz Zaborski return (EINVAL); 4101608c46eSMark Johnston nvl = cnvlist_get_nvlist(cookie); 4111608c46eSMark Johnston error = sysctl_valid(nvl, true); 4121608c46eSMark Johnston if (error != 0) 4131608c46eSMark Johnston return (error); 4141608c46eSMark Johnston if (!sysctl_allowed(oldlimits, nvl)) 415c501d73cSMariusz Zaborski return (ENOTCAPABLE); 416c501d73cSMariusz Zaborski } 417c501d73cSMariusz Zaborski 418c501d73cSMariusz Zaborski return (0); 419c501d73cSMariusz Zaborski } 420c501d73cSMariusz Zaborski 421c501d73cSMariusz Zaborski static int 4221608c46eSMark Johnston nametomib(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout) 4231608c46eSMark Johnston { 4241608c46eSMark Johnston const char *name; 4251608c46eSMark Johnston size_t size; 4261608c46eSMark Johnston int error, *mibp; 4271608c46eSMark Johnston 4281608c46eSMark Johnston if (!sysctl_allowed(limits, nvlin)) 4291608c46eSMark Johnston return (ENOTCAPABLE); 4301608c46eSMark Johnston 4311608c46eSMark Johnston name = nvlist_get_string(nvlin, "name"); 4321608c46eSMark Johnston size = (size_t)nvlist_get_number(nvlin, "size"); 4331608c46eSMark Johnston 4341608c46eSMark Johnston mibp = malloc(size * sizeof(*mibp)); 4351608c46eSMark Johnston if (mibp == NULL) 4361608c46eSMark Johnston return (ENOMEM); 4371608c46eSMark Johnston 4381608c46eSMark Johnston error = sysctlnametomib(name, mibp, &size); 4391608c46eSMark Johnston if (error != 0) { 4401608c46eSMark Johnston error = errno; 4411608c46eSMark Johnston free(mibp); 4421608c46eSMark Johnston return (error); 4431608c46eSMark Johnston } 4441608c46eSMark Johnston 4451608c46eSMark Johnston nvlist_add_binary(nvlout, "mib", mibp, size * sizeof(*mibp)); 4461608c46eSMark Johnston 4471608c46eSMark Johnston return (0); 4481608c46eSMark Johnston } 4491608c46eSMark Johnston 4501608c46eSMark Johnston static int 451c501d73cSMariusz Zaborski sysctl_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin, 452c501d73cSMariusz Zaborski nvlist_t *nvlout) 453c501d73cSMariusz Zaborski { 454c501d73cSMariusz Zaborski const char *name; 455c501d73cSMariusz Zaborski const void *newp; 4561608c46eSMark Johnston const int *mibp; 457c501d73cSMariusz Zaborski void *oldp; 458c501d73cSMariusz Zaborski uint64_t operation; 4591608c46eSMark Johnston size_t oldlen, newlen, size; 460c501d73cSMariusz Zaborski size_t *oldlenp; 461c501d73cSMariusz Zaborski int error; 462c501d73cSMariusz Zaborski 4631608c46eSMark Johnston if (strcmp(cmd, "sysctlnametomib") == 0) 4641608c46eSMark Johnston return (nametomib(limits, nvlin, nvlout)); 4651608c46eSMark Johnston 4661608c46eSMark Johnston if (strcmp(cmd, "sysctlbyname") != 0 && strcmp(cmd, "sysctl") != 0) 467c501d73cSMariusz Zaborski return (EINVAL); 4681608c46eSMark Johnston error = sysctl_valid(nvlin, false); 469c501d73cSMariusz Zaborski if (error != 0) 470c501d73cSMariusz Zaborski return (error); 4711608c46eSMark Johnston if (!sysctl_allowed(limits, nvlin)) 472c501d73cSMariusz Zaborski return (ENOTCAPABLE); 473c501d73cSMariusz Zaborski 4741608c46eSMark Johnston operation = nvlist_get_number(nvlin, "operation"); 475c501d73cSMariusz Zaborski if ((operation & CAP_SYSCTL_WRITE) != 0) { 476c501d73cSMariusz Zaborski if (!nvlist_exists_binary(nvlin, "newp")) 477c501d73cSMariusz Zaborski return (EINVAL); 478c501d73cSMariusz Zaborski newp = nvlist_get_binary(nvlin, "newp", &newlen); 479c501d73cSMariusz Zaborski assert(newp != NULL && newlen > 0); 480c501d73cSMariusz Zaborski } else { 481c501d73cSMariusz Zaborski newp = NULL; 482c501d73cSMariusz Zaborski newlen = 0; 483c501d73cSMariusz Zaborski } 484c501d73cSMariusz Zaborski 485c501d73cSMariusz Zaborski if ((operation & CAP_SYSCTL_READ) != 0) { 486c501d73cSMariusz Zaborski if (nvlist_exists_null(nvlin, "justsize")) { 487c501d73cSMariusz Zaborski oldp = NULL; 488c501d73cSMariusz Zaborski oldlen = 0; 489c501d73cSMariusz Zaborski oldlenp = &oldlen; 490c501d73cSMariusz Zaborski } else { 491c501d73cSMariusz Zaborski if (!nvlist_exists_number(nvlin, "oldlen")) 492c501d73cSMariusz Zaborski return (EINVAL); 493c501d73cSMariusz Zaborski oldlen = (size_t)nvlist_get_number(nvlin, "oldlen"); 494c501d73cSMariusz Zaborski if (oldlen == 0) 495c501d73cSMariusz Zaborski return (EINVAL); 496c501d73cSMariusz Zaborski oldp = calloc(1, oldlen); 497c501d73cSMariusz Zaborski if (oldp == NULL) 498c501d73cSMariusz Zaborski return (ENOMEM); 499c501d73cSMariusz Zaborski oldlenp = &oldlen; 500c501d73cSMariusz Zaborski } 501c501d73cSMariusz Zaborski } else { 502c501d73cSMariusz Zaborski oldp = NULL; 503c501d73cSMariusz Zaborski oldlen = 0; 504c501d73cSMariusz Zaborski oldlenp = NULL; 505c501d73cSMariusz Zaborski } 506c501d73cSMariusz Zaborski 5071608c46eSMark Johnston if (strcmp(cmd, "sysctlbyname") == 0) { 5081608c46eSMark Johnston name = nvlist_get_string(nvlin, "name"); 5091608c46eSMark Johnston error = sysctlbyname(name, oldp, oldlenp, newp, newlen); 5101608c46eSMark Johnston } else { 5111608c46eSMark Johnston mibp = nvlist_get_binary(nvlin, "mib", &size); 5121608c46eSMark Johnston error = sysctl(mibp, size / sizeof(*mibp), oldp, oldlenp, newp, 5131608c46eSMark Johnston newlen); 5141608c46eSMark Johnston } 5151608c46eSMark Johnston if (error != 0) { 516c501d73cSMariusz Zaborski error = errno; 517c501d73cSMariusz Zaborski free(oldp); 518c501d73cSMariusz Zaborski return (error); 519c501d73cSMariusz Zaborski } 520c501d73cSMariusz Zaborski 521c501d73cSMariusz Zaborski if ((operation & CAP_SYSCTL_READ) != 0) { 522c501d73cSMariusz Zaborski if (nvlist_exists_null(nvlin, "justsize")) 523c501d73cSMariusz Zaborski nvlist_add_number(nvlout, "oldlen", (uint64_t)oldlen); 524c501d73cSMariusz Zaborski else 525c501d73cSMariusz Zaborski nvlist_move_binary(nvlout, "oldp", oldp, oldlen); 526c501d73cSMariusz Zaborski } 527c501d73cSMariusz Zaborski 528c501d73cSMariusz Zaborski return (0); 529c501d73cSMariusz Zaborski } 530c501d73cSMariusz Zaborski 531920be817SMariusz Zaborski CREATE_SERVICE("system.sysctl", sysctl_limit, sysctl_command, 0); 532