]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libcasper/services/cap_sysctl/cap_sysctl.c
Convert casperd(8) daemon to the libcasper.
[FreeBSD/FreeBSD.git] / lib / libcasper / services / cap_sysctl / cap_sysctl.c
1 /*-
2  * Copyright (c) 2013 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/types.h>
34 #include <sys/sysctl.h>
35 #include <sys/nv.h>
36
37 #include <assert.h>
38 #include <errno.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include <libcasper.h>
43 #include <libcasper_service.h>
44
45 #include "cap_sysctl.h"
46
47 int
48 cap_sysctlbyname(cap_channel_t *chan, const char *name, void *oldp,
49     size_t *oldlenp, const void *newp, size_t newlen)
50 {
51         nvlist_t *nvl;
52         const uint8_t *retoldp;
53         uint8_t operation;
54         size_t oldlen;
55
56         operation = 0;
57         if (oldp != NULL)
58                 operation |= CAP_SYSCTL_READ;
59         if (newp != NULL)
60                 operation |= CAP_SYSCTL_WRITE;
61
62         nvl = nvlist_create(0);
63         nvlist_add_string(nvl, "cmd", "sysctl");
64         nvlist_add_string(nvl, "name", name);
65         nvlist_add_number(nvl, "operation", (uint64_t)operation);
66         if (oldp == NULL && oldlenp != NULL)
67                 nvlist_add_null(nvl, "justsize");
68         else if (oldlenp != NULL)
69                 nvlist_add_number(nvl, "oldlen", (uint64_t)*oldlenp);
70         if (newp != NULL)
71                 nvlist_add_binary(nvl, "newp", newp, newlen);
72         nvl = cap_xfer_nvlist(chan, nvl, 0);
73         if (nvl == NULL)
74                 return (-1);
75         if (nvlist_get_number(nvl, "error") != 0) {
76                 errno = (int)nvlist_get_number(nvl, "error");
77                 nvlist_destroy(nvl);
78                 return (-1);
79         }
80
81         if (oldp == NULL && oldlenp != NULL) {
82                 *oldlenp = (size_t)nvlist_get_number(nvl, "oldlen");
83         } else if (oldp != NULL) {
84                 retoldp = nvlist_get_binary(nvl, "oldp", &oldlen);
85                 memcpy(oldp, retoldp, oldlen);
86                 if (oldlenp != NULL)
87                         *oldlenp = oldlen;
88         }
89         nvlist_destroy(nvl);
90
91         return (0);
92 }
93
94 /*
95  * Service functions.
96  */
97 static int
98 sysctl_check_one(const nvlist_t *nvl, bool islimit)
99 {
100         const char *name;
101         void *cookie;
102         int type;
103         unsigned int fields;
104
105         /* NULL nvl is of course invalid. */
106         if (nvl == NULL)
107                 return (EINVAL);
108         if (nvlist_error(nvl) != 0)
109                 return (nvlist_error(nvl));
110
111 #define HAS_NAME        0x01
112 #define HAS_OPERATION   0x02
113
114         fields = 0;
115         cookie = NULL;
116         while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
117                 /* We accept only one 'name' and one 'operation' in nvl. */
118                 if (strcmp(name, "name") == 0) {
119                         if (type != NV_TYPE_STRING)
120                                 return (EINVAL);
121                         /* Only one 'name' can be present. */
122                         if ((fields & HAS_NAME) != 0)
123                                 return (EINVAL);
124                         fields |= HAS_NAME;
125                 } else if (strcmp(name, "operation") == 0) {
126                         uint64_t operation;
127
128                         if (type != NV_TYPE_NUMBER)
129                                 return (EINVAL);
130                         /*
131                          * We accept only CAP_SYSCTL_READ and
132                          * CAP_SYSCTL_WRITE flags.
133                          */
134                         operation = nvlist_get_number(nvl, name);
135                         if ((operation & ~(CAP_SYSCTL_RDWR)) != 0)
136                                 return (EINVAL);
137                         /* ...but there has to be at least one of them. */
138                         if ((operation & (CAP_SYSCTL_RDWR)) == 0)
139                                 return (EINVAL);
140                         /* Only one 'operation' can be present. */
141                         if ((fields & HAS_OPERATION) != 0)
142                                 return (EINVAL);
143                         fields |= HAS_OPERATION;
144                 } else if (islimit) {
145                         /* If this is limit, there can be no other fields. */
146                         return (EINVAL);
147                 }
148         }
149
150         /* Both fields has to be there. */
151         if (fields != (HAS_NAME | HAS_OPERATION))
152                 return (EINVAL);
153
154 #undef  HAS_OPERATION
155 #undef  HAS_NAME
156
157         return (0);
158 }
159
160 static bool
161 sysctl_allowed(const nvlist_t *limits, const char *chname, uint64_t choperation)
162 {
163         uint64_t operation;
164         const char *name;
165         void *cookie;
166         int type;
167
168         if (limits == NULL)
169                 return (true);
170
171         cookie = NULL;
172         while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
173                 assert(type == NV_TYPE_NUMBER);
174
175                 operation = nvlist_get_number(limits, name);
176                 if ((operation & choperation) != choperation)
177                         continue;
178
179                 if ((operation & CAP_SYSCTL_RECURSIVE) == 0) {
180                         if (strcmp(name, chname) != 0)
181                                 continue;
182                 } else {
183                         size_t namelen;
184
185                         namelen = strlen(name);
186                         if (strncmp(name, chname, namelen) != 0)
187                                 continue;
188                         if (chname[namelen] != '.' && chname[namelen] != '\0')
189                                 continue;
190                 }
191
192                 return (true);
193         }
194
195         return (false);
196 }
197
198 static int
199 sysctl_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
200 {
201         const char *name;
202         void *cookie;
203         uint64_t operation;
204         int type;
205
206         cookie = NULL;
207         while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
208                 if (type != NV_TYPE_NUMBER)
209                         return (EINVAL);
210                 operation = nvlist_get_number(newlimits, name);
211                 if ((operation & ~(CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE)) != 0)
212                         return (EINVAL);
213                 if ((operation & (CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE)) == 0)
214                         return (EINVAL);
215                 if (!sysctl_allowed(oldlimits, name, operation))
216                         return (ENOTCAPABLE);
217         }
218
219         return (0);
220 }
221
222 static int
223 sysctl_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
224     nvlist_t *nvlout)
225 {
226         const char *name;
227         const void *newp;
228         void *oldp;
229         uint64_t operation;
230         size_t oldlen, newlen;
231         size_t *oldlenp;
232         int error;
233
234         if (strcmp(cmd, "sysctl") != 0)
235                 return (EINVAL);
236         error = sysctl_check_one(nvlin, false);
237         if (error != 0)
238                 return (error);
239
240         name = nvlist_get_string(nvlin, "name");
241         operation = nvlist_get_number(nvlin, "operation");
242         if (!sysctl_allowed(limits, name, operation))
243                 return (ENOTCAPABLE);
244
245         if ((operation & CAP_SYSCTL_WRITE) != 0) {
246                 if (!nvlist_exists_binary(nvlin, "newp"))
247                         return (EINVAL);
248                 newp = nvlist_get_binary(nvlin, "newp", &newlen);
249                 assert(newp != NULL && newlen > 0);
250         } else {
251                 newp = NULL;
252                 newlen = 0;
253         }
254
255         if ((operation & CAP_SYSCTL_READ) != 0) {
256                 if (nvlist_exists_null(nvlin, "justsize")) {
257                         oldp = NULL;
258                         oldlen = 0;
259                         oldlenp = &oldlen;
260                 } else {
261                         if (!nvlist_exists_number(nvlin, "oldlen"))
262                                 return (EINVAL);
263                         oldlen = (size_t)nvlist_get_number(nvlin, "oldlen");
264                         if (oldlen == 0)
265                                 return (EINVAL);
266                         oldp = calloc(1, oldlen);
267                         if (oldp == NULL)
268                                 return (ENOMEM);
269                         oldlenp = &oldlen;
270                 }
271         } else {
272                 oldp = NULL;
273                 oldlen = 0;
274                 oldlenp = NULL;
275         }
276
277         if (sysctlbyname(name, oldp, oldlenp, newp, newlen) == -1) {
278                 error = errno;
279                 free(oldp);
280                 return (error);
281         }
282
283         if ((operation & CAP_SYSCTL_READ) != 0) {
284                 if (nvlist_exists_null(nvlin, "justsize"))
285                         nvlist_add_number(nvlout, "oldlen", (uint64_t)oldlen);
286                 else
287                         nvlist_move_binary(nvlout, "oldp", oldp, oldlen);
288         }
289
290         return (0);
291 }
292
293 CREATE_SERVICE("system.sysctl", sysctl_limit, sysctl_command);