Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libcasper/services/cap_sysctl/cap_sysctl.c
48261 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2013, 2018 The FreeBSD Foundation
5
*
6
* This software was developed by Pawel Jakub Dawidek under sponsorship from
7
* the FreeBSD Foundation.
8
*
9
* Portions of this software were developed by Mark Johnston
10
* under sponsorship from the FreeBSD Foundation.
11
*
12
* Redistribution and use in source and binary forms, with or without
13
* modification, are permitted provided that the following conditions
14
* are met:
15
* 1. Redistributions of source code must retain the above copyright
16
* notice, this list of conditions and the following disclaimer.
17
* 2. Redistributions in binary form must reproduce the above copyright
18
* notice, this list of conditions and the following disclaimer in the
19
* documentation and/or other materials provided with the distribution.
20
*
21
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
22
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
25
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
* SUCH DAMAGE.
32
*/
33
34
#include <sys/param.h>
35
#include <sys/cnv.h>
36
#include <sys/dnv.h>
37
#include <sys/nv.h>
38
#include <sys/sysctl.h>
39
40
#include <assert.h>
41
#include <errno.h>
42
#include <stdlib.h>
43
#include <string.h>
44
45
#include <libcasper.h>
46
#include <libcasper_service.h>
47
48
#include "cap_sysctl.h"
49
50
/*
51
* Limit interface.
52
*/
53
54
struct cap_sysctl_limit {
55
cap_channel_t *chan;
56
nvlist_t *nv;
57
};
58
59
cap_sysctl_limit_t *
60
cap_sysctl_limit_init(cap_channel_t *chan)
61
{
62
cap_sysctl_limit_t *limit;
63
int error;
64
65
limit = malloc(sizeof(*limit));
66
if (limit != NULL) {
67
limit->chan = chan;
68
limit->nv = nvlist_create(NV_FLAG_NO_UNIQUE);
69
if (limit->nv == NULL) {
70
error = errno;
71
free(limit);
72
limit = NULL;
73
errno = error;
74
}
75
}
76
return (limit);
77
}
78
79
cap_sysctl_limit_t *
80
cap_sysctl_limit_name(cap_sysctl_limit_t *limit, const char *name, int flags)
81
{
82
nvlist_t *lnv;
83
size_t mibsz;
84
int error, mib[CTL_MAXNAME];
85
86
lnv = nvlist_create(0);
87
if (lnv == NULL) {
88
error = errno;
89
if (limit->nv != NULL)
90
nvlist_destroy(limit->nv);
91
free(limit);
92
errno = error;
93
return (NULL);
94
}
95
nvlist_add_string(lnv, "name", name);
96
nvlist_add_number(lnv, "operation", flags);
97
98
mibsz = nitems(mib);
99
error = cap_sysctlnametomib(limit->chan, name, mib, &mibsz);
100
if (error == 0)
101
nvlist_add_binary(lnv, "mib", mib, mibsz * sizeof(int));
102
103
nvlist_move_nvlist(limit->nv, "limit", lnv);
104
return (limit);
105
}
106
107
cap_sysctl_limit_t *
108
cap_sysctl_limit_mib(cap_sysctl_limit_t *limit, const int *mibp, u_int miblen,
109
int flags)
110
{
111
nvlist_t *lnv;
112
int error;
113
114
lnv = nvlist_create(0);
115
if (lnv == NULL) {
116
error = errno;
117
if (limit->nv != NULL)
118
nvlist_destroy(limit->nv);
119
free(limit);
120
errno = error;
121
return (NULL);
122
}
123
nvlist_add_binary(lnv, "mib", mibp, miblen * sizeof(int));
124
nvlist_add_number(lnv, "operation", flags);
125
nvlist_add_nvlist(limit->nv, "limit", lnv);
126
return (limit);
127
}
128
129
int
130
cap_sysctl_limit(cap_sysctl_limit_t *limit)
131
{
132
cap_channel_t *chan;
133
nvlist_t *lnv;
134
135
chan = limit->chan;
136
lnv = limit->nv;
137
free(limit);
138
139
/* cap_limit_set(3) will always free the nvlist. */
140
return (cap_limit_set(chan, lnv));
141
}
142
143
/*
144
* Service interface.
145
*/
146
147
static int
148
do_sysctl(cap_channel_t *chan, nvlist_t *nvl, void *oldp, size_t *oldlenp,
149
const void *newp, size_t newlen)
150
{
151
const uint8_t *retoldp;
152
size_t oldlen;
153
int error;
154
uint8_t operation;
155
156
operation = 0;
157
if (oldlenp != NULL)
158
operation |= CAP_SYSCTL_READ;
159
if (newp != NULL)
160
operation |= CAP_SYSCTL_WRITE;
161
nvlist_add_number(nvl, "operation", (uint64_t)operation);
162
if (oldp == NULL && oldlenp != NULL)
163
nvlist_add_null(nvl, "justsize");
164
else if (oldlenp != NULL)
165
nvlist_add_number(nvl, "oldlen", (uint64_t)*oldlenp);
166
if (newp != NULL)
167
nvlist_add_binary(nvl, "newp", newp, newlen);
168
169
nvl = cap_xfer_nvlist(chan, nvl);
170
if (nvl == NULL)
171
return (-1);
172
error = (int)dnvlist_get_number(nvl, "error", 0);
173
if (error != 0) {
174
nvlist_destroy(nvl);
175
errno = error;
176
return (-1);
177
}
178
179
if (oldp == NULL && oldlenp != NULL) {
180
*oldlenp = (size_t)nvlist_get_number(nvl, "oldlen");
181
} else if (oldp != NULL) {
182
retoldp = nvlist_get_binary(nvl, "oldp", &oldlen);
183
memcpy(oldp, retoldp, oldlen);
184
if (oldlenp != NULL)
185
*oldlenp = oldlen;
186
}
187
188
nvlist_destroy(nvl);
189
190
return (0);
191
}
192
193
int
194
cap_sysctl(cap_channel_t *chan, const int *name, u_int namelen, void *oldp,
195
size_t *oldlenp, const void *newp, size_t newlen)
196
{
197
nvlist_t *req;
198
199
req = nvlist_create(0);
200
nvlist_add_string(req, "cmd", "sysctl");
201
nvlist_add_binary(req, "mib", name, (size_t)namelen * sizeof(int));
202
return (do_sysctl(chan, req, oldp, oldlenp, newp, newlen));
203
}
204
205
int
206
cap_sysctlbyname(cap_channel_t *chan, const char *name, void *oldp,
207
size_t *oldlenp, const void *newp, size_t newlen)
208
{
209
nvlist_t *req;
210
211
req = nvlist_create(0);
212
nvlist_add_string(req, "cmd", "sysctlbyname");
213
nvlist_add_string(req, "name", name);
214
return (do_sysctl(chan, req, oldp, oldlenp, newp, newlen));
215
}
216
217
int
218
cap_sysctlnametomib(cap_channel_t *chan, const char *name, int *mibp,
219
size_t *sizep)
220
{
221
nvlist_t *req;
222
const void *mib;
223
size_t mibsz;
224
int error;
225
226
req = nvlist_create(0);
227
nvlist_add_string(req, "cmd", "sysctlnametomib");
228
nvlist_add_string(req, "name", name);
229
nvlist_add_number(req, "operation", 0);
230
nvlist_add_number(req, "size", (uint64_t)*sizep);
231
232
req = cap_xfer_nvlist(chan, req);
233
if (req == NULL)
234
return (-1);
235
error = (int)dnvlist_get_number(req, "error", 0);
236
if (error != 0) {
237
nvlist_destroy(req);
238
errno = error;
239
return (-1);
240
}
241
242
mib = nvlist_get_binary(req, "mib", &mibsz);
243
*sizep = mibsz / sizeof(int);
244
245
memcpy(mibp, mib, mibsz);
246
247
nvlist_destroy(req);
248
249
return (0);
250
}
251
252
/*
253
* Service implementation.
254
*/
255
256
/*
257
* Validate a sysctl description. This must consist of an nvlist with either a
258
* binary "mib" field or a string "name", and an operation.
259
*/
260
static int
261
sysctl_valid(const nvlist_t *nvl, bool limit)
262
{
263
const char *name;
264
void *cookie;
265
int type;
266
size_t size;
267
unsigned int field, fields;
268
269
/* NULL nvl is of course invalid. */
270
if (nvl == NULL)
271
return (EINVAL);
272
if (nvlist_error(nvl) != 0)
273
return (nvlist_error(nvl));
274
275
#define HAS_NAME 0x01
276
#define HAS_MIB 0x02
277
#define HAS_ID (HAS_NAME | HAS_MIB)
278
#define HAS_OPERATION 0x04
279
280
fields = 0;
281
cookie = NULL;
282
while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
283
if ((strcmp(name, "name") == 0 && type == NV_TYPE_STRING) ||
284
(strcmp(name, "mib") == 0 && type == NV_TYPE_BINARY)) {
285
if (strcmp(name, "mib") == 0) {
286
/* A MIB must be an array of integers. */
287
(void)cnvlist_get_binary(cookie, &size);
288
if (size % sizeof(int) != 0)
289
return (EINVAL);
290
field = HAS_MIB;
291
} else
292
field = HAS_NAME;
293
294
/*
295
* A limit may contain both a name and a MIB identifier.
296
*/
297
if ((fields & field) != 0 ||
298
(!limit && (fields & HAS_ID) != 0))
299
return (EINVAL);
300
fields |= field;
301
} else if (strcmp(name, "operation") == 0) {
302
uint64_t mask, operation;
303
304
if (type != NV_TYPE_NUMBER)
305
return (EINVAL);
306
307
operation = cnvlist_get_number(cookie);
308
309
/*
310
* Requests can only include the RDWR flags; limits may
311
* also include the RECURSIVE flag.
312
*/
313
mask = limit ? (CAP_SYSCTL_RDWR |
314
CAP_SYSCTL_RECURSIVE) : CAP_SYSCTL_RDWR;
315
if ((operation & ~mask) != 0 ||
316
(operation & CAP_SYSCTL_RDWR) == 0)
317
return (EINVAL);
318
/* Only one 'operation' can be present. */
319
if ((fields & HAS_OPERATION) != 0)
320
return (EINVAL);
321
fields |= HAS_OPERATION;
322
} else if (limit)
323
return (EINVAL);
324
}
325
326
if ((fields & HAS_OPERATION) == 0 || (fields & HAS_ID) == 0)
327
return (EINVAL);
328
329
#undef HAS_OPERATION
330
#undef HAS_ID
331
#undef HAS_MIB
332
#undef HAS_NAME
333
334
return (0);
335
}
336
337
static bool
338
sysctl_allowed(const nvlist_t *limits, const nvlist_t *req)
339
{
340
const nvlist_t *limit;
341
uint64_t op, reqop;
342
const char *lname, *name, *reqname;
343
void *cookie;
344
size_t lsize, reqsize;
345
const int *lmib, *reqmib;
346
int type;
347
348
if (limits == NULL)
349
return (true);
350
351
reqmib = dnvlist_get_binary(req, "mib", &reqsize, NULL, 0);
352
reqname = dnvlist_get_string(req, "name", NULL);
353
reqop = nvlist_get_number(req, "operation");
354
355
cookie = NULL;
356
while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
357
assert(type == NV_TYPE_NVLIST);
358
359
limit = cnvlist_get_nvlist(cookie);
360
op = nvlist_get_number(limit, "operation");
361
if ((reqop & op) != reqop)
362
continue;
363
364
if (reqname != NULL) {
365
lname = dnvlist_get_string(limit, "name", NULL);
366
if (lname == NULL)
367
continue;
368
if ((op & CAP_SYSCTL_RECURSIVE) == 0) {
369
if (strcmp(lname, reqname) != 0)
370
continue;
371
} else {
372
size_t namelen;
373
374
namelen = strlen(lname);
375
if (strncmp(lname, reqname, namelen) != 0)
376
continue;
377
if (reqname[namelen] != '.' &&
378
reqname[namelen] != '\0')
379
continue;
380
}
381
} else {
382
lmib = dnvlist_get_binary(limit, "mib", &lsize, NULL, 0);
383
if (lmib == NULL)
384
continue;
385
if (lsize > reqsize || ((op & CAP_SYSCTL_RECURSIVE) == 0 &&
386
lsize < reqsize))
387
continue;
388
if (memcmp(lmib, reqmib, lsize) != 0)
389
continue;
390
}
391
392
return (true);
393
}
394
395
return (false);
396
}
397
398
static int
399
sysctl_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
400
{
401
const nvlist_t *nvl;
402
const char *name;
403
void *cookie;
404
int error, type;
405
406
cookie = NULL;
407
while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
408
if (strcmp(name, "limit") != 0 || type != NV_TYPE_NVLIST)
409
return (EINVAL);
410
nvl = cnvlist_get_nvlist(cookie);
411
error = sysctl_valid(nvl, true);
412
if (error != 0)
413
return (error);
414
if (!sysctl_allowed(oldlimits, nvl))
415
return (ENOTCAPABLE);
416
}
417
418
return (0);
419
}
420
421
static int
422
nametomib(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
423
{
424
const char *name;
425
size_t size;
426
int error, *mibp;
427
428
if (!sysctl_allowed(limits, nvlin))
429
return (ENOTCAPABLE);
430
431
name = nvlist_get_string(nvlin, "name");
432
size = (size_t)nvlist_get_number(nvlin, "size");
433
434
mibp = malloc(size * sizeof(*mibp));
435
if (mibp == NULL)
436
return (ENOMEM);
437
438
error = sysctlnametomib(name, mibp, &size);
439
if (error != 0) {
440
error = errno;
441
free(mibp);
442
return (error);
443
}
444
445
nvlist_add_binary(nvlout, "mib", mibp, size * sizeof(*mibp));
446
447
return (0);
448
}
449
450
static int
451
sysctl_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
452
nvlist_t *nvlout)
453
{
454
const char *name;
455
const void *newp;
456
const int *mibp;
457
void *oldp;
458
uint64_t operation;
459
size_t oldlen, newlen, size;
460
size_t *oldlenp;
461
int error;
462
463
if (strcmp(cmd, "sysctlnametomib") == 0)
464
return (nametomib(limits, nvlin, nvlout));
465
466
if (strcmp(cmd, "sysctlbyname") != 0 && strcmp(cmd, "sysctl") != 0)
467
return (EINVAL);
468
error = sysctl_valid(nvlin, false);
469
if (error != 0)
470
return (error);
471
if (!sysctl_allowed(limits, nvlin))
472
return (ENOTCAPABLE);
473
474
operation = nvlist_get_number(nvlin, "operation");
475
if ((operation & CAP_SYSCTL_WRITE) != 0) {
476
if (!nvlist_exists_binary(nvlin, "newp"))
477
return (EINVAL);
478
newp = nvlist_get_binary(nvlin, "newp", &newlen);
479
assert(newp != NULL && newlen > 0);
480
} else {
481
newp = NULL;
482
newlen = 0;
483
}
484
485
if ((operation & CAP_SYSCTL_READ) != 0) {
486
if (nvlist_exists_null(nvlin, "justsize")) {
487
oldp = NULL;
488
oldlen = 0;
489
oldlenp = &oldlen;
490
} else {
491
if (!nvlist_exists_number(nvlin, "oldlen"))
492
return (EINVAL);
493
oldlen = (size_t)nvlist_get_number(nvlin, "oldlen");
494
if (oldlen == 0)
495
return (EINVAL);
496
oldp = calloc(1, oldlen);
497
if (oldp == NULL)
498
return (ENOMEM);
499
oldlenp = &oldlen;
500
}
501
} else {
502
oldp = NULL;
503
oldlen = 0;
504
oldlenp = NULL;
505
}
506
507
if (strcmp(cmd, "sysctlbyname") == 0) {
508
name = nvlist_get_string(nvlin, "name");
509
error = sysctlbyname(name, oldp, oldlenp, newp, newlen);
510
} else {
511
mibp = nvlist_get_binary(nvlin, "mib", &size);
512
error = sysctl(mibp, size / sizeof(*mibp), oldp, oldlenp, newp,
513
newlen);
514
}
515
if (error != 0) {
516
error = errno;
517
free(oldp);
518
return (error);
519
}
520
521
if ((operation & CAP_SYSCTL_READ) != 0) {
522
if (nvlist_exists_null(nvlin, "justsize"))
523
nvlist_add_number(nvlout, "oldlen", (uint64_t)oldlen);
524
else
525
nvlist_move_binary(nvlout, "oldp", oldp, oldlen);
526
}
527
528
return (0);
529
}
530
531
CREATE_SERVICE("system.sysctl", sysctl_limit, sysctl_command, 0);
532
533