]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libcasper/services/cap_fileargs/cap_fileargs.c
Update to bmake-20240108
[FreeBSD/FreeBSD.git] / lib / libcasper / services / cap_fileargs / cap_fileargs.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2018-2021 Mariusz Zaborski <oshogbo@FreeBSD.org>
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);