Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libcasper/services/cap_fileargs/cap_fileargs.c
48254 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2018-2021 Mariusz Zaborski <[email protected]>
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/types.h>
30
#include <sys/capsicum.h>
31
#include <sys/sysctl.h>
32
#include <sys/cnv.h>
33
#include <sys/dnv.h>
34
#include <sys/nv.h>
35
#include <sys/stat.h>
36
37
#include <assert.h>
38
#include <errno.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <unistd.h>
42
43
#include <libcasper.h>
44
#include <libcasper_service.h>
45
46
#include "cap_fileargs.h"
47
48
#define CACHE_SIZE 128
49
50
#define FILEARGS_MAGIC 0xFA00FA00
51
52
struct fileargs {
53
uint32_t fa_magic;
54
nvlist_t *fa_cache;
55
cap_channel_t *fa_chann;
56
int fa_fdflags;
57
};
58
59
static int
60
fileargs_get_lstat_cache(fileargs_t *fa, const char *name, struct stat *sb)
61
{
62
const nvlist_t *nvl;
63
size_t size;
64
const void *buf;
65
66
assert(fa != NULL);
67
assert(fa->fa_magic == FILEARGS_MAGIC);
68
assert(name != NULL);
69
70
if (fa->fa_cache == NULL)
71
return (-1);
72
73
nvl = dnvlist_get_nvlist(fa->fa_cache, name, NULL);
74
if (nvl == NULL)
75
return (-1);
76
77
if (!nvlist_exists_binary(nvl, "stat")) {
78
return (-1);
79
}
80
81
buf = nvlist_get_binary(nvl, "stat", &size);
82
assert(size == sizeof(*sb));
83
memcpy(sb, buf, size);
84
85
return (0);
86
}
87
88
static int
89
fileargs_get_fd_cache(fileargs_t *fa, const char *name)
90
{
91
int fd;
92
const nvlist_t *nvl;
93
nvlist_t *tnvl;
94
95
assert(fa != NULL);
96
assert(fa->fa_magic == FILEARGS_MAGIC);
97
assert(name != NULL);
98
99
if (fa->fa_cache == NULL)
100
return (-1);
101
102
if ((fa->fa_fdflags & O_CREAT) != 0)
103
return (-1);
104
105
nvl = dnvlist_get_nvlist(fa->fa_cache, name, NULL);
106
if (nvl == NULL)
107
return (-1);
108
109
tnvl = nvlist_take_nvlist(fa->fa_cache, name);
110
111
if (!nvlist_exists_descriptor(tnvl, "fd")) {
112
nvlist_destroy(tnvl);
113
return (-1);
114
}
115
116
fd = nvlist_take_descriptor(tnvl, "fd");
117
nvlist_destroy(tnvl);
118
119
if ((fa->fa_fdflags & O_CLOEXEC) != O_CLOEXEC) {
120
if (fcntl(fd, F_SETFD, fa->fa_fdflags) == -1) {
121
close(fd);
122
return (-1);
123
}
124
}
125
126
return (fd);
127
}
128
129
static void
130
fileargs_set_cache(fileargs_t *fa, nvlist_t *nvl)
131
{
132
133
nvlist_destroy(fa->fa_cache);
134
fa->fa_cache = nvl;
135
}
136
137
static nvlist_t*
138
fileargs_fetch(fileargs_t *fa, const char *name, const char *cmd)
139
{
140
nvlist_t *nvl;
141
int serrno;
142
143
assert(fa != NULL);
144
assert(name != NULL);
145
146
nvl = nvlist_create(NV_FLAG_NO_UNIQUE);
147
nvlist_add_string(nvl, "cmd", cmd);
148
nvlist_add_string(nvl, "name", name);
149
150
nvl = cap_xfer_nvlist(fa->fa_chann, nvl);
151
if (nvl == NULL)
152
return (NULL);
153
154
if (nvlist_get_number(nvl, "error") != 0) {
155
serrno = (int)nvlist_get_number(nvl, "error");
156
nvlist_destroy(nvl);
157
errno = serrno;
158
return (NULL);
159
}
160
161
return (nvl);
162
}
163
164
static nvlist_t *
165
fileargs_create_limit(int argc, const char * const *argv, int flags,
166
mode_t mode, cap_rights_t *rightsp, int operations)
167
{
168
nvlist_t *limits;
169
int i;
170
171
limits = nvlist_create(NV_FLAG_NO_UNIQUE);
172
if (limits == NULL)
173
return (NULL);
174
175
nvlist_add_number(limits, "flags", flags);
176
nvlist_add_number(limits, "operations", operations);
177
if (rightsp != NULL) {
178
nvlist_add_binary(limits, "cap_rights", rightsp,
179
sizeof(*rightsp));
180
}
181
if ((flags & O_CREAT) != 0)
182
nvlist_add_number(limits, "mode", (uint64_t)mode);
183
184
for (i = 0; i < argc; i++) {
185
if (strlen(argv[i]) >= MAXPATHLEN) {
186
nvlist_destroy(limits);
187
errno = ENAMETOOLONG;
188
return (NULL);
189
}
190
nvlist_add_null(limits, argv[i]);
191
}
192
193
return (limits);
194
}
195
196
static fileargs_t *
197
fileargs_create(cap_channel_t *chan, int fdflags)
198
{
199
fileargs_t *fa;
200
201
fa = malloc(sizeof(*fa));
202
if (fa != NULL) {
203
fa->fa_cache = NULL;
204
fa->fa_chann = chan;
205
fa->fa_fdflags = fdflags;
206
fa->fa_magic = FILEARGS_MAGIC;
207
}
208
209
return (fa);
210
}
211
212
fileargs_t *
213
fileargs_init(int argc, char *argv[], int flags, mode_t mode,
214
cap_rights_t *rightsp, int operations)
215
{
216
nvlist_t *limits;
217
218
if (argc <= 0 || argv == NULL) {
219
return (fileargs_create(NULL, 0));
220
}
221
222
limits = fileargs_create_limit(argc, (const char * const *)argv, flags,
223
mode, rightsp, operations);
224
if (limits == NULL)
225
return (NULL);
226
227
return (fileargs_initnv(limits));
228
}
229
230
fileargs_t *
231
fileargs_cinit(cap_channel_t *cas, int argc, char *argv[], int flags,
232
mode_t mode, cap_rights_t *rightsp, int operations)
233
{
234
nvlist_t *limits;
235
236
if (argc <= 0 || argv == NULL) {
237
return (fileargs_create(NULL, 0));
238
}
239
240
limits = fileargs_create_limit(argc, (const char * const *)argv, flags,
241
mode, rightsp, operations);
242
if (limits == NULL)
243
return (NULL);
244
245
return (fileargs_cinitnv(cas, limits));
246
}
247
248
fileargs_t *
249
fileargs_initnv(nvlist_t *limits)
250
{
251
cap_channel_t *cas;
252
fileargs_t *fa;
253
254
if (limits == NULL) {
255
return (fileargs_create(NULL, 0));
256
}
257
258
cas = cap_init();
259
if (cas == NULL) {
260
nvlist_destroy(limits);
261
return (NULL);
262
}
263
264
fa = fileargs_cinitnv(cas, limits);
265
cap_close(cas);
266
267
return (fa);
268
}
269
270
fileargs_t *
271
fileargs_cinitnv(cap_channel_t *cas, nvlist_t *limits)
272
{
273
cap_channel_t *chann;
274
fileargs_t *fa;
275
int flags, ret, serrno;
276
277
assert(cas != NULL);
278
279
if (limits == NULL) {
280
return (fileargs_create(NULL, 0));
281
}
282
283
chann = NULL;
284
fa = NULL;
285
286
chann = cap_service_open(cas, "system.fileargs");
287
if (chann == NULL) {
288
nvlist_destroy(limits);
289
return (NULL);
290
}
291
292
flags = nvlist_get_number(limits, "flags");
293
(void)nvlist_get_number(limits, "operations");
294
295
/* Limits are consumed no need to free them. */
296
ret = cap_limit_set(chann, limits);
297
if (ret < 0)
298
goto out;
299
300
fa = fileargs_create(chann, flags);
301
if (fa == NULL)
302
goto out;
303
304
return (fa);
305
out:
306
serrno = errno;
307
if (chann != NULL)
308
cap_close(chann);
309
errno = serrno;
310
return (NULL);
311
}
312
313
int
314
fileargs_open(fileargs_t *fa, const char *name)
315
{
316
int fd;
317
nvlist_t *nvl;
318
char *cmd;
319
320
assert(fa != NULL);
321
assert(fa->fa_magic == FILEARGS_MAGIC);
322
323
if (name == NULL) {
324
errno = EINVAL;
325
return (-1);
326
}
327
328
if (fa->fa_chann == NULL) {
329
errno = ENOTCAPABLE;
330
return (-1);
331
}
332
333
fd = fileargs_get_fd_cache(fa, name);
334
if (fd != -1)
335
return (fd);
336
337
nvl = fileargs_fetch(fa, name, "open");
338
if (nvl == NULL)
339
return (-1);
340
341
fd = nvlist_take_descriptor(nvl, "fd");
342
cmd = nvlist_take_string(nvl, "cmd");
343
if (strcmp(cmd, "cache") == 0)
344
fileargs_set_cache(fa, nvl);
345
else
346
nvlist_destroy(nvl);
347
free(cmd);
348
349
return (fd);
350
}
351
352
FILE *
353
fileargs_fopen(fileargs_t *fa, const char *name, const char *mode)
354
{
355
int fd;
356
357
if ((fd = fileargs_open(fa, name)) < 0) {
358
return (NULL);
359
}
360
361
return (fdopen(fd, mode));
362
}
363
364
int
365
fileargs_lstat(fileargs_t *fa, const char *name, struct stat *sb)
366
{
367
nvlist_t *nvl;
368
const void *buf;
369
size_t size;
370
char *cmd;
371
372
assert(fa != NULL);
373
assert(fa->fa_magic == FILEARGS_MAGIC);
374
375
if (name == NULL) {
376
errno = EINVAL;
377
return (-1);
378
}
379
380
if (sb == NULL) {
381
errno = EFAULT;
382
return (-1);
383
}
384
385
if (fa->fa_chann == NULL) {
386
errno = ENOTCAPABLE;
387
return (-1);
388
}
389
390
if (fileargs_get_lstat_cache(fa, name, sb) != -1)
391
return (0);
392
393
nvl = fileargs_fetch(fa, name, "lstat");
394
if (nvl == NULL)
395
return (-1);
396
397
buf = nvlist_get_binary(nvl, "stat", &size);
398
assert(size == sizeof(*sb));
399
memcpy(sb, buf, size);
400
401
cmd = nvlist_take_string(nvl, "cmd");
402
if (strcmp(cmd, "cache") == 0)
403
fileargs_set_cache(fa, nvl);
404
else
405
nvlist_destroy(nvl);
406
free(cmd);
407
408
return (0);
409
}
410
411
char *
412
fileargs_realpath(fileargs_t *fa, const char *pathname, char *reserved_path)
413
{
414
nvlist_t *nvl;
415
char *ret;
416
417
assert(fa != NULL);
418
assert(fa->fa_magic == FILEARGS_MAGIC);
419
420
if (pathname == NULL) {
421
errno = EINVAL;
422
return (NULL);
423
}
424
425
if (fa->fa_chann == NULL) {
426
errno = ENOTCAPABLE;
427
return (NULL);
428
}
429
430
nvl = fileargs_fetch(fa, pathname, "realpath");
431
if (nvl == NULL)
432
return (NULL);
433
434
if (reserved_path != NULL) {
435
ret = reserved_path;
436
strcpy(reserved_path,
437
nvlist_get_string(nvl, "realpath"));
438
} else {
439
ret = nvlist_take_string(nvl, "realpath");
440
}
441
nvlist_destroy(nvl);
442
443
return (ret);
444
}
445
446
void
447
fileargs_free(fileargs_t *fa)
448
{
449
450
if (fa == NULL)
451
return;
452
453
assert(fa->fa_magic == FILEARGS_MAGIC);
454
455
nvlist_destroy(fa->fa_cache);
456
if (fa->fa_chann != NULL) {
457
cap_close(fa->fa_chann);
458
}
459
explicit_bzero(&fa->fa_magic, sizeof(fa->fa_magic));
460
free(fa);
461
}
462
463
cap_channel_t *
464
fileargs_unwrap(fileargs_t *fa, int *flags)
465
{
466
cap_channel_t *chan;
467
468
if (fa == NULL)
469
return (NULL);
470
471
assert(fa->fa_magic == FILEARGS_MAGIC);
472
473
chan = fa->fa_chann;
474
if (flags != NULL) {
475
*flags = fa->fa_fdflags;
476
}
477
478
nvlist_destroy(fa->fa_cache);
479
explicit_bzero(&fa->fa_magic, sizeof(fa->fa_magic));
480
free(fa);
481
482
return (chan);
483
}
484
485
fileargs_t *
486
fileargs_wrap(cap_channel_t *chan, int fdflags)
487
{
488
489
if (chan == NULL) {
490
return (NULL);
491
}
492
493
return (fileargs_create(chan, fdflags));
494
}
495
496
/*
497
* Service functions.
498
*/
499
500
static const char *lastname;
501
static void *cacheposition;
502
static bool allcached;
503
static const cap_rights_t *caprightsp;
504
static int capflags;
505
static int allowed_operations;
506
static mode_t capmode;
507
508
static int
509
open_file(const char *name)
510
{
511
int fd, serrno;
512
513
if ((capflags & O_CREAT) == 0)
514
fd = open(name, capflags);
515
else
516
fd = open(name, capflags, capmode);
517
if (fd < 0)
518
return (-1);
519
520
if (caprightsp != NULL) {
521
if (cap_rights_limit(fd, caprightsp) < 0 && errno != ENOSYS) {
522
serrno = errno;
523
close(fd);
524
errno = serrno;
525
return (-1);
526
}
527
}
528
529
return (fd);
530
}
531
532
static void
533
fileargs_add_cache(nvlist_t *nvlout, const nvlist_t *limits,
534
const char *current_name)
535
{
536
int type, i, fd;
537
void *cookie;
538
nvlist_t *new;
539
const char *fname;
540
struct stat sb;
541
542
if ((capflags & O_CREAT) != 0) {
543
allcached = true;
544
return;
545
}
546
547
cookie = cacheposition;
548
for (i = 0; i < CACHE_SIZE + 1; i++) {
549
fname = nvlist_next(limits, &type, &cookie);
550
if (fname == NULL) {
551
cacheposition = NULL;
552
lastname = NULL;
553
allcached = true;
554
return;
555
}
556
/* We doing that to catch next element name. */
557
if (i == CACHE_SIZE) {
558
break;
559
}
560
561
if (type != NV_TYPE_NULL) {
562
i--;
563
continue;
564
}
565
if (current_name != NULL &&
566
strcmp(fname, current_name) == 0) {
567
current_name = NULL;
568
i--;
569
continue;
570
}
571
572
new = nvlist_create(NV_FLAG_NO_UNIQUE);
573
if ((allowed_operations & FA_OPEN) != 0) {
574
fd = open_file(fname);
575
if (fd < 0) {
576
i--;
577
nvlist_destroy(new);
578
continue;
579
}
580
nvlist_move_descriptor(new, "fd", fd);
581
}
582
if ((allowed_operations & FA_LSTAT) != 0) {
583
if (lstat(fname, &sb) < 0) {
584
i--;
585
nvlist_destroy(new);
586
continue;
587
}
588
nvlist_add_binary(new, "stat", &sb, sizeof(sb));
589
}
590
591
nvlist_move_nvlist(nvlout, fname, new);
592
}
593
cacheposition = cookie;
594
lastname = fname;
595
}
596
597
static bool
598
fileargs_allowed(const nvlist_t *limits, const nvlist_t *request, int operation)
599
{
600
const char *name;
601
602
if ((allowed_operations & operation) == 0)
603
return (false);
604
605
name = dnvlist_get_string(request, "name", NULL);
606
if (name == NULL)
607
return (false);
608
609
/* Fast path. */
610
if (lastname != NULL && strcmp(name, lastname) == 0)
611
return (true);
612
613
if (!nvlist_exists_null(limits, name))
614
return (false);
615
616
return (true);
617
}
618
619
static int
620
fileargs_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
621
{
622
623
if (oldlimits != NULL)
624
return (ENOTCAPABLE);
625
626
capflags = (int)dnvlist_get_number(newlimits, "flags", 0);
627
allowed_operations = (int)dnvlist_get_number(newlimits, "operations", 0);
628
if ((capflags & O_CREAT) != 0)
629
capmode = (mode_t)nvlist_get_number(newlimits, "mode");
630
else
631
capmode = 0;
632
633
caprightsp = dnvlist_get_binary(newlimits, "cap_rights", NULL, NULL, 0);
634
635
return (0);
636
}
637
638
static int
639
fileargs_command_lstat(const nvlist_t *limits, nvlist_t *nvlin,
640
nvlist_t *nvlout)
641
{
642
int error;
643
const char *name;
644
struct stat sb;
645
646
if (limits == NULL)
647
return (ENOTCAPABLE);
648
649
if (!fileargs_allowed(limits, nvlin, FA_LSTAT))
650
return (ENOTCAPABLE);
651
652
name = nvlist_get_string(nvlin, "name");
653
654
error = lstat(name, &sb);
655
if (error < 0)
656
return (errno);
657
658
if (!allcached && (lastname == NULL ||
659
strcmp(name, lastname) == 0)) {
660
nvlist_add_string(nvlout, "cmd", "cache");
661
fileargs_add_cache(nvlout, limits, name);
662
} else {
663
nvlist_add_string(nvlout, "cmd", "lstat");
664
}
665
nvlist_add_binary(nvlout, "stat", &sb, sizeof(sb));
666
return (0);
667
}
668
669
static int
670
fileargs_command_realpath(const nvlist_t *limits, nvlist_t *nvlin,
671
nvlist_t *nvlout)
672
{
673
const char *pathname;
674
char *resolvedpath;
675
676
if (limits == NULL)
677
return (ENOTCAPABLE);
678
679
if (!fileargs_allowed(limits, nvlin, FA_REALPATH))
680
return (ENOTCAPABLE);
681
682
pathname = nvlist_get_string(nvlin, "name");
683
resolvedpath = realpath(pathname, NULL);
684
if (resolvedpath == NULL)
685
return (errno);
686
687
nvlist_move_string(nvlout, "realpath", resolvedpath);
688
return (0);
689
}
690
691
static int
692
fileargs_command_open(const nvlist_t *limits, nvlist_t *nvlin,
693
nvlist_t *nvlout)
694
{
695
int fd;
696
const char *name;
697
698
if (limits == NULL)
699
return (ENOTCAPABLE);
700
701
if (!fileargs_allowed(limits, nvlin, FA_OPEN))
702
return (ENOTCAPABLE);
703
704
name = nvlist_get_string(nvlin, "name");
705
706
fd = open_file(name);
707
if (fd < 0)
708
return (errno);
709
710
if (!allcached && (lastname == NULL ||
711
strcmp(name, lastname) == 0)) {
712
nvlist_add_string(nvlout, "cmd", "cache");
713
fileargs_add_cache(nvlout, limits, name);
714
} else {
715
nvlist_add_string(nvlout, "cmd", "open");
716
}
717
nvlist_move_descriptor(nvlout, "fd", fd);
718
return (0);
719
}
720
721
static int
722
fileargs_command(const char *cmd, const nvlist_t *limits,
723
nvlist_t *nvlin, nvlist_t *nvlout)
724
{
725
726
if (strcmp(cmd, "open") == 0)
727
return (fileargs_command_open(limits, nvlin, nvlout));
728
if (strcmp(cmd, "lstat") == 0)
729
return (fileargs_command_lstat(limits, nvlin, nvlout));
730
if (strcmp(cmd, "realpath") == 0)
731
return (fileargs_command_realpath(limits, nvlin, nvlout));
732
733
return (EINVAL);
734
}
735
736
CREATE_SERVICE("system.fileargs", fileargs_limit, fileargs_command,
737
CASPER_SERVICE_FD | CASPER_SERVICE_STDIO | CASPER_SERVICE_NO_UNIQ_LIMITS);
738
739