xref: /freebsd/lib/libcasper/services/cap_sysctl/cap_sysctl.c (revision 5c2bc3db201a4fe8d7911cf816bea104d5dc2138)
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